General
Deployment

Virtual Private Server (VPS)

Learn how to deploy a VPS.

A VPS offers the best performance among hosting options while keeping costs low. A clever hosting trick is to grab an inexpensive, overprovisioned server—like a Hetzner auction deal during Christmas week—and run both your apps and database on it, achieving near-instant 0ms network latency.

Prerequisites

  • Linux-based VPS (Debian or Ubuntu recommended).
  • DNS control for yourdomain.com.
  • SSH access to your VPS

Server preparation

Update the system

Terminal
sudo apt update && sudo apt upgrade -y

Install dependencies

Install essential tools:

Terminal
sudo apt install -y build-essential curl software-properties-common git

Install Node.js

Use the NodeSource setup script:

Terminal
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

Install pnpm globally:

Terminal
corepack enable
corepack prepare pnpm@latest --activate

Install PostgreSQL

Terminal
sudo apt install -y postgresql postgresql-contrib
sudo -u postgres psql

In the PostgreSQL shell, set up a user:

Terminal
CREATE USER postgres WITH PASSWORD 'secure_password';
ALTER USER postgres WITH SUPERUSER;
\q

Install PM2

Terminal
sudo npm install -g pm2

DNS configuration

Add DNS records

Configure the following DNS records:

TypeNameValue
A@VPS_IP_ADDRESS
AdashboardVPS_IP_ADDRESS

Replace VPS_IP_ADDRESS with your server's IP address.

Application Deployment

Clone the repository

Log in to your VPS and clone your project:

Terminal
git clone https://github.com/your-repo/achromatic.git /var/www/achromatic
cd /var/www/achromatic

Install dependencies and build

Use pnpm for faster and disk-efficient installs:

Terminal
pnpm install
pnpm build

Start applications with PM2

Set up the Marketing and Dashboard services:

Terminal
pm2 start npm --name "marketing" --prefix ./marketing -- run start
pm2 start npm --name "dashboard" --prefix ./dashboard -- run start
pm2 save

Ensure PM2 restarts your apps on reboot:

Terminal
pm2 startup

SSL with Certbot

Install Certbot and generate SSL certificates:

Terminal
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d dashboard.yourdomain.com

Configure auto-renewal:

Terminal
sudo systemctl enable certbot.timer

Nginx configuration

Marketing application

Create a configuration for Marketing:

Terminal
sudo nano /etc/nginx/sites-available/marketing
/etc/nginx/sites-available/marketing
server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

}

Enable the configuration:

Terminal
sudo ln -s /etc/nginx/sites-available/marketing /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Dashboard application

Create a configuration for Dashboard:

Terminal
sudo nano /etc/nginx/sites-available/dashboard
/etc/nginx/sites-available/dashboard
server {
    listen 80;
    server_name dashboard.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name dashboard.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

}

Enable the configuration:

Terminal
sudo ln -s /etc/nginx/sites-available/dashboard /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

CI/CD pipeline

GitHub Actions workflow

Create .github/workflows/deploy.yml in your repository:

.github/workflows/deploy.yml
name: Deploy Achromatic

on:
  push:
    branches: - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install pnpm
        run: |
          corepack enable
          corepack prepare pnpm@latest --activate

      - name: Install Dependencies
        run: pnpm install

      - name: Build Applications
        run: pnpm build

      - name: Deploy to Server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /var/www/achromatic
            git pull origin main
            pnpm install
            pnpm build
            pm2 restart all

Setting up secrets

Add the following secrets in your repository settings:

Secret NameValue
SSH_HOSTVPS_IP_ADDRESS
SSH_USERNAMEYour SSH username
SSH_PRIVATE_KEYYour private SSH key

Monitoring

Check application status:

Terminal
pm2 list
pm2 logs

Further steps

Next you can harden your security by installing ufw as your firewall, fail2ban to prevent intrusions and a cron job to upload your db backups to s3.