07 — Docker & Deployment

How to build, configure, and deploy Forge Platform to production.


Architecture

Forge Platform uses a separated architecture with independent Docker images:

Service Image Purpose
forge-web ghcr.io/forgeplatform/forge-backend Django API (uwsgi + daphne + nginx-internal)
forge-task ghcr.io/forgeplatform/forge-backend Task execution (dispatcher, callback, receptor)
forge-init ghcr.io/forgeplatform/forge-backend One-shot: migrations, admin user, provisioning
forge-frontend ghcr.io/forgeplatform/forge-frontend React SPA served by nginx
postgres postgres:15-alpine Database
redis redis:7-alpine Cache and message broker
nginx nginx:1.27-alpine TLS termination, routing

Startup Order

postgres ──► redis ──► forge-init ──► forge-web ──► forge-task ──► nginx
                                                     forge-frontend ──┘

Each service waits for the previous one to be healthy before starting.

Request Routing (External Nginx)

Path Destination Description
/api/* forge-web:8013 REST API
/sso/* forge-web:8013 SSO/SAML/LDAP
/api/login/ forge-web:8013 Login (rate-limited)
/(api/)?websocket/ forge-web:8013 WebSocket (upgrade)
/* (everything else) forge-frontend:80 React SPA

Building Docker Images

Backend

cd forge-backend
docker build -t ghcr.io/forgeplatform/forge-backend:latest .
docker push ghcr.io/forgeplatform/forge-backend:latest

The Dockerfile is a multi-stage build: 1. builder (Ubuntu 24.04): installs Python deps, builds sdist, runs collectstatic 2. runtime (Ubuntu 24.04): minimal image with runtime deps, receptor, supervisor

Frontend

cd forge-frontend
docker build -t ghcr.io/forgeplatform/forge-frontend:latest .
docker push ghcr.io/forgeplatform/forge-frontend:latest

The Dockerfile is a multi-stage build: 1. builder (Node 20 Alpine): npm ci && npm run build 2. runtime (nginx 1.27 Alpine): serves built assets with SPA fallback


Production Deployment

Prerequisites

Quick Start

cd forge-deploy

# 1. Create configuration
cp .env.example .env
# Edit .env — set all REQUIRED values (see below)

# 2. SSL certificates
mkdir -p nginx/ssl

# Let's Encrypt (production):
certbot certonly --standalone -d forge.example.com
cp /etc/letsencrypt/live/forge.example.com/fullchain.pem nginx/ssl/
cp /etc/letsencrypt/live/forge.example.com/privkey.pem nginx/ssl/

# Or self-signed (testing):
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout nginx/ssl/privkey.pem -out nginx/ssl/fullchain.pem \
  -subj "/CN=forge.example.com"

# 3. Deploy
docker compose up -d

# 4. Watch initialization
docker compose logs -f forge-init

# 5. Verify
curl -k https://forge.example.com/api/v2/ping/

Deploy in Vagrant (testing)

cd forge-deploy
vagrant up          # Ubuntu 24.04 VM + Docker + Compose + SSL + .env auto-generated
vagrant ssh
cd /forge-deploy
docker compose up -d

# Access from host: https://192.168.56.22/

Environment Variables

Required

Variable Description Generate with...
POSTGRES_PASSWORD DB password openssl rand -hex 16
FORGE_SECRET_KEY Django crypto key openssl rand -hex 32
FORGE_BROADCAST_WEBSOCKET_SECRET WS auth secret openssl rand -hex 32
FORGE_ADMIN_PASSWORD Admin password Strong password
FORGE_CSRF_TRUSTED_ORIGINS CSRF origins https://forge.example.com

Optional

Variable Default Description
FORGE_ALLOWED_HOSTS * Allowed HTTP hosts
FORGE_ADMIN_USER admin Admin username
FORGE_ADMIN_EMAIL admin@example.com Admin email
FORGE_NODE_NAME forge-node Instance hostname
FORGE_NODE_TYPE hybrid hybrid, control, or execution
FORGE_BACKEND_IMAGE ghcr.io/forgeplatform/forge-backend Backend Docker image
FORGE_FRONTEND_IMAGE ghcr.io/forgeplatform/forge-frontend Frontend Docker image
FORGE_TAG latest Image tag
NGINX_HTTP_PORT 80 External HTTP port
NGINX_HTTPS_PORT 443 External HTTPS port

Watch out


SSL/TLS

certbot certonly --standalone -d forge.example.com
cp /etc/letsencrypt/live/forge.example.com/{fullchain,privkey}.pem nginx/ssl/

Auto-renewal (crontab):

0 0 1 * * certbot renew && cp /etc/letsencrypt/live/forge.example.com/*.pem /path/to/nginx/ssl/ && docker compose restart nginx

Security Notes


Backup & Restore

Backup

docker compose exec forge-task bash /etc/forge/backup.sh

# With custom retention (30 days)
docker compose exec forge-task bash /etc/forge/backup.sh 30

Scheduled backup (crontab)

0 2 * * * cd /path/to/forge-deploy && docker compose exec -T forge-task bash /etc/forge/backup.sh

Restore

docker compose stop forge-web forge-task
gunzip -c forge_backup_20260317.sql.gz | docker compose exec -T postgres psql -U forge forge
docker compose start forge-web forge-task

Health Checks

# API ping (no auth)
curl -k https://forge.example.com/api/v2/ping/

# Instance capacity (auth required)
curl -k -u admin:password https://forge.example.com/api/v2/instances/

# Service status
docker compose ps

# Supervisor processes
docker compose exec forge-web supervisorctl status
docker compose exec forge-task supervisorctl status

Troubleshooting

Container won't start

docker compose logs forge-init    # Check migrations and init
# "database does not exist" → POSTGRES_DB mismatch
# "authentication failed" → POSTGRES_PASSWORD mismatch

Can't log in (403 CSRF)

# Check FORGE_CSRF_TRUSTED_ORIGINS in .env
# Must be full URL with https:// (e.g., https://192.168.56.22)

Server Error (500 on root page)

# Check if frontend container is running
docker compose ps forge-frontend
# Must be healthy

# Check nginx routing
docker compose logs nginx

Jobs not running

docker compose exec forge-task supervisorctl status
# All 4 must be RUNNING: receptor, dispatcher, callback-receiver, wsrelay

docker compose exec forge-web forge-manage list_instances

Forgotten admin password

docker compose exec forge-web forge-manage update_password --username=admin --password=NewPass123!

Upgrading

cd forge-deploy

# 1. Pull new images
docker compose pull

# 2. Recreate containers (migrations run automatically via forge-init)
docker compose up -d

# 3. Verify
docker compose ps
curl -k https://forge.example.com/api/v2/ping/

Scaling

Adding an execution node

# On the execution node:
docker run -d --name forge-task \
  -e DATABASE_HOST=db.example.com \
  -e REDIS_HOST=redis.example.com \
  -e FORGE_NODE_TYPE=execution \
  -e FORGE_NODE_NAME=exec-node-1 \
  ghcr.io/forgeplatform/forge-backend:latest launch_awx_task.sh

# On the control node:
docker compose exec forge-web forge-manage provision_instance --hostname=exec-node-1 --node-type=execution
docker compose exec forge-web forge-manage register_queue --queuename=default --hostnames=exec-node-1
Size CPU RAM Disk
Small (≤100 hosts) 4 8GB 50GB SSD
Medium (≤1000 hosts) 8 16GB 100GB SSD
Large (≤10000 hosts) 16 32GB 200GB SSD