Skip to content

Latest commit

 

History

History
618 lines (454 loc) · 15 KB

File metadata and controls

618 lines (454 loc) · 15 KB

Synkronus Production Deployment Guide

This guide covers deploying Synkronus in production using Docker Compose.

Recommended Production Setup

For a production deployment, we recommend:

  • Clean Linux server (Ubuntu 22.04 LTS or Debian 12)
  • Docker & Docker Compose installed
  • Cloudflared tunnel for secure external access (no port forwarding needed)
  • PostgreSQL database (dockerized via docker-compose)
  • Nginx reverse proxy (included in docker-compose)
  • Persistent volumes for data storage

Quick Start

1. Server Preparation

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

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

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

# Verify installation
docker --version
docker compose version

2. Deploy Synkronus

# Create deployment directory
mkdir -p ~/synkronus
cd ~/synkronus

# Download configuration files
wget https://raw.githubusercontent.com/opendataensemble/ode/main/synkronus/docker-compose.example.yml -O docker-compose.yml
wget https://raw.githubusercontent.com/opendataensemble/ode/main/synkronus/nginx.conf

# Generate secure secrets
JWT_SECRET=$(openssl rand -base64 32)
DB_ROOT_PASSWORD=$(openssl rand -base64 24)
ADMIN_PASSWORD=$(openssl rand -base64 16)

# Update docker-compose.yml with secrets (PostgreSQL root and Synkronus secrets)
sed -i "s/CHANGE_THIS_PASSWORD/$DB_ROOT_PASSWORD/g" docker-compose.yml
sed -i "s/CHANGE_THIS_TO_RANDOM_32_CHAR_STRING/$JWT_SECRET/g" docker-compose.yml
sed -i "s/CHANGE_THIS_ADMIN_PASSWORD/$ADMIN_PASSWORD/g" docker-compose.yml

# Start the stack (Postgres, Synkronus, nginx)
docker compose up -d

# Verify it's running via nginx health endpoint
curl http://localhost/health

3. Create Database and User for Synkronus

In this setup we use one PostgreSQL container and create one or more application databases inside it using the root postgres user. This is the recommended way to initialize databases for Synkronus.

# Open a psql shell into the Postgres container (as root user)
docker compose exec postgres psql -U postgres

From the psql prompt, create a role (user) and a database owned by that role for Synkronus:

-- Replace names/passwords as appropriate
CREATE ROLE synkronus_user LOGIN PASSWORD 'CHANGE_THIS_APP_PASSWORD';
CREATE DATABASE synkronus OWNER synkronus_user;

Then update the synkronus service DB_CONNECTION in docker-compose.yml to match:

services:
  synkronus:
    environment:
      DB_CONNECTION: "postgres://synkronus_user:CHANGE_THIS_APP_PASSWORD@postgres:5432/synkronus?sslmode=disable"

To add additional Synkronus instances later, repeat the same pattern:

CREATE ROLE synkronus_another_user LOGIN PASSWORD 'another_password';
CREATE DATABASE synkronus_another OWNER synkronus_another_user;

and point a new Synkronus service at that database with its own DB_CONNECTION.

3. Set Up Cloudflared Tunnel (Optional but Recommended)

Cloudflared provides secure external access without exposing ports or managing SSL certificates.

Install Cloudflared

# Download and install
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# Verify installation
cloudflared --version

Create Tunnel

# Login to Cloudflare
cloudflared tunnel login

# Create tunnel
cloudflared tunnel create synkronus

# Note the tunnel ID from the output

Configure Tunnel

Create ~/.cloudflared/config.yml:

tunnel: <your-tunnel-id>
credentials-file: /root/.cloudflared/<your-tunnel-id>.json

ingress:
  - hostname: synkronus.your-domain.com
    service: http://localhost:80
  - service: http_status:404

Route DNS

# Route your domain to the tunnel
cloudflared tunnel route dns synkronus synkronus.your-domain.com

Run Tunnel as Service

# Install as systemd service
sudo cloudflared service install

# Start service
sudo systemctl start cloudflared
sudo systemctl enable cloudflared

# Check status
sudo systemctl status cloudflared

Your Synkronus instance is now accessible at https://synkronus.your-domain.com with automatic SSL!

Environment Variables

Required Variables

Variable Description Example
DB_CONNECTION PostgreSQL connection string postgres://user:pass@postgres:5432/synkronus
JWT_SECRET Secret key for JWT token signing Generate with openssl rand -base64 32

Optional Variables

Variable Default Description
PORT 8080 HTTP server port
LOG_LEVEL info Logging level (debug, info, warn, error)
APP_BUNDLE_PATH /app/data/app-bundles Path for app bundle storage
MAX_VERSIONS_KEPT 5 Number of app bundle versions to retain
ADMIN_USERNAME admin Initial admin username
ADMIN_PASSWORD admin Initial admin password (CHANGE THIS!)

Volume Management

Persistent Volumes

The docker-compose setup creates two persistent volumes:

  1. postgres-data: PostgreSQL database files
  2. app-bundles: Uploaded application bundles
# List volumes
docker volume ls

# Inspect volume
docker volume inspect synkronus_postgres-data

# Backup volume
docker run --rm -v synkronus_postgres-data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data

# Restore volume
docker run --rm -v synkronus_postgres-data:/data -v $(pwd):/backup alpine tar xzf /backup/postgres-backup.tar.gz -C /

App Bundle Directory Permissions

When you bind-mount a host directory for app-bundles, the synkronus container must be able to write to that path. The container runs as user synkronus with uid=1000 and gid=1000, so the host directory should be owned (or at least writable) by 1000:1000.

# Check running containers
docker ps

# Confirm the user inside the synkronus container
docker exec -it <synkronus-container-id> id

# Example: fix permissions on a host directory used for app bundles
sudo chown -R 1000:1000 ~/server/app-bundles

If you use a different host path, adjust the chown command accordingly. After fixing permissions, you may need to restart the stack:

docker compose restart synkronus

Monitoring and Maintenance

View Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f synkronus
docker compose logs -f postgres
docker compose logs -f nginx

# Last 100 lines
docker compose logs --tail=100 synkronus

Health Checks

# Check service status
docker compose ps

# Test health endpoint
curl http://localhost/health

# Via cloudflared tunnel
curl https://synkronus.your-domain.com/health

Restart Services

# Restart all services
docker compose restart

# Restart specific service
docker compose restart synkronus

# Reload nginx configuration
docker compose exec nginx nginx -s reload

Update to Latest Version

# Pull latest image
docker compose pull

# Recreate containers with new image
docker compose up -d

# Remove old images
docker image prune -f

Backup and Restore

Database Backup

# Create backup
docker compose exec postgres pg_dump -U synkronus_user synkronus > backup-$(date +%Y%m%d).sql

# Automated daily backups (add to crontab)
0 2 * * * cd ~/synkronus && docker compose exec -T postgres pg_dump -U synkronus_user synkronus > /backups/synkronus-$(date +\%Y\%m\%d).sql

Database Restore

# Restore from backup
docker compose exec -T postgres psql -U synkronus_user synkronus < backup-20250114.sql

Full System Backup

# Backup everything
tar czf synkronus-full-backup-$(date +%Y%m%d).tar.gz \
  docker-compose.yml \
  nginx.conf \
  $(docker volume inspect synkronus_postgres-data --format '{{ .Mountpoint }}') \
  $(docker volume inspect synkronus_app-bundles --format '{{ .Mountpoint }}')

Security Best Practices

1. Use Strong Secrets

# Generate strong JWT secret
openssl rand -base64 32

# Generate strong passwords
openssl rand -base64 24

2. Change Default Admin Password

# After first deployment, change admin password via API
curl -X POST https://synkronus.your-domain.com/users/change-password \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '{"current_password":"old","new_password":"new"}'

3. Regular Updates

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

# Update Docker images
docker compose pull
docker compose up -d

4. Firewall Configuration

# If not using cloudflared, configure firewall
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

5. Enable SSL (if not using Cloudflared)

Update nginx.conf to include SSL configuration and mount certificates in docker-compose.yml.

Troubleshooting

Service Won't Start

# Check logs
docker compose logs synkronus

# Check environment variables
docker compose config

# Verify database connection
docker compose exec synkronus sh
# Inside container:
apk add postgresql-client
psql "$DB_CONNECTION"

Database Connection Issues

# Check PostgreSQL is running
docker compose ps postgres

# Check PostgreSQL logs
docker compose logs postgres

# Test connection from synkronus container
docker compose exec synkronus sh -c 'apk add postgresql-client && psql "$DB_CONNECTION"'

Nginx Issues

# Test nginx configuration
docker compose exec nginx nginx -t

# Reload nginx
docker compose exec nginx nginx -s reload

# Check nginx logs
docker compose logs nginx

Cloudflared Tunnel Issues

# Check tunnel status
sudo systemctl status cloudflared

# View tunnel logs
sudo journalctl -u cloudflared -f

# Test tunnel connectivity
cloudflared tunnel info synkronus

Port Already in Use

# Find what's using port 80
sudo lsof -i :80

# Stop conflicting service
sudo systemctl stop apache2  # or nginx, etc.

Performance Tuning

PostgreSQL Optimization

Add to docker-compose.yml under postgres service:

command:
  - "postgres"
  - "-c"
  - "max_connections=100"
  - "-c"
  - "shared_buffers=256MB"
  - "-c"
  - "effective_cache_size=1GB"
  - "-c"
  - "maintenance_work_mem=64MB"
  - "-c"
  - "checkpoint_completion_target=0.9"
  - "-c"
  - "wal_buffers=16MB"
  - "-c"
  - "default_statistics_target=100"

Resource Limits

Add to docker-compose.yml under each service:

deploy:
  resources:
    limits:
      cpus: '1.0'
      memory: 512M
    reservations:
      cpus: '0.5'
      memory: 256M

Nginx Caching

Update nginx.conf to add caching for static assets:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
proxy_cache my_cache;

Scaling

Horizontal Scaling

To run multiple Synkronus instances:

# In docker-compose.yml
synkronus:
  image: ghcr.io/opendataensemble/synkronus:latest
  deploy:
    replicas: 3

Update nginx.conf upstream:

upstream synkronus_backend {
    least_conn;
    server synkronus:8080;
    # Add more instances as needed
}

External PostgreSQL

For better performance, use a managed PostgreSQL service:

  1. Remove postgres service from docker-compose.yml
  2. Update DB_CONNECTION to point to external database
  3. Ensure network connectivity

Monitoring

Prometheus Metrics (Future Enhancement)

Add to docker-compose.yml:

prometheus:
  image: prom/prometheus
  volumes:
    - ./prometheus.yml:/etc/prometheus/prometheus.yml
    - prometheus-data:/prometheus
  ports:
    - "9090:9090"

grafana:
  image: grafana/grafana
  ports:
    - "3000:3000"
  volumes:
    - grafana-data:/var/lib/grafana

Log Aggregation

Use Docker logging drivers:

# In docker-compose.yml
logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

Architecture Diagram

┌─────────────────────────────────────────┐
│         Cloudflared Tunnel              │
│         (Optional - Cloudflare)         │
│         Automatic SSL/TLS               │
└──────────────┬──────────────────────────┘
               │ HTTPS
               ▼
┌─────────────────────────────────────────┐
│         Nginx Reverse Proxy             │
│         Port 80/443                     │
│         - Load balancing                │
│         - Request routing               │
│         - Compression                   │
└──────────────┬──────────────────────────┘
               │ HTTP
               ▼
┌─────────────────────────────────────────┐
│         Synkronus Container             │
│         Port 8080 (internal)            │
│         - API endpoints                 │
│         - Business logic                │
│         - File storage                  │
└──────────────┬──────────────────────────┘
               │ PostgreSQL protocol
               ▼
┌─────────────────────────────────────────┐
│         PostgreSQL Database             │
│         Port 5432 (internal)            │
│         - Data persistence              │
│         - Transactions                  │
└─────────────────────────────────────────┘

Support and Resources

Documentation

External Resources

Getting Help

  1. Check logs: docker compose logs
  2. Review this guide
  3. Check GitHub issues
  4. Consult the troubleshooting section

Production Checklist

Before going live:

  • Strong JWT secret generated
  • Strong database password set
  • Admin password changed from default
  • Cloudflared tunnel configured (or SSL certificates installed)
  • Backup strategy implemented
  • Monitoring configured
  • Health checks passing
  • Firewall configured (if not using Cloudflared)
  • Resource limits set
  • Log rotation configured
  • Documentation reviewed
  • Test deployment verified

Your Synkronus instance is now ready for production! 🚀