# ATCR Production Deployment with Caddy # For UpCloud Rocky Linux deployment # # Usage: # 1. Copy .env.prod.template to .env and fill in your values # 2. docker compose -f deploy/docker-compose.prod.yml up -d # # Domains: # - atcr.io → AppView (registry API + web UI) # - hold01.atcr.io → Hold service (presigned URL generator) # - blobs.atcr.io → S3 object storage (CNAME to UpCloud S3) services: caddy: image: caddy:2-alpine container_name: atcr-caddy restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" # HTTP/3 environment: APPVIEW_DOMAIN: ${APPVIEW_DOMAIN:-atcr.io} HOLD_DOMAIN: ${HOLD_DOMAIN:-hold01.atcr.io} volumes: - caddy_data:/data - caddy_config:/config configs: - source: caddyfile target: /etc/caddy/Caddyfile networks: - atcr-network healthcheck: test: ["CMD", "caddy", "validate", "--config", "/etc/caddy/Caddyfile"] interval: 30s timeout: 10s retries: 3 start_period: 10s atcr-appview: build: context: .. dockerfile: Dockerfile.appview image: atcr-appview:latest container_name: atcr-appview restart: unless-stopped command: ["serve", "--config", "/config.yaml"] # Base config: config-appview.example.yaml # Env vars below override config file values for this deployment environment: ATCR_BASE_URL: https://${APPVIEW_DOMAIN:-atcr.io} ATCR_DEFAULT_HOLD_DID: ${ATCR_DEFAULT_HOLD_DID:-did:web:${HOLD_DOMAIN:-hold01.atcr.io}} ATCR_LOG_LEVEL: ${ATCR_LOG_LEVEL:-info} ATCR_LOG_FORMATTER: ${ATCR_LOG_FORMATTER:-text} volumes: - ./config-appview.yaml:/config.yaml:ro # Persistent data: auth keys, UI database, OAuth tokens, Jetstream cache - atcr-appview-data:/var/lib/atcr networks: - atcr-network healthcheck: test: ["CMD", "/healthcheck", "http://localhost:5000/health"] interval: 30s timeout: 10s retries: 3 start_period: 30s atcr-hold: build: context: .. dockerfile: Dockerfile.hold image: atcr-hold:latest container_name: atcr-hold restart: unless-stopped command: ["serve", "--config", "/config.yaml"] # Base config: config-hold.example.yaml # Env vars below override config file values for this deployment environment: HOLD_PUBLIC_URL: ${HOLD_PUBLIC_URL:-https://${HOLD_DOMAIN:-hold01.atcr.io}} HOLD_OWNER: ${HOLD_OWNER:-} HOLD_BLUESKY_POSTS_ENABLED: ${HOLD_BLUESKY_POSTS_ENABLED:-true} # S3/UpCloud Object Storage (REQUIRED) AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-} AWS_REGION: ${AWS_REGION:-us-east-1} S3_BUCKET: ${S3_BUCKET:-atcr-blobs} S3_ENDPOINT: ${S3_ENDPOINT:-} HOLD_LOG_LEVEL: ${ATCR_LOG_LEVEL:-info} volumes: - ./config-hold.yaml:/config.yaml:ro # PDS data (carstore SQLite + signing keys) - atcr-hold-data:/var/lib/atcr-hold - ./quotas.yaml:/quotas.yaml:ro networks: - atcr-network healthcheck: test: ["CMD", "/healthcheck", "http://localhost:8080/xrpc/_health"] interval: 30s timeout: 10s retries: 3 start_period: 30s networks: atcr-network: driver: bridge ipam: config: - subnet: 172.29.0.0/24 volumes: caddy_data: driver: local caddy_config: driver: local atcr-appview-data: driver: local atcr-hold-data: driver: local configs: caddyfile: content: | # ATCR AppView - Main registry + web UI ${APPVIEW_DOMAIN:-atcr.io} { # Reverse proxy to AppView container reverse_proxy atcr-appview:5000 { # Preserve original host header header_up Host {host} header_up X-Real-IP {remote_host} } # Enable compression encode gzip # Logging log { output file /data/logs/appview.log { roll_size 100mb roll_keep 10 } } } # ATCR Hold Service - Storage presigned URL generator ${HOLD_DOMAIN:-hold01.atcr.io} { # Reverse proxy to Hold service container reverse_proxy atcr-hold:8080 { # Preserve original host header header_up Host {host} header_up X-Real-IP {remote_host} } # Enable compression encode gzip # Logging log { output file /data/logs/hold.log { roll_size 100mb roll_keep 10 } } }