A community based topic aggregation platform built on atproto
1# Coves Production Stack 2# 3# Architecture: 4# - coves.social: AppView domain (API, frontend, .well-known/did.json) 5# - coves.me: PDS domain (must be separate from AppView) 6# 7# Hardware: AMD Epyc 7351p (16c/32t), 256GB RAM, 2x500GB NVMe RAID 8# 9# Usage: 10# docker-compose -f docker-compose.prod.yml up -d 11# 12# Prerequisites: 13# 1. DNS configured for both domains 14# 2. SSL certificates (Caddy handles this automatically) 15# 3. .env.prod file with secrets 16# 4. .well-known/did.json deployed to coves.social 17 18services: 19 # PostgreSQL Database for AppView 20 postgres: 21 image: postgres:15 22 container_name: coves-prod-postgres 23 restart: unless-stopped 24 environment: 25 POSTGRES_DB: ${POSTGRES_DB} 26 POSTGRES_USER: ${POSTGRES_USER} 27 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 28 volumes: 29 - postgres-data:/var/lib/postgresql/data 30 # Mount backup directory for pg_dump 31 - ./backups:/backups 32 networks: 33 - coves-internal 34 healthcheck: 35 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] 36 interval: 10s 37 timeout: 5s 38 retries: 5 39 # Generous limits for 256GB server 40 deploy: 41 resources: 42 limits: 43 memory: 32G 44 reservations: 45 memory: 4G 46 47 # Coves AppView (Go Server) 48 appview: 49 build: 50 context: . 51 dockerfile: Dockerfile 52 image: coves/appview:${VERSION:-latest} 53 container_name: coves-prod-appview 54 restart: unless-stopped 55 ports: 56 - "127.0.0.1:8080:8080" # Only expose to localhost (Caddy proxies) 57 environment: 58 # Database 59 DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable 60 61 # Instance identity 62 INSTANCE_DID: did:web:coves.social 63 INSTANCE_DOMAIN: coves.social 64 65 # PDS connection (separate domain!) 66 PDS_URL: https://coves.me 67 68 # Jetstream (Bluesky production firehose) 69 JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe 70 71 # Security - MUST be false in production 72 AUTH_SKIP_VERIFY: "false" 73 SKIP_DID_WEB_VERIFICATION: "false" 74 75 # OAuth (for community account provisioning) 76 OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID} 77 OAUTH_REDIRECT_URI: ${OAUTH_REDIRECT_URI} 78 OAUTH_PRIVATE_JWK: ${OAUTH_PRIVATE_JWK} 79 80 # Application settings 81 PORT: 8080 82 ENV: production 83 LOG_LEVEL: info 84 85 # Encryption key for community credentials 86 ENCRYPTION_KEY: ${ENCRYPTION_KEY} 87 networks: 88 - coves-internal 89 depends_on: 90 postgres: 91 condition: service_healthy 92 healthcheck: 93 test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/xrpc/_health"] 94 interval: 30s 95 timeout: 5s 96 retries: 3 97 start_period: 10s 98 # Go is memory-efficient, but give it room for connection pools 99 deploy: 100 resources: 101 limits: 102 memory: 8G 103 reservations: 104 memory: 512M 105 106 # Bluesky PDS (Personal Data Server) 107 # Handles community accounts and their repositories 108 pds: 109 image: ghcr.io/bluesky-social/pds:latest 110 container_name: coves-prod-pds 111 restart: unless-stopped 112 ports: 113 - "127.0.0.1:3000:3000" # Only expose to localhost (Caddy proxies) 114 environment: 115 # PDS identity 116 PDS_HOSTNAME: coves.me 117 PDS_PORT: 3000 118 PDS_DATA_DIRECTORY: /pds 119 PDS_BLOBSTORE_DISK_LOCATION: /pds/blocks 120 121 # PLC Directory (production) 122 PDS_DID_PLC_URL: https://plc.directory 123 124 # Handle domains 125 # Community handles use @community.coves.social (AppView domain) 126 PDS_SERVICE_HANDLE_DOMAINS: .coves.social 127 128 # Security (set real values in .env.prod) 129 PDS_JWT_SECRET: ${PDS_JWT_SECRET} 130 PDS_ADMIN_PASSWORD: ${PDS_ADMIN_PASSWORD} 131 PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ${PDS_ROTATION_KEY} 132 133 # Email (optional, for account recovery) 134 PDS_EMAIL_SMTP_URL: ${PDS_EMAIL_SMTP_URL:-} 135 PDS_EMAIL_FROM_ADDRESS: ${PDS_EMAIL_FROM_ADDRESS:-noreply@coves.me} 136 137 # Production mode 138 PDS_DEV_MODE: "false" 139 PDS_INVITE_REQUIRED: "false" # Set to true if you want invite-only 140 141 # Logging 142 NODE_ENV: production 143 LOG_ENABLED: "true" 144 LOG_LEVEL: info 145 volumes: 146 - pds-data:/pds 147 networks: 148 - coves-internal 149 healthcheck: 150 test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/xrpc/_health"] 151 interval: 30s 152 timeout: 5s 153 retries: 5 154 # PDS (Node.js) needs memory for blob handling 155 deploy: 156 resources: 157 limits: 158 memory: 16G 159 reservations: 160 memory: 1G 161 162 # Caddy Reverse Proxy 163 # Handles HTTPS automatically via Let's Encrypt 164 # Uses Cloudflare plugin for wildcard SSL certificates (*.coves.social) 165 caddy: 166 # Pre-built Caddy with Cloudflare DNS plugin 167 # Updates automatically with docker-compose pull 168 # Alternative: build your own with Dockerfile.caddy 169 image: ghcr.io/slothcroissant/caddy-cloudflaredns:latest 170 container_name: coves-prod-caddy 171 restart: unless-stopped 172 ports: 173 - "80:80" 174 - "443:443" 175 environment: 176 # Required for wildcard SSL via DNS challenge 177 # Create at: Cloudflare Dashboard → My Profile → API Tokens → Create Token 178 # Permissions: Zone:DNS:Edit for coves.social zone 179 CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN} 180 volumes: 181 - ./Caddyfile:/etc/caddy/Caddyfile:ro 182 - caddy-data:/data 183 - caddy-config:/config 184 # Static files (.well-known, client-metadata.json, oauth callback) 185 - ./static:/srv:ro 186 networks: 187 - coves-internal 188 depends_on: 189 - appview 190 - pds 191 192networks: 193 coves-internal: 194 driver: bridge 195 name: coves-prod-network 196 197volumes: 198 postgres-data: 199 name: coves-prod-postgres-data 200 pds-data: 201 name: coves-prod-pds-data 202 caddy-data: 203 name: coves-prod-caddy-data 204 caddy-config: 205 name: coves-prod-caddy-config