Skip to content

🚀 BasicRum Backend (staging) — Deployment

1. Overview

Our BasicRum Staging Backend runs on a Hetzner server using:

  • Node.js (installed via nvm)
  • PM2 for process management
  • Yarn for dependency installation
  • Docker (installed via apt)
  • PostgreSQL Running on the server via docker
  • ClickHouse for now running on another server. The location is in the env file
  • GitHub Actions for automated deployments
  • Secrets in GitHub for sensitive values (i.e. ssh keys for Hetzner)
  • Nginx for reverse proxy

We keep environment variables (.env) outside the Git repo in another directory to avoid overwriting them during git pull. In the future, we may consider using a secrets management tool like sops or Infisical.

The Hetzner server is authorized to pull (read only) from our GitHub repository. We use a deploy key called HETZNER_DEV_DEPLOY_KEY for this purpose. The key pair is generated on the server and the public keys is added to the GitHub repository settings. Learn more for deploy keys

GitHub Actions also need ssh access to the server to run necessary commands. I generated a new SSH key pair specifically for GitHub Actions and added the public key to the server's authorized keys ~/.ssh/authorized_keys. Then I added the private key HETZNER_SSH_KEY to the GitHub repository secrets.


2. How to deploy automatically

To deploy changes automatically to the staging environment, we use GitHub Actions. The workflow is triggered on pushes to the main branch of the backend directory.

You can also manually trigger the workflow from the GitHub Actions tab by running the staging_backend_deploy workflow.

Prerequisites

The automatic deployment script assumes the postgres db is running via docker on the server. We also need to ensure that the necessary environment variables are there. And the remote clickhouse instance is accessible.

3. How to deploy manually

In case something goes wrong with the automatic deployment, you can manually deploy the changes using SSH.

SSH from local machine

bash
ssh root@<HETZNER_IP>

You need to be authorized to ssh into the server. Please ask us for the necessary credentials and provide us your public SSH key. We will add it to the allowed keys on the server. The keys are stored in ~/.ssh/authorized_keys on the server.

Pull changes from Git

Go to the project directory, pull the latest changes and install any new dependencies.

bash
cd /srv/basicrum
git pull origin main
pnpm install

To run the application, first build the api_contract, and the backend app itself.

bash
cd api_contract
pnpm build
cd ../backend
pnpm build

NOTE

We have to make sure the environment variables are set correctly before starting the application. If not, please follow the Environment Variables section below.

We use pm2 to manage the application processes. First check if any existing basicrum-backend pm2 processes are running:

bash
pm2 ls

If you see a process named basicrum-backend, stop and delete it with:

bash
pm2 stop basicrum-backend
pm2 delete basicrum-backend

If it's not running, you can start it with:

bash
pm2 start build/index.js --name basicrum-backend

Now check if the process is running:

bash
pm2 ls

You can also see its logs by running:

bash
pm2 logs basicrum-backend

4. Environment Variables

We store the environment variables needed to start the application in /srv/assets/basicrum-backend/.env file. If you need to change any environment variables, please do so in this file.

You can use nano to edit the file.

bash
nano /srv/assets/basicrum-backend/.env

The deploy script automatically copies the .env file to the backend directory during deployment. For manual deployments, make sure to copy the .env file to the backend directory:

bash
cp /srv/assets/basicrum-backend/.env /srv/basicrum/backend/.env

5. Nginx setup

We use Nginx as a reverse proxy to forward requests to the backend application. The Nginx configuration file is located at /etc/nginx/sites-available/basicrum-backend. This config was enabled in Nginx by creating a symlink using sudo ln -s /etc/nginx/sites-available/basicrum-backend /etc/nginx/sites-enabled/

You can edit it:

bash
sudo nano /etc/nginx/sites-available/basicrum-backend

Current config:

nginx
server {
    listen 80;
    server_name <HETZNER_IP>;

    #frontend
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    #backend: notice the /backend route prefix
    location /backend {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

After making changes to the Nginx configuration, test the configuration for syntax errors:

bash
sudo nginx -t

If the test is successful, reload Nginx to apply the changes:

bash
sudo systemctl reload nginx

Check the app

You can check if the app is running by visiting http://<HETZNER_IP>/backend/v1/health in your web browser. You should see a JSON response message "YOU SHALL NOT PASS" indicating you are unauthorized and the app is working correctly.

Alternatively you can also point your local frontend to the Hetzner server's IP address to test the integration. Just update you local frontend/.env file accordingly.

bash
BACKEND_URL=<HETZNER_IP>
BACKEND_URL_FOR_AUTH=http://<HETZNER_IP>/backend/v1
NEXT_PUBLIC_API_URL=http://<HETZNER_IP>

Basicrum Frontend (staging) - Deployment.

The frontend deployment follows exactly the same pattern. The name of the pm2 instance is basicrum-frontend. The deployment also happens automatically on merge to the main branch. The script for deployment is located in staging_frontend_deploy.yml in the repository.

Similarly to the backend, the .env file for the frontend is located at /srv/assets/basicrum-frontend/.env in the remote server.

Miscellaneous

How to update node version

We use nvm to manage node versions on the server. To update node version, first ssh into the server and run:

bash
nvm install <new_version>

This will automatically set the new version as the default version.

How to update pm2 globally

To update pm2 globally on the server, run:

bash
npm install -g pm2

This will make the in-memory daemon out of date. To update the daemon, run:

bash
pm2 update

This will automatically restart all pm2 processes with the updated pm2 version.