version: '3.8' # Plunk Self-Hosting Docker Compose # This setup runs all Plunk services in a single container with nginx reverse proxy # # Uses subdomain-based routing: # - api.example.com -> API Server # - app.example.com -> Web Dashboard # - www.example.com -> Landing Page # - docs.example.com -> Documentation # - smtp.example.com -> SMTP Relay Server (ports 465, 587) # # Usage: # 1. Copy .env.self-host.example to .env and configure # 2. Set your domain names (API_DOMAIN, DASHBOARD_DOMAIN, SMTP_DOMAIN, etc.) # 3. Run: docker compose up -d # 4. Access your instance at the configured domains # # SMTP Server: # - Supports TLS certificates via: # 1. Traefik acme.json (mount to /certs/acme.json, requires SMTP_DOMAIN) # 2. PEM files (mount privkey.pem and fullchain.pem to /certs/) # - Runs without TLS if no certificates are mounted services: # ============================================ # Infrastructure Services # ============================================ postgres: image: postgres:16-alpine container_name: plunk-postgres restart: unless-stopped environment: POSTGRES_DB: plunk POSTGRES_USER: plunk POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme123} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: [ "CMD-SHELL", "pg_isready -U plunk" ] interval: 10s timeout: 5s retries: 5 networks: - plunk redis: image: redis:7-alpine container_name: plunk-redis restart: unless-stopped command: redis-server --appendonly yes volumes: - redis_data:/data healthcheck: test: [ "CMD", "redis-cli", "ping" ] interval: 10s timeout: 5s retries: 5 networks: - plunk minio: image: minio/minio:latest container_name: plunk-minio restart: unless-stopped command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-plunk} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-plunkminiopass} volumes: - minio_data:/data ports: # Expose Minio API (for S3 operations) and Console (for web UI) - "${MINIO_API_PORT:-9000}:9000" - "${MINIO_CONSOLE_PORT:-9001}:9001" healthcheck: test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ] interval: 30s timeout: 20s retries: 3 networks: - plunk ntfy: image: binwiederhier/ntfy:latest container_name: plunk-ntfy restart: unless-stopped command: serve environment: - TZ=UTC volumes: - ntfy_cache:/var/cache/ntfy - ntfy_etc:/etc/ntfy ports: # Expose ntfy web UI and API - "${NTFY_PORT:-8080}:80" healthcheck: test: [ "CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1" ] interval: 30s timeout: 10s retries: 3 start_period: 10s networks: - plunk # ============================================ # Plunk Application # ============================================ plunk: image: ghcr.io/useplunk/plunk:latest container_name: plunk restart: unless-stopped environment: # Service mode SERVICE: all NODE_ENV: production # Database DATABASE_URL: postgresql://plunk:${DB_PASSWORD:-changeme123}@postgres:5432/plunk DIRECT_DATABASE_URL: postgresql://plunk:${DB_PASSWORD:-changeme123}@postgres:5432/plunk # Redis REDIS_URL: redis://redis:6379 # Security JWT_SECRET: ${JWT_SECRET} # Nginx configuration NGINX_PORT: ${NGINX_PORT:-80} # Domain configuration (subdomain-based routing) # Each service gets its own subdomain API_DOMAIN: ${API_DOMAIN:-api.localhost} DASHBOARD_DOMAIN: ${DASHBOARD_DOMAIN:-app.localhost} LANDING_DOMAIN: ${LANDING_DOMAIN:-www.localhost} WIKI_DOMAIN: ${WIKI_DOMAIN:-docs.localhost} # Protocol configuration (set to true for HTTPS in production) USE_HTTPS: ${USE_HTTPS:-false} # Application URLs (auto-configured by setup-nginx.sh from domains above) # You can override these if needed API_URI: ${API_URI:-} DASHBOARD_URI: ${DASHBOARD_URI:-} LANDING_URI: ${LANDING_URI:-} WIKI_URI: ${WIKI_URI:-} # AWS SES (for sending emails) AWS_SES_REGION: ${AWS_SES_REGION} AWS_SES_ACCESS_KEY_ID: ${AWS_SES_ACCESS_KEY_ID} AWS_SES_SECRET_ACCESS_KEY: ${AWS_SES_SECRET_ACCESS_KEY} SES_CONFIGURATION_SET: ${SES_CONFIGURATION_SET} SES_CONFIGURATION_SET_NO_TRACKING: ${SES_CONFIGURATION_SET_NO_TRACKING:-} # Optional: OAuth GITHUB_OAUTH_CLIENT: ${GITHUB_OAUTH_CLIENT:-} GITHUB_OAUTH_SECRET: ${GITHUB_OAUTH_SECRET:-} GOOGLE_OAUTH_CLIENT: ${GOOGLE_OAUTH_CLIENT:-} GOOGLE_OAUTH_SECRET: ${GOOGLE_OAUTH_SECRET:-} # Optional: Stripe STRIPE_SK: ${STRIPE_SK:-} STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-} STRIPE_PRICE_ONBOARDING: ${STRIPE_PRICE_ONBOARDING:-} STRIPE_PRICE_EMAIL_USAGE: ${STRIPE_PRICE_EMAIL_USAGE:-} STRIPE_METER_EVENT_NAME: ${STRIPE_METER_EVENT_NAME:-emails} # S3-compatible storage (Minio) S3_ENDPOINT: ${S3_ENDPOINT:-http://minio:9000} S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-plunk} S3_ACCESS_KEY_SECRET: ${S3_ACCESS_KEY_SECRET:-plunkminiopass} S3_BUCKET: ${S3_BUCKET:-uploads} S3_PUBLIC_URL: ${S3_PUBLIC_URL:-http://localhost:9000/uploads} S3_FORCE_PATH_STYLE: ${S3_FORCE_PATH_STYLE:-true} # Notifications (ntfy.sh) NTFY_URL: ${NTFY_URL:-http://ntfy/plunk-notifications} # SMTP Server (for sending emails via SMTP) # SMTP_DOMAIN is required to select the correct certificate from Traefik acme.json # If using PEM files, SMTP_DOMAIN is optional SMTP_DOMAIN: ${SMTP_DOMAIN:-} volumes: # Persistent storage for application data - plunk_data:/app/data # Optional: Mount certificates for SMTP TLS # For Traefik acme.json: # - /path/to/acme.json:/certs/acme.json:ro # For PEM files: # - /path/to/privkey.pem:/certs/privkey.pem:ro # - /path/to/fullchain.pem:/certs/fullchain.pem:ro ports: # SMTP ports (for email relay) - "465:465" # SMTPS (implicit TLS) - "587:587" # SMTP Submission (STARTTLS) # Optional: Expose individual service ports for debugging # - "8080:8080" # API # - "3000:3000" # Web # - "4000:4000" # Landing # - "1000:1000" # Wiki depends_on: postgres: condition: service_healthy redis: condition: service_healthy minio: condition: service_healthy ntfy: condition: service_healthy healthcheck: # Check nginx is running and responding test: [ "CMD", "curl", "-f", "http://localhost:80/" ] interval: 30s timeout: 10s retries: 3 start_period: 60s networks: - plunk volumes: postgres_data: driver: local redis_data: driver: local minio_data: driver: local plunk_data: driver: local ntfy_cache: driver: local ntfy_etc: driver: local networks: plunk: driver: bridge