--- title: Docker Deployment description: Deploy Plunk with Docker Compose --- ## Prerequisites - Docker installed - Docker Compose installed - Git installed ## Quick Start ### 1. Clone Repository ```bash git clone https://github.com/useplunk/plunk.git cd plunk ``` ### 2. Copy Environment File ```bash cp .env.self-host.example .env ``` ### 3. Configure Environment Edit `.env` and configure required variables: ```bash # Database Password (PostgreSQL) DB_PASSWORD="changeme123" # JWT Secret (generate with: openssl rand -base64 32) JWT_SECRET="your-secret-here" # Domains (for subdomain-based routing) API_DOMAIN="api.localhost" DASHBOARD_DOMAIN="app.localhost" LANDING_DOMAIN="www.localhost" WIKI_DOMAIN="docs.localhost" # Set to 'true' for HTTPS in production USE_HTTPS="false" # AWS SES (for email sending) AWS_SES_REGION="us-east-1" AWS_SES_ACCESS_KEY_ID="your-access-key" AWS_SES_SECRET_ACCESS_KEY="your-secret-key" SES_CONFIGURATION_SET="plunk-configuration-set" # S3-compatible storage (Minio is included by default) # Leave defaults unless using external S3 S3_ENDPOINT="http://minio:9000" S3_ACCESS_KEY_ID="plunk" S3_ACCESS_KEY_SECRET="plunkminiopass" S3_BUCKET="uploads" S3_PUBLIC_URL="http://localhost:9000/uploads" S3_FORCE_PATH_STYLE="true" ``` ### 4. Start Services ```bash docker compose up -d ``` This starts: - PostgreSQL database - Redis - Minio (S3-compatible storage) - Plunk application (all services in one container with nginx) - API server - Worker process - Web dashboard - Landing page - Documentation ### 5. Access Services The services are available at the configured domains: - **Dashboard**: `http://app.localhost` (or your configured domain) - **API**: `http://api.localhost` - **Landing**: `http://www.localhost` - **Docs**: `http://docs.localhost` - **Minio Console**: `http://localhost:9001` Create your first account and start sending emails! ## Docker Compose Configuration The `docker-compose.yml` file uses the pre-built Plunk image from GitHub Container Registry: ```yaml version: '3.8' 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 networks: - plunk redis: image: redis:7-alpine container_name: plunk-redis restart: unless-stopped command: redis-server --appendonly yes volumes: - redis_data:/data 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: - "9000:9000" # API - "9001:9001" # Console networks: - plunk plunk: image: ghcr.io/useplunk/plunk:latest container_name: plunk restart: unless-stopped environment: SERVICE: all # Runs all services (API, Worker, Web, Landing, Wiki) DATABASE_URL: postgresql://plunk:${DB_PASSWORD}@postgres:5432/plunk REDIS_URL: redis://redis:6379 JWT_SECRET: ${JWT_SECRET} # Domain configuration for subdomain routing API_DOMAIN: ${API_DOMAIN:-api.localhost} DASHBOARD_DOMAIN: ${DASHBOARD_DOMAIN:-app.localhost} LANDING_DOMAIN: ${LANDING_DOMAIN:-www.localhost} WIKI_DOMAIN: ${WIKI_DOMAIN:-docs.localhost} USE_HTTPS: ${USE_HTTPS:-false} # AWS SES 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} # S3/Minio storage 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} ports: - "465:465" # SMTP (implicit TLS) - "587:587" # SMTP (STARTTLS) depends_on: - postgres - redis - minio networks: - plunk volumes: postgres_data: redis_data: minio_data: plunk_data: networks: plunk: driver: bridge ``` **Note**: The Plunk image contains all applications (API, Worker, Web, Landing, Wiki) and uses nginx for subdomain-based routing. ## Production Deployment ### Using Pre-built Image The easiest way to deploy is using the pre-built image from GitHub Container Registry: ```bash docker pull ghcr.io/useplunk/plunk:latest docker compose up -d ``` ### Building Your Own Image If you want to build from source: ```bash docker build -t plunk:custom . ``` Then update `docker-compose.yml` to use your custom image: ```yaml plunk: image: plunk:custom # ... rest of configuration ``` ### Security Hardening 1. **Use strong passwords** ```bash DB_PASSWORD=$(openssl rand -base64 32) JWT_SECRET=$(openssl rand -base64 32) ``` 2. **Use HTTPS** - Set `USE_HTTPS=true` in your `.env` - Set up reverse proxy (Traefik, Caddy, or nginx) - Configure SSL certificates (Let's Encrypt) 3. **Configure domains** ```bash API_DOMAIN=api.yourdomain.com DASHBOARD_DOMAIN=app.yourdomain.com LANDING_DOMAIN=www.yourdomain.com WIKI_DOMAIN=docs.yourdomain.com USE_HTTPS=true ``` 4. **Restrict network access** - Don't expose database and Redis ports publicly - Use internal Docker networks - Only expose port 80/443 (via reverse proxy) and SMTP ports 5. **Regular backups** - Database backups (automated via cron) - Minio data backups ### Scaling The Plunk image runs all services in a single container by default. For higher scale: #### Separate Services You can run services separately by setting the `SERVICE` environment variable: ```yaml # API only plunk-api: image: ghcr.io/useplunk/plunk:latest environment: SERVICE: api # ... configuration # Worker only plunk-worker: image: ghcr.io/useplunk/plunk:latest environment: SERVICE: worker # ... configuration # Web only plunk-web: image: ghcr.io/useplunk/plunk:latest environment: SERVICE: web # ... configuration ``` #### Scale Workers For higher email throughput, run multiple worker containers: ```bash docker compose up -d --scale plunk-worker=3 ``` #### External Services For production at scale, use managed services: - Managed PostgreSQL (AWS RDS, DigitalOcean, Supabase) - Managed Redis (AWS ElastiCache, Redis Cloud, Upstash) - AWS S3 (instead of Minio) ## Maintenance ### View Logs ```bash # All services docker compose logs -f # Specific service docker compose logs -f plunk docker compose logs -f postgres ``` ### Restart Services ```bash docker compose restart plunk ``` ### Update Plunk Pull the latest image and restart: ```bash docker compose pull plunk docker compose up -d plunk ``` If you built from source: ```bash git pull docker build -t plunk:custom . docker compose up -d ``` ### Backup Database ```bash docker compose exec postgres pg_dump -U plunk plunk > backup-$(date +%Y%m%d).sql ``` ### Restore Database ```bash docker compose exec -T postgres psql -U plunk plunk < backup.sql ``` ### Backup Minio Data ```bash docker compose exec minio mc alias set local http://localhost:9000 plunk plunkminiopass docker compose exec minio mc mirror local/uploads /backups/minio ``` ## Troubleshooting ### Services won't start Check logs: ```bash docker compose logs plunk ``` ### Database connection errors Ensure DATABASE_URL is correct and database is running: ```bash docker compose ps postgres docker compose exec postgres psql -U plunk -d plunk -c "SELECT 1;" ``` ### Worker not processing jobs Check Plunk container logs for worker output: ```bash docker compose logs plunk | grep worker ``` Verify Redis connection: ```bash docker compose exec redis redis-cli PING ``` ### Cannot access services Check that your domains resolve correctly: ```bash # For local development with *.localhost domains, these should work automatically # For production domains, ensure DNS is configured correctly curl http://api.localhost curl http://app.localhost ``` ### Minio not accessible Check Minio is running: ```bash docker compose ps minio docker compose logs minio ``` Access Minio console at `http://localhost:9001` with credentials from `.env`. ## Next Steps - [Configure environment variables](/self-hosting/environment-variables) - [Set up email delivery](/self-hosting/email-setup) - [Database setup and migrations](/self-hosting/database-setup)