Documentation
Deployment

Deploy to VPS (Hetzner, Digital Ocean, EC2, etc.) via Docker

Deployment guide for VPS hosting providers like Hetzner, Digital Ocean, and EC2 using GitHub Actions, Docker, and Traefik.

This guide provides step-by-step instructions for deploying the GoLiveKit application using GitHub Actions, Docker, and Traefik on a VPS.

Prerequisites

1. VPS Setup

Minimal Requirement for VPS

1 GB Ram / 1 CPU
25 GB SSD Disk

Choose either Digital Ocean, Hetzner, or EC2 (or any VPS provider):

Digital Ocean Setup

  1. Create a droplet with SSH access
  2. SSH to your server: ssh root@your-server-ip
  3. Install Docker:
    curl -fsSL https://get.docker.com | sudo bash
  4. Verify Docker installation:
    sudo systemctl status docker
    Expected output: Active: active (running)

Hetzner Setup

  • Follow similar steps as Digital Ocean
  • Ensure Docker is installed and running

2. Domain Configuration

  1. Purchase a domain name
  2. Point your domain's DNS A record to your VPS IP address
  3. Ensure both yourdomain.com and www.yourdomain.com point to your VPS

GitHub Repository Setup

1. Enable GitHub Container Registry

  1. Go to your GitHub repository
  2. Navigate to SettingsActionsGeneral
  3. Under Workflow permissions, select Read and write permissions

If the options are grayed out or disabled in your repository, you must change them at the organization level first. You need to be an Organization Owner to do this.

  1. Navigate to your GitHub Organization's main page
  2. Click Settings (top navigation bar)
  3. In the left sidebar, under Code, planning, and automation, click ActionsGeneral
  4. Scroll down to Workflow permissions
  5. Select Read and write permissions
  6. Click Save

2. Generate GitHub Container Registry Token

  1. Go to GitHub SettingsDeveloper settingsPersonal access tokensTokens (classic)
  2. Generate a new token with these permissions:
    • write:packages
    • read:packages
    • delete:packages
  3. Copy the token (you'll need it for GHCR_TOKEN secret)
  4. Note the GitHub username that owns this token (you'll need it for GHCR_USERNAME secret)

3. Configure Repository Secrets

Go to your repository SettingsSecrets and variablesActions and add these Repository secrets:

Required Secrets

Secret NameDescriptionExample Value
ENV_KEYSComplete .env file content for your applicationSee Environment Variables section
GHCR_USERNAMEGitHub username that owns GHCR_TOKENIOZ
GHCR_TOKENGitHub Container Registry tokenghp_xxxxxxxxxxxxxxxxxxxx
VPS_HOSTYour VPS IP address138.197.191.210
VPS_USERNAMEVPS username (usually root)root
VPS_SSH_KEYPrivate SSH key for VPS accessContents of your private SSH key file
DOMAINYour domain name (without protocol)yourdomain.com
ACME_EMAILEmail for Let's Encrypt SSL certificates[email protected]
TRAEFIK_AUTH_USERSBasic auth for Traefik dashboardSee Traefik Auth section

GHCR_USERNAME must match the account that generated GHCR_TOKEN. In organization repositories, this is often your user (or bot) account, not the organization name.

Environment Variables

Docker Compose DB Host

If your app and Postgres run in Docker Compose, set DB_HOST to the Postgres service name (usually postgres), not localhost. Inside containers, localhost points to the same container and causes Prisma P1001 errors. For internal Docker network connections without Postgres TLS enabled, use DB_SSLMODE=disable.

Create your ENV_KEYS secret with all the environment variables your application needs. Example structure:

NEXT_PUBLIC_BASE_HOST=https://yourdomain.com
NEXT_PUBLIC_BRAND_NAME=GoLiveKit

# DB connection
DB_USER=db_user
DB_PASS=db_pass
DB_HOST=postgres
DB_PORT=5432
DB_NAME=db_name
DB_SSLMODE=disable

# Auth providers
BETTER_AUTH_SECRET="auth_secret"
BETTER_AUTH_URL=https://yourdomain.com
GOOGLE_CLIENT_ID=google_client_id
GOOGLE_CLIENT_SECRET=google_client_secret

# Admin user
ADMIN_EMAIL=admin_email_address
ADMIN_PASSWORD=password_minimum_8_symbols
ADMIN_NAME=admin_name

# Email SMTP config
[email protected]
EMAIL_SERVER_PASSWORD=your_email_password
EMAIL_SERVER_HOST=smtp.gmail.com
EMAIL_SERVER_PORT=465
EMAIL_FROM=Your Name from GoLiveKit<[email protected]>

# Payments
## Stripe
STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key

# Notifications
NOTIFICATION_PROVIDER=telegram
TELEGRAM_CHANNEL_ID=your_telegram_channel_id
TELEGRAM_BOT_TOKEN=your_telegram_bot_token

SSH Key Setup

  1. On your local machine, generate SSH key pair:

    ssh-keygen -t rsa -b 4096 -C "[email protected]"
  2. Copy public key to your VPS:

    ssh-copy-id root@your-vps-ip
  3. Copy the private key content to the VPS_SSH_KEY GitHub secret:

    cat ~/.ssh/id_rsa

Traefik Authentication

Generate basic auth credentials for Traefik dashboard:

  1. Install htpasswd (usually comes with Apache):

    # Ubuntu/Debian
    sudo apt-get install apache2-utils
    
    # macOS
    brew install httpd
  2. Generate auth string:

    htpasswd -nbB YOUR_USER YOUR_PASSWORD
  3. Replace YOUR_USER and YOUR_PASSWORD with real credentials you will actually use to log in.

    Example:

    htpasswd -nbB bobby 12345abc
  4. Copy the full output (for example user:$2y$...) to the TRAEFIK_AUTH_USERS secret exactly as-is.

    Do not wrap it in quotes and do not manually escape $.

  5. Use that same username/password to log in to all services protected by traefik-auth middleware (Traefik dashboard, Dozzle, Adminer).

Deployment Process

Automatic Deployment

The application automatically deploys when you push to the master or main branch:

  1. Linting: Code is linted for quality checks
  2. Build: Docker image is built and pushed to GitHub Container Registry
  3. Deploy: Application is deployed to your VPS with:
    • SSL certificates via Let's Encrypt
    • Automatic HTTP to HTTPS redirects
    • WWW to non-WWW redirects
    • Traefik reverse proxy setup

Post-Deployment

Access Your Application

  • Main site: https://yourdomain.com
  • Traefik dashboard: https://traefik.yourdomain.com (protected by basic auth)
  • Dozzle (Docker logs UI): https://dozzle.yourdomain.com (protected by basic auth)
  • Adminer (DB UI): https://adminer.yourdomain.com (protected by basic auth)

Protected services use the credentials generated with htpasswd and stored in TRAEFIK_AUTH_USERS.

Monitoring and Maintenance

  1. Check application logs:

    docker logs golivekit
  2. Check Traefik logs:

    docker logs traefik
  3. Monitor disk usage:

    docker system df
  4. Clean up old images (done automatically in CI/CD):

    docker system prune -af

SSL Certificate Renewal

Let's Encrypt certificates are automatically renewed by Traefik. No manual intervention required.

Security Considerations

  1. Keep secrets secure: Never commit secrets to your repository
  2. Regular updates: Keep Docker images and VPS updated
  3. Firewall: Configure firewall to only allow necessary ports (22, 80, 443)
  4. SSH security: Use key-based authentication, disable password login
  5. Traefik dashboard: Always protect with strong basic auth credentials

Architecture Overview

The deployment consists of:

  • Next.js Application: Main application container
  • Traefik: Reverse proxy with automatic SSL
  • GitHub Actions: CI/CD pipeline
  • GitHub Container Registry: Docker image storage

Docker image size: ~556 MB

On this page