go-first/docs/DEPLOYMENT.md
2026-10-07 16:30:34 +02:00

9.3 KiB

Deployment Guide — go-first / URPI

This guide covers deploying the go-first application to production at https://www.urpi.be.


🏗️ Architecture Overview

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Users     │────▶│   NGINX     │────▶│   Go App    │
│  (HTTPS)    │     │  (SSL/TLS)  │     │  (Port 8080)│
└─────────────┘     └─────────────┘     └──────┬──────┘
                                               │
                    ┌─────────────┐     ┌──────▼──────┐
                    │  PostgreSQL │◀───▶│   Redis     │
                    │  (Port 5432)│     │  (Port 6379)│
                    └─────────────┘     └─────────────┘

📋 Prerequisites

Server Requirements

  • OS: Ubuntu 22.04+ / Debian 12+ / Alpine Linux
  • CPU: 2+ vCPUs
  • RAM: 4GB+ (2GB minimum for app + DB)
  • Disk: 20GB+ SSD
  • Network: Public IPv4/IPv6, ports 80, 443, 22

Software

  • Docker 24+ & Docker Compose 2+
  • Git
  • Certbot (Let's Encrypt)
  • (Optional) kubectl for Kubernetes

🔧 Server Setup

1. Initial Server Hardening

# Update system
sudo apt update && sudo apt upgrade -y

# Create deploy user
sudo adduser deploy --gecos "" --disabled-password
sudo usermod -aG docker deploy
sudo usermod -aG sudo deploy

# Setup SSH keys (from your local machine)
ssh-copy-id deploy@your-server-ip

# Configure firewall
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable

2. Install Docker

# Install Docker
curl -fsSL https://get.docker.com | sh

# Install Docker Compose
sudo apt install -y docker-compose-plugin

# Verify
docker --version
docker compose version

3. Install Certbot

sudo apt install -y certbot python3-certbot-nginx

📦 Application Deployment

1. Clone Repository

cd /opt
sudo git clone https://github.com/dnoulet/go-first.git
sudo chown -R deploy:deploy go-first
cd go-first

2. Configure Environment

# Copy production template
cp .env.production.example .env.production

# Edit with your values (use strong passwords!)
vim .env.production

# Required changes:
# - DATABASE_PASSWORD: Strong random password
# - JWT_SECRET: 32+ char random string
# - EMAIL_*: Your SMTP credentials

3. Generate SSL Certificates

# Stop any service on port 80
sudo systemctl stop nginx 2>/dev/null || true

# Get certificates
sudo certbot certonly --standalone \
  -d www.urpi.be \
  -d urpi.be \
  --email daniel.noulet@urpi.be \
  --agree-tos --non-interactive

# Certificates will be at:
# /etc/letsencrypt/live/www.urpi.be/fullchain.pem
# /etc/letsencrypt/live/www.urpi.be/privkey.pem

4. Setup NGINX SSL Directory

sudo mkdir -p /opt/go-first/deploy/ssl
sudo cp /etc/letsencrypt/live/www.urpi.be/fullchain.pem /opt/go-first/deploy/ssl/
sudo cp /etc/letsencrypt/live/www.urpi.be/privkey.pem /opt/go-first/deploy/ssl/
sudo chown -R deploy:deploy /opt/go-first/deploy/ssl

5. Deploy with Docker Compose

cd /opt/go-first

# Build and start
docker compose --env-file .env.production up -d --build

# Verify
docker compose --env-file .env.production ps
docker compose --env-file .env.production logs -f app

6. Verify Deployment

# Health check
curl -f https://www.urpi.be/api/v1/health

# Check frontend
curl -f https://www.urpi.be/ | grep -c "URPI"

🔐 SSL Certificate Renewal

Automatic Renewal (systemd timer)

# Check certbot timer
sudo systemctl status certbot.timer

# Test renewal
sudo certbot renew --dry-run

Manual Renewal Hook

# Create renewal hook
sudo tee /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh > /dev/null <<'EOF'
#!/bin/bash
cp /etc/letsencrypt/live/www.urpi.be/fullchain.pem /opt/go-first/deploy/ssl/
cp /etc/letsencrypt/live/www.urpi.be/privkey.pem /opt/go-first/deploy/ssl/
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production exec nginx nginx -s reload
EOF

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh

🗄️ Database Management

Backup

# Create backup script
cat > /opt/go-first/backup.sh <<'EOF'
#!/bin/bash
set -euo pipefail

BACKUP_DIR="/opt/backups/go-first"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"

docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production \
  exec -T postgres pg_dump -U postgres go_first | gzip > "$BACKUP_DIR/go_first_$DATE.sql.gz"

# Keep last 30 days
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete

echo "Backup saved to $BACKUP_DIR/go_first_$DATE.sql.gz"
EOF

chmod +x /opt/go-first/backup.sh

# Add to crontab (daily at 2 AM)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/go-first/backup.sh") | crontab -

Restore

# Stop app
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production stop app

# Restore
gunzip -c /opt/backups/go-first/go_first_20240115_020000.sql.gz | \
  docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production exec -T postgres psql -U postgres go_first

# Start app
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production start app

📊 Monitoring & Logging

Log Aggregation (Optional: Loki + Promtail)

# Add to docker-compose.yml
services:
  promtail:
    image: grafana/promtail:2.9
    volumes:
      - /var/log:/var/log
      - /opt/go-first/deploy/promtail.yml:/etc/promtail/config.yml
    depends_on:
      - loki

  loki:
    image: grafana/loki:2.9
    volumes:
      - loki_data:/loki

Health Checks

# Application health
curl https://www.urpi.be/api/v1/health

# Database
docker compose exec postgres pg_isready -U postgres

# Redis
docker compose exec redis redis-cli ping

# NGINX
docker compose exec nginx nginx -t

Metrics (Optional: Prometheus + Grafana)

Add to docker-compose.yml:

  prometheus:
    image: prom/prometheus:v2.47
    volumes:
      - ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana:10.2
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana

🔄 Updates & Rollbacks

Update Application

cd /opt/go-first

# Pull latest code
git pull origin main

# Rebuild and deploy
docker compose --env-file .env.production up -d --build --remove-orphans

# Verify
curl -f https://www.urpi.be/api/v1/health

Rollback

# List images
docker images ghcr.io/dnoulet/go-first

# Rollback to previous tag
docker tag ghcr.io/dnoulet/go-first:previous-tag ghcr.io/dnoulet/go-first:latest
docker compose --env-file .env.production up -d --no-deps app

Zero-Downtime Deploy (with multiple replicas)

# In docker-compose.yml, add:
deploy:
  replicas: 2
  update_config:
    parallelism: 1
    delay: 10s
    order: start-first
  restart_policy:
    condition: on-failure

🛡️ Security Checklist

  • Strong DATABASE_PASSWORD (32+ chars)
  • Strong JWT_SECRET (32+ chars, random)
  • SMTP credentials secured
  • Firewall: only 22, 80, 443 open
  • SSH key-only authentication
  • Fail2ban configured for SSH
  • Regular security updates (apt update && apt upgrade)
  • Docker images scanned for vulnerabilities (docker scout cves)
  • NGINX security headers enabled
  • Rate limiting configured
  • CSP headers configured
  • HSTS enabled
  • Database not exposed publicly
  • Redis not exposed publicly
  • Backup encryption at rest
  • Audit logging enabled

🚨 Troubleshooting

Application Won't Start

# Check logs
docker compose --env-file .env.production logs app

# Common issues:
# - Port 8080 already in use
# - Database connection failed
# - Config file missing
# - Permission denied on volumes

502 Bad Gateway

# Check if app is healthy
docker compose --env-file .env.production ps app

# Check NGINX config
docker compose --env-file .env.production exec nginx nginx -t

# Check upstream
docker compose --env-file .env.production exec nginx curl -f http://app:8080/api/v1/health

SSL Certificate Issues

# Check certificate validity
openssl x509 -in /etc/letsencrypt/live/www.urpi.be/fullchain.pem -text -noout

# Check NGINX SSL config
docker compose --env-file .env.production exec nginx nginx -T | grep ssl

Database Connection Issues

# Test connection
docker compose --env-file .env.production exec postgres psql -U postgres -d go_first -c "SELECT 1;"

# Check credentials in .env.production match docker-compose.yml

📞 Support

For deployment issues:


📝 Maintenance Windows

Recommended: Sunday 02:00-04:00 UTC

  • System updates
  • Docker image updates
  • Database maintenance
  • Certificate renewal verification