This guide covers deploying BlockBot to a VPS with Docker Compose and Traefik for automatic SSL.
- Ubuntu 22.04+ or Debian 12+ (recommended)
- 2GB RAM minimum (4GB recommended)
- 20GB storage
- Docker and Docker Compose installed
- Domain name pointing to server IP
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install Docker Compose (if not included)
sudo apt install docker-compose-plugin
# Add user to docker group (logout/login required)
sudo usermod -aG docker $USERgit clone https://github.com/yourusername/blockbot.git
cd blockbotcp .env.example .envEdit .env with your production values:
# Required - Database
POSTGRES_PASSWORD=generate-secure-password-here
# Required - Authentication
JWT_SECRET=generate-32-char-minimum-secret
JWT_REFRESH_SECRET=generate-another-32-char-secret
# Required - Email (get from https://resend.com)
RESEND_API_KEY=re_your_api_key
# Required - Domain
DOMAIN=api.yourdomain.com
ACME_EMAIL=admin@yourdomain.comGenerate secure secrets:
# Generate JWT secrets
openssl rand -base64 32
openssl rand -base64 32
# Generate PostgreSQL password
openssl rand -base64 24docker compose -f docker-compose.prod.yml up -ddocker compose -f docker-compose.prod.yml exec api bun run db:migrate# Check all services are running
docker compose -f docker-compose.prod.yml ps
# Check API health
curl https://your-domain.com/health
# Check detailed health (database + redis)
curl https://your-domain.com/api/v1/health| Variable | Required | Description | Example |
|---|---|---|---|
POSTGRES_PASSWORD |
Yes | PostgreSQL password | secure-random-string |
JWT_SECRET |
Yes | JWT signing secret (32+ chars) | openssl rand -base64 32 |
JWT_REFRESH_SECRET |
Yes | Refresh token secret (32+ chars) | openssl rand -base64 32 |
RESEND_API_KEY |
Yes | Resend.com API key | re_xxxxx |
DOMAIN |
Yes | API domain (no https://) | api.example.com |
ACME_EMAIL |
Yes | Let's Encrypt email | admin@example.com |
NODE_ENV |
No | Environment | production |
PORT |
No | Internal port | 3000 |
┌─────────────┐
│ Traefik │
│ (SSL/LB) │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌────────┐
│ API │ │ Postgres │ │ Redis │
│ (Bun) │──│ (DB) │ │(Cache) │
└────────┘ └──────────┘ └────────┘
- Port 80: HTTP (redirects to HTTPS)
- Port 443: HTTPS (Traefik)
- Internal: Services communicate on Docker network
Firewall setup:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp # SSH
sudo ufw enable# All services
docker compose -f docker-compose.prod.yml logs -f
# Specific service
docker compose -f docker-compose.prod.yml logs -f api
docker compose -f docker-compose.prod.yml logs -f postgres
docker compose -f docker-compose.prod.yml logs -f traefik# Restart all
docker compose -f docker-compose.prod.yml restart
# Restart specific service
docker compose -f docker-compose.prod.yml restart api# Pull latest code
git pull origin main
# Rebuild and restart
docker compose -f docker-compose.prod.yml up -d --build
# Run migrations if needed
docker compose -f docker-compose.prod.yml exec api bun run db:migrateBackup:
docker compose -f docker-compose.prod.yml exec postgres \
pg_dump -U blockbot blockbot > backup-$(date +%Y%m%d).sqlRestore:
docker compose -f docker-compose.prod.yml exec -T postgres \
psql -U blockbot blockbot < backup-20240101.sqlAccess PostgreSQL:
docker compose -f docker-compose.prod.yml exec postgres \
psql -U blockbot blockbotAccess Redis CLI:
docker compose -f docker-compose.prod.yml exec redis redis-cliClear rate limit data:
docker compose -f docker-compose.prod.yml exec redis \
redis-cli KEYS "ratelimit:*" | xargs redis-cli DEL| Endpoint | Purpose | Response |
|---|---|---|
/health |
Basic liveness | {"status":"ok"} |
/api/v1/health |
Detailed status | DB + Redis status with latency |
/api/v1/health/live |
Kubernetes liveness | {"status":"ok"} |
/api/v1/health/ready |
Kubernetes readiness | 200 if dependencies up |
Add to crontab:
*/5 * * * * curl -fsS --retry 3 https://hc-ping.com/your-uuid > /dev/nullLogs are JSON-formatted. Send to external service:
# Example: tail logs to stdout for external collector
docker compose -f docker-compose.prod.yml logs -f --no-log-prefix api | \
your-log-shipperSymptoms: HTTPS not working, certificate errors
Solutions:
- Verify domain DNS is pointing to server
- Check Traefik logs:
docker compose -f docker-compose.prod.yml logs traefik - Ensure ports 80/443 are open
- Let's Encrypt rate limits: wait 1 hour if exceeded
Reset certificates:
docker compose -f docker-compose.prod.yml down
docker volume rm blockbot_letsencrypt
docker compose -f docker-compose.prod.yml up -dSymptoms: API returns 503, health check shows database unhealthy
Solutions:
- Check PostgreSQL is running:
docker compose -f docker-compose.prod.yml ps postgres - View PostgreSQL logs:
docker compose -f docker-compose.prod.yml logs postgres - Verify
POSTGRES_PASSWORDmatches in.env - Check disk space:
df -h
Symptoms: Rate limiting not working, health check shows redis unhealthy
Solutions:
- Check Redis is running:
docker compose -f docker-compose.prod.yml ps redis - View Redis logs:
docker compose -f docker-compose.prod.yml logs redis - Test connection:
docker compose -f docker-compose.prod.yml exec redis redis-cli ping
Symptoms: Container keeps restarting, 502 errors
Solutions:
- Check API logs:
docker compose -f docker-compose.prod.yml logs api - Verify all environment variables are set
- Ensure database migrations have run
- Check memory:
free -m
Symptoms: Slow response times, OOM kills
Solutions:
- Check memory:
docker stats - Add swap space:
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
- Restart services to clear memory:
docker compose -f docker-compose.prod.yml restart
- Strong passwords generated for all secrets
- Firewall configured (UFW or iptables)
- SSH key authentication enabled, password disabled
- Regular backups configured
- Fail2ban installed for SSH protection
- Server updates automated (
unattended-upgrades)
For horizontal scaling, consider:
- Load balancer in front of multiple API instances
- Managed PostgreSQL (RDS, Cloud SQL)
- Managed Redis (ElastiCache, Redis Cloud)
- Container orchestration (Docker Swarm, Kubernetes)