Skip to main content

Docker Deployment

Deploy Boards using Docker Compose with pre-built container images from GitHub Container Registry (GHCR) or Docker Hub.

Architecture Overview

A Boards deployment consists of four services:

ServiceImagePurpose
apighcr.io/weirdfingers/boards-backendGraphQL API server (uvicorn)
workerghcr.io/weirdfingers/boards-backendBackground job processor
dbpostgres:16PostgreSQL database
cacheredis:7Job queue and caching

The API and worker use the same image but run different commands. The backend image is published to both GHCR (recommended) and Docker Hub:

  • GHCR: ghcr.io/weirdfingers/boards-backend:latest
  • Docker Hub: cdiddy/weirdfingers-boards-backend:latest
Frontend Image

The frontend requires a custom build since it needs your environment-specific configuration baked in at build time. See Frontend Deployment for details.

Quick Start

1. Create Project Directory

mkdir my-boards-app && cd my-boards-app

2. Create Docker Compose File

Create compose.yaml:

name: boards

services:
db:
image: postgres:16
environment:
POSTGRES_USER: boards
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
POSTGRES_DB: boards
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U boards -d boards"]
interval: 5s
timeout: 5s
retries: 20
networks:
- internal

cache:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
- cache-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 20
networks:
- internal

api:
image: ghcr.io/weirdfingers/boards-backend:${BACKEND_VERSION:-latest}
command: ["uvicorn", "boards.api.app:app", "--host", "0.0.0.0", "--port", "8800"]
environment:
- BOARDS_DATABASE_URL=postgresql://boards:${POSTGRES_PASSWORD:-changeme}@db:5432/boards
- BOARDS_REDIS_URL=redis://cache:6379/0
- BOARDS_GENERATORS_CONFIG_PATH=/app/config/generators.yaml
- BOARDS_STORAGE_CONFIG_PATH=/app/config/storage_config.yaml
- PYTHONUNBUFFERED=1
env_file:
- .env
volumes:
- ./config:/app/config:ro
- ./data/storage:/app/data/storage
- ./extensions:/app/extensions:ro
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
ports:
- "${API_PORT:-8800}:8800"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8800/health"]
interval: 30s
timeout: 3s
retries: 50
networks:
- internal

worker:
image: ghcr.io/weirdfingers/boards-backend:${BACKEND_VERSION:-latest}
command: ["boards-worker", "--log-level", "info", "--processes", "1", "--threads", "1"]
environment:
- BOARDS_DATABASE_URL=postgresql://boards:${POSTGRES_PASSWORD:-changeme}@db:5432/boards
- BOARDS_REDIS_URL=redis://cache:6379/0
- BOARDS_GENERATORS_CONFIG_PATH=/app/config/generators.yaml
- BOARDS_STORAGE_CONFIG_PATH=/app/config/storage_config.yaml
- BOARDS_INTERNAL_API_URL=http://api:8800
- PYTHONUNBUFFERED=1
env_file:
- .env
volumes:
- ./config:/app/config:ro
- ./data/storage:/app/data/storage
- ./extensions:/app/extensions:ro
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
networks:
- internal

networks:
internal:
driver: bridge

volumes:
db-data:
cache-data:

3. Create Environment File

Create .env:

# Database
POSTGRES_PASSWORD=your-secure-password-here

# Image version (optional, defaults to latest)
BACKEND_VERSION=latest

# API port (optional, defaults to 8800)
API_PORT=8800

# Auth provider (see Authentication docs)
BOARDS_AUTH_PROVIDER=none

# Generator API keys (JSON format)
BOARDS_GENERATOR_API_KEYS={"fal": "your-fal-key", "openai": "your-openai-key"}

4. Create Configuration Files

Create the config directory and files:

mkdir -p config data/storage extensions

Create config/generators.yaml:

generators:
- class: boards.generators.fal.flux.FluxProGenerator
enabled: true
- class: boards.generators.openai.dalle.DallE3Generator
enabled: true

Create config/storage_config.yaml:

default_provider: local

providers:
local:
type: local
base_path: /app/data/storage
public_url_base: http://localhost:8800/storage

5. Start Services

docker compose up -d

Verify services are running:

docker compose ps
docker compose logs -f api

The API will be available at http://localhost:8800.

Production Configuration

Using Managed Services

For production, consider using managed database and cache services:

services:
api:
image: ghcr.io/weirdfingers/boards-backend:${BACKEND_VERSION:-latest}
environment:
# Use managed PostgreSQL (see Database docs)
- BOARDS_DATABASE_URL=postgresql://user:pass@your-managed-db.com:5432/boards?sslmode=require
# Use managed Redis (e.g., ElastiCache, Upstash)
- BOARDS_REDIS_URL=rediss://user:pass@your-managed-redis.com:6379/0
# ... rest of config

See the Database documentation for provider-specific guides.

Scaling Workers

Scale workers to handle more concurrent jobs:

# Scale to 3 worker instances
docker compose up -d --scale worker=3

Or configure in compose.yaml:

services:
worker:
# ... existing config
deploy:
replicas: 3

External Storage

For production, use cloud storage instead of local:

# config/storage_config.yaml
default_provider: s3

providers:
s3:
type: s3
bucket: my-boards-bucket
region: us-east-1
# Credentials from environment: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY

See Storage Configuration for all providers.

Reverse Proxy

Place a reverse proxy (nginx, Caddy, Traefik) in front for SSL termination:

services:
proxy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy-data:/data
depends_on:
- api
networks:
- internal

volumes:
caddy-data:

Example Caddyfile:

boards.example.com {
reverse_proxy api:8800
}

Common Operations

View Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f api
docker compose logs -f worker

Restart Services

# Restart all
docker compose restart

# Restart specific service
docker compose restart api

Update Images

# Pull latest images
docker compose pull

# Recreate containers with new images
docker compose up -d

Database Migrations

Migrations run automatically on API startup. To run manually:

docker compose exec api python -m boards.db.migrate

Backup Database

docker compose exec db pg_dump -U boards boards > backup.sql

Restore Database

docker compose exec -T db psql -U boards boards < backup.sql

Troubleshooting

API Won't Start

Check health of dependencies:

docker compose ps
docker compose logs db
docker compose logs cache

Verify database connection:

docker compose exec api python -c "from boards.db import engine; print(engine.url)"

Worker Not Processing Jobs

Check worker logs:

docker compose logs -f worker

Verify Redis connection:

docker compose exec cache redis-cli ping

Out of Disk Space

Clean up old images and volumes:

docker system prune -a
docker volume prune

Next Steps