🚀 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
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.
cd /srv/basicrum
git pull origin main
pnpm installTo run the application, first build the api_contract, and the backend app itself.
cd api_contract
pnpm build
cd ../backend
pnpm buildNOTE
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:
pm2 lsIf you see a process named basicrum-backend, stop and delete it with:
pm2 stop basicrum-backend
pm2 delete basicrum-backendIf it's not running, you can start it with:
pm2 start build/index.js --name basicrum-backendNow check if the process is running:
pm2 lsYou can also see its logs by running:
pm2 logs basicrum-backend4. 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.
nano /srv/assets/basicrum-backend/.envThe 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:
cp /srv/assets/basicrum-backend/.env /srv/basicrum/backend/.env5. 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:
sudo nano /etc/nginx/sites-available/basicrum-backendCurrent config:
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:
sudo nginx -tIf the test is successful, reload Nginx to apply the changes:
sudo systemctl reload nginxCheck 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.
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:
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:
npm install -g pm2This will make the in-memory daemon out of date. To update the daemon, run:
pm2 updateThis will automatically restart all pm2 processes with the updated pm2 version.