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
Choose either Digital Ocean, Hetzner, or EC2 (or any VPS provider):
Digital Ocean Setup
- Create a droplet with SSH access
- SSH to your server:
ssh root@your-server-ip - Install Docker:
curl -fsSL https://get.docker.com | sudo bash - Verify Docker installation:
Expected output:sudo systemctl status dockerActive: active (running)
Hetzner Setup
- Follow similar steps as Digital Ocean
- Ensure Docker is installed and running
2. Domain Configuration
- Purchase a domain name
- Point your domain's DNS A record to your VPS IP address
- Ensure both
yourdomain.comandwww.yourdomain.compoint to your VPS
GitHub Repository Setup
1. Enable GitHub Container Registry
- Go to your GitHub repository
- Navigate to Settings → Actions → General
- Under Workflow permissions, select Read and write permissions
2. Generate GitHub Container Registry Token
- Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
- Generate a new token with these permissions:
write:packagesread:packagesdelete:packages
- Copy the token (you'll need it for
GHCR_TOKENsecret)
3. Configure Repository Secrets
Go to your repository Settings → Secrets and variables → Actions and add these secrets:
Required Secrets
| Secret Name | Description | Example Value |
|---|---|---|
ENV_KEYS | Complete .env file content for your application | See Environment Variables section |
GHCR_TOKEN | GitHub Container Registry token | ghp_xxxxxxxxxxxxxxxxxxxx |
VPS_HOST | Your VPS IP address | 138.197.191.210 |
VPS_USERNAME | VPS username (usually root) | root |
VPS_SSH_KEY | Private SSH key for VPS access | Contents of your private SSH key file |
DOMAIN | Your domain name (without protocol) | yourdomain.com |
ACME_EMAIL | Email for Let's Encrypt SSL certificates | your-email@example.com |
TRAEFIK_AUTH_USERS | Basic auth for Traefik dashboard | See Traefik Auth section |
Environment Variables
[!IMPORTANT] If your app and Postgres run in Docker Compose, set
DB_HOSTto the Postgres service name (usuallypostgres), notlocalhost. Inside containers,localhostpoints to the same container and causes PrismaP1001errors. For internal Docker network connections without Postgres TLS enabled, useDB_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
# Email SMTP config
EMAIL_SERVER_USER=your_email@domain.com
EMAIL_SERVER_PASSWORD=your_email_password
EMAIL_SERVER_HOST=smtp.gmail.com
EMAIL_SERVER_PORT=465
EMAIL_FROM=Your Name from GoLiveKit<your_email@domain.com>
# 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_tokenSSH Key Setup
-
On your local machine, generate SSH key pair:
ssh-keygen -t rsa -b 4096 -C "your-email@example.com" -
Copy public key to your VPS:
ssh-copy-id root@your-vps-ip -
Copy the private key content to the
VPS_SSH_KEYGitHub secret:cat ~/.ssh/id_rsa
Traefik Authentication
Generate basic auth credentials for Traefik dashboard:
-
Install
htpasswd(usually comes with Apache):# Ubuntu/Debian sudo apt-get install apache2-utils # macOS brew install httpd -
Generate auth string:
htpasswd -nbB YOUR_USER YOUR_PASSWORD -
Replace
YOUR_USERandYOUR_PASSWORDwith real credentials you will actually use to log in.Example:
htpasswd -nbB bobby 12345abc -
Copy the full output (for example
user:$2y$...) to theTRAEFIK_AUTH_USERSsecret exactly as-is.Do not wrap it in quotes and do not manually escape
$. -
Use that same username/password to log in to all services protected by
traefik-authmiddleware (Traefik dashboard, Dozzle, Adminer).
Deployment Process
Automatic Deployment
The application automatically deploys when you push to the master branch:
- Linting: Code is linted for quality checks
- Build: Docker image is built and pushed to GitHub Container Registry
- 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
-
Check application logs:
docker logs golivekit -
Check Traefik logs:
docker logs traefik -
Monitor disk usage:
docker system df -
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
- Keep secrets secure: Never commit secrets to your repository
- Regular updates: Keep Docker images and VPS updated
- Firewall: Configure firewall to only allow necessary ports (22, 80, 443)
- SSH security: Use key-based authentication, disable password login
- 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