# Mastodon using Docker named volumes. All runtime env vars are passed through (no env_file). # Komodo / your orchestration should inject the environment variables listed below into each container. services: db: image: postgres:14-alpine restart: unless-stopped environment: POSTGRES_DB: mastodon_production POSTGRES_USER: mastodon # Komodo must provide DB_PASSWORD in the environment for this service POSTGRES_PASSWORD: "${DB_PASSWORD}" volumes: - db-data:/var/lib/postgresql/data redis: image: redis:6-alpine restart: unless-stopped command: ["redis-server", "--appendonly", "yes"] volumes: - redis-data:/data init: image: ghcr.io/mastodon/mastodon:latest depends_on: - db - redis restart: "no" volumes: - public-system:/mastodon/public/system - public-assets:/mastodon/public/assets - public-packs:/mastodon/public/packs - mastodon-log:/mastodon/log environment: - RAILS_ENV=production - LOCAL_DOMAIN=${LOCAL_DOMAIN} - LOCAL_HTTPS=${LOCAL_HTTPS} - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASS=${DB_PASS} - DB_PASSWORD=${DB_PASSWORD} - REDIS_URL=${REDIS_URL} - SECRET_KEY_BASE=${SECRET_KEY_BASE} - OTP_SECRET=${OTP_SECRET} - VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY} - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY} - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY} - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY} - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT} - SMTP_SERVER=${SMTP_SERVER} - SMTP_PORT=${SMTP_PORT} - SMTP_LOGIN=${SMTP_LOGIN} - SMTP_PASSWORD=${SMTP_PASSWORD} - SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS} - STREAMING_ENABLED=${STREAMING_ENABLED} - RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} command: > bash -lc " set -euo pipefail echo '== Mastodon init job starting' # 1) Verify ActiveRecord encryption keys. If missing, generate and print them and exit so operator can set them. if [ -z \"${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY:-}\" ] || [ -z \"${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY:-}\" ] || [ -z \"${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT:-}\" ]; then echo 'ActiveRecord encryption keys are NOT set. Running bin/rails db:encryption:init to generate keys...' bin/rails db:encryption:init || true echo '=======================================================' echo 'The above command generated the ACTIVE_RECORD encryption keys. Copy them into Komodo (use these exact env names):' echo ' ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY' echo ' ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY' echo ' ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT' echo '' echo 'After adding those to Komodo, re-run this init job (docker-compose run --rm --no-deps init).' echo 'Exiting with code 1 to ensure you capture and persist the keys in your secret store.' exit 1 fi echo 'ActiveRecord encryption keys present. Continuing initialization...' # 2) Wait for DB to accept connections (retry loop) echo 'Waiting for Postgres to be ready...' attempt=0 until bundle exec rails db:version >/dev/null 2>&1; do attempt=$((attempt+1)) if [ \"$attempt\" -gt 60 ]; then echo 'Timed out waiting for Postgres (60 attempts). Check DB connectivity and env vars.' >&2 exit 2 fi echo \"Postgres not ready yet (attempt $attempt). Sleeping 2s...\" sleep 2 done echo 'Postgres is ready.' # 3) Prepare DB (create/migrate as needed) echo 'Running rails db:prepare (create DB / migrate if needed)...' bundle exec rails db:prepare # 4) Generate VAPID keys if not provided if [ -z \"${VAPID_PUBLIC_KEY:-}\" ] || [ -z \"${VAPID_PRIVATE_KEY:-}\" ]; then echo 'VAPID keys (VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY) are missing. Generating...' bundle exec rake mastodon:webpush:generate_vapid_key || true echo '=======================================================' echo 'If VAPID keys were printed above, copy them into Komodo as VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY and re-run this init job (or continue to start services if you accept missing VAPID keys).' else echo 'VAPID keys present.' fi # 5) Install JS deps and precompile assets echo 'Installing javascript dependencies (yarn)...' if command -v yarn >/dev/null 2>&1; then yarn install --check-files --production=false else echo 'yarn not found in image; skipping yarn install (ensure assets are available in the image or build them externally).' fi echo 'Precompiling rails assets...' RAILS_ENV=production bundle exec rails assets:precompile echo 'Init job complete. You can now start web/sidekiq/streaming services.' " web: image: ghcr.io/mastodon/mastodon:latest depends_on: - db - redis restart: unless-stopped volumes: - public-system:/mastodon/public/system - public-assets:/mastodon/public/assets - public-packs:/mastodon/public/packs - mastodon-log:/mastodon/log ports: - "3000:3000" environment: - RAILS_ENV=production - LOCAL_DOMAIN=${LOCAL_DOMAIN} - LOCAL_HTTPS=${LOCAL_HTTPS} - PORT=${PORT} - STREAMING_PORT=${STREAMING_PORT} - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASS=${DB_PASS} - DB_PASSWORD=${DB_PASSWORD} - REDIS_URL=${REDIS_URL} - SECRET_KEY_BASE=${SECRET_KEY_BASE} - OTP_SECRET=${OTP_SECRET} - VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY} - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY} - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY} - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY} - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT} - SMTP_SERVER=${SMTP_SERVER} - SMTP_PORT=${SMTP_PORT} - SMTP_LOGIN=${SMTP_LOGIN} - SMTP_PASSWORD=${SMTP_PASSWORD} - SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS} - STREAMING_ENABLED=${STREAMING_ENABLED} - RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} command: bash -lc "RAILS_ENV=production bundle exec puma -C config/puma.rb" sidekiq: image: ghcr.io/mastodon/mastodon:latest depends_on: - db - redis restart: unless-stopped volumes: - public-system:/mastodon/public/system - mastodon-log:/mastodon/log environment: - RAILS_ENV=production - LOCAL_DOMAIN=${LOCAL_DOMAIN} - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASS=${DB_PASS} - DB_PASSWORD=${DB_PASSWORD} - REDIS_URL=${REDIS_URL} - SECRET_KEY_BASE=${SECRET_KEY_BASE} - VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY} - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY} - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY} - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY} - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT} - SMTP_SERVER=${SMTP_SERVER} - SMTP_PORT=${SMTP_PORT} - SMTP_LOGIN=${SMTP_LOGIN} - SMTP_PASSWORD=${SMTP_PASSWORD} - SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS} command: bash -lc "RAILS_ENV=production bundle exec sidekiq" streaming: image: ghcr.io/mastodon/mastodon:latest depends_on: - redis restart: unless-stopped volumes: - mastodon-log:/mastodon/log ports: - "4000:4000" environment: - RAILS_ENV=production - LOCAL_DOMAIN=${LOCAL_DOMAIN} - PORT=${STREAMING_PORT} - REDIS_URL=${REDIS_URL} - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY} - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY} - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT} - STREAMING_ENABLED=${STREAMING_ENABLED} command: bash -lc "NODE_ENV=production ./bin/streaming" volumes: db-data: redis-data: public-system: public-assets: public-packs: mastodon-log: