Files
b0esche_cloud/docs/DEPLOYMENT.md

12 KiB

b0esche.cloud Deployment Guide

This guide covers production deployment, server configuration, and operations.

Production Architecture

Server Overview

Component Domain Port Container
Flutter Web www.b0esche.cloud 80 flutter-web
Go Backend go.b0esche.cloud 8080 go-backend
PostgreSQL internal 5432 go-postgres
Nextcloud storage.b0esche.cloud 80 nextcloud
Collabora of.b0esche.cloud 9980 collabora
Traefik - 80, 443 traefik

Server Directory Structure

/opt/
├── traefik/
│   ├── docker-compose.yml    # Traefik + Nextcloud + Collabora
│   ├── traefik.yml           # Static configuration
│   ├── .env                  # DNS credentials
│   └── acme/                 # SSL certificates
├── go/
│   ├── docker-compose.yml    # Go backend + PostgreSQL
│   ├── .env.production       # Production environment
│   └── data/
│       └── postgres/
│           └── backend/
│               └── go_cloud/ # Backend source code
├── flutter/
│   ├── docker-compose.yml    # Nginx for Flutter
│   ├── nginx.conf            # Nginx configuration
│   └── web/                  # Built Flutter files
├── scripts/
│   ├── auto-deploy.sh        # Daily auto-deployment
│   ├── deploy-now.sh         # Manual deployment trigger
│   ├── backup.sh             # Backup script
│   ├── monitor.sh            # Health monitoring
│   └── webhook-server.py     # GitLab webhook receiver
└── auto-deploy/
    └── b0esche_cloud_rollout/ # Deployment workspace

Deployment Methods

Deployments run automatically at 3 AM daily via cron:

0 3 * * * /opt/scripts/auto-deploy.sh >> /var/log/auto-deploy.log 2>&1

The auto-deploy script:

  1. Pulls latest changes from GitLab
  2. Builds Flutter web app
  3. Rebuilds Go backend Docker image
  4. Restarts services
  5. Validates health checks

2. Manual Deployment (Immediate)

Trigger an immediate deployment:

# From local machine
ssh b0esche-cloud '/opt/scripts/deploy-now.sh'

# Or directly on server
/opt/scripts/deploy-now.sh

3. GitLab Webhook (On Push)

The webhook server listens for push events:

# Start webhook server (runs as systemd service)
systemctl start webhook-server

# Check webhook logs
journalctl -u webhook-server -f

Service Management

Starting All Services

# Start in order (dependencies first)
cd /opt/traefik && docker-compose up -d
cd /opt/go && docker-compose up -d
cd /opt/flutter && docker-compose up -d

Stopping All Services

cd /opt/flutter && docker-compose down
cd /opt/go && docker-compose down
cd /opt/traefik && docker-compose down

Restarting Individual Services

# Restart Go backend
cd /opt/go && docker-compose restart go-backend

# Restart Flutter frontend
cd /opt/flutter && docker-compose restart flutter-web

# Restart Traefik (caution: brief SSL interruption)
cd /opt/traefik && docker-compose restart traefik

Viewing Logs

# Follow Go backend logs
docker logs -f go-backend

# Follow Flutter/Nginx logs
docker logs -f flutter-web

# Follow Traefik logs
docker logs -f traefik

# All logs with timestamps
docker logs -f --timestamps go-backend

Configuration Files

Traefik Configuration

docker-compose.yml:

version: '3.8'

services:
  traefik:
    image: traefik:v2.10
    container_name: traefik
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./acme:/etc/traefik/acme
    networks:
      - proxy
    restart: unless-stopped

networks:
  proxy:
    external: true

traefik.yml:

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@b0esche.cloud
      storage: /etc/traefik/acme/acme.json
      dnsChallenge:
        provider: bunny
        delayBeforeCheck: 30

providers:
  docker:
    exposedByDefault: false

Go Backend Configuration

docker-compose.yml:

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    container_name: go-postgres
    environment:
      POSTGRES_USER: go_backend
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: go_backend
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - backend
    restart: unless-stopped

  go-backend:
    build:
      context: ./data/postgres/backend/go_cloud
      dockerfile: Dockerfile
    container_name: go-backend
    env_file: .env.production
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.go.rule=Host(`go.b0esche.cloud`)"
      - "traefik.http.routers.go.tls.certresolver=letsencrypt"
    depends_on:
      - postgres
    networks:
      - proxy
      - backend
    restart: unless-stopped

networks:
  proxy:
    external: true
  backend:
    driver: bridge

Flutter/Nginx Configuration

docker-compose.yml:

version: '3.8'

services:
  flutter-web:
    image: nginx:alpine
    container_name: flutter-web
    volumes:
      - ./web:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.flutter.rule=Host(`www.b0esche.cloud`)"
      - "traefik.http.routers.flutter.tls.certresolver=letsencrypt"
    networks:
      - proxy
    restart: unless-stopped

networks:
  proxy:
    external: true

nginx.conf:

server {
    listen 80;
    server_name www.b0esche.cloud;
    root /usr/share/nginx/html;
    index index.html;

    # Flutter web app routing
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
}

Database Operations

Running Migrations

# Enter backend container
docker exec -it go-backend sh

# Run migrations
./api migrate up

# Or from host
docker exec go-backend ./api migrate up

Database Backup

# Manual backup
docker exec go-postgres pg_dump -U go_backend -Fc go_backend > backup.sqlc

# Restore from backup
docker exec -i go-postgres pg_restore -U go_backend -d go_backend < backup.sqlc

Connecting to Database

# Via docker exec
docker exec -it go-postgres psql -U go_backend -d go_backend

# Common queries
\dt                          # List tables
\d users                     # Describe table
SELECT count(*) FROM users;  # Count users

SSL Certificate Management

Certificate Status

# Check certificate expiry
docker exec traefik cat /etc/traefik/acme/acme.json | jq '.letsencrypt.Certificates[].certificate.NotAfter'

# Force certificate renewal
docker restart traefik

Manual Certificate Operations

# Backup certificates
cp -r /opt/traefik/acme /opt/traefik/acme.backup

# View certificate details
openssl s_client -connect www.b0esche.cloud:443 -servername www.b0esche.cloud </dev/null 2>/dev/null | openssl x509 -noout -dates

Monitoring

Health Checks

# Check all services
/opt/scripts/monitor.sh

# Manual health checks
curl -s https://go.b0esche.cloud/health
curl -s -o /dev/null -w "%{http_code}" https://www.b0esche.cloud
curl -s -o /dev/null -w "%{http_code}" https://storage.b0esche.cloud

Container Status

# All containers
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# Resource usage
docker stats --no-stream

# Container health
docker inspect --format='{{.State.Health.Status}}' go-backend

Disk Usage

# Docker disk usage
docker system df

# PostgreSQL data size
du -sh /opt/go/data/postgres

# Log sizes
du -sh /var/lib/docker/containers/*/

Backup Strategy

Automated Backups

Backups run daily via cron:

0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

Backup Contents

  1. PostgreSQL database (pg_dump)
  2. Nextcloud database (mysqldump)
  3. Traefik certificates (/opt/traefik/acme)
  4. Configuration files (.env, docker-compose.yml)
  5. Nextcloud data volume

Backup Retention

  • Keep backups for 30 days
  • Stored in /opt/backups/b0esche_cloud/
  • Compressed as .tar.gz

Manual Backup

# Run backup now
/opt/scripts/backup.sh

# List backups
ls -lh /opt/backups/b0esche_cloud/

Restore Procedure

# 1. Stop services
cd /opt/go && docker-compose down
cd /opt/flutter && docker-compose down

# 2. Extract backup
cd /opt/backups/b0esche_cloud
tar -xzf 20260113_020000.tar.gz

# 3. Restore database
docker exec -i go-postgres pg_restore -U go_backend -d go_backend < go_backend.sqlc

# 4. Restore configurations
cp .env.production /opt/go/
cp go-docker-compose.yml /opt/go/docker-compose.yml

# 5. Restart services
cd /opt/go && docker-compose up -d
cd /opt/flutter && docker-compose up -d

Troubleshooting

Common Issues

Service won't start

# Check logs for errors
docker logs go-backend --tail 50

# Check container status
docker inspect go-backend | jq '.[0].State'

# Check port conflicts
netstat -tlnp | grep -E '80|443|8080'

Database connection issues

# Test database connectivity
docker exec go-backend ping -c 3 postgres

# Check PostgreSQL logs
docker logs go-postgres --tail 50

# Verify credentials
docker exec go-postgres psql -U go_backend -c "SELECT 1"

SSL certificate errors

# Check certificate status
curl -vI https://www.b0esche.cloud 2>&1 | grep -A 5 "Server certificate"

# Force renewal
docker restart traefik
sleep 60
curl -vI https://www.b0esche.cloud

Out of disk space

# Check disk usage
df -h

# Clean Docker resources
docker system prune -a --volumes

# Clean old backups
find /opt/backups -mtime +30 -delete

# Clean old logs
truncate -s 0 /var/log/auto-deploy.log

Emergency Procedures

Rollback Deployment

# 1. Stop current services
cd /opt/go && docker-compose down
cd /opt/flutter && docker-compose down

# 2. Checkout previous version
cd /opt/auto-deploy/b0esche_cloud_rollout
git log --oneline -10  # Find last working commit
git checkout <commit-hash>

# 3. Redeploy
/opt/scripts/auto-deploy.sh

Database Recovery

# Find latest backup
ls -lt /opt/backups/b0esche_cloud/ | head -5

# Restore (see Restore Procedure above)

Full System Recovery

  1. Provision new server
  2. Install Docker
  3. Copy /opt from backup
  4. Start services in order
  5. Restore database from backup
  6. Verify health checks

Security Checklist

  • All services behind Traefik (no direct port exposure)
  • SSL certificates valid and auto-renewing
  • Database not accessible from internet
  • Strong passwords in .env.production
  • Regular backups verified
  • Firewall configured (only 80, 443, 22 open)
  • SSH key authentication only
  • Auto-deploy logs monitored

Performance Tuning

PostgreSQL

-- Check slow queries
SELECT * FROM pg_stat_activity WHERE state = 'active';

-- Analyze tables
ANALYZE;

-- Vacuum
VACUUM ANALYZE;

Nginx

# Add to nginx.conf for better performance
worker_connections 1024;
keepalive_timeout 65;
gzip_comp_level 6;

Docker

# Limit container resources
docker update --memory="512m" --cpus="1" go-backend

# Clean up unused resources
docker system prune -f