Skip to content

Stateful Docker Compose

This Docker Compose configuration runs all services including PostgreSQL, Redis, and MinIO as containers. This is the recommended setup for:

  • VPS deployments (Digital Ocean, Linode, Hetzner)
  • Local installations
  • One-click deployments (Coolify, Dokploy)

Use this configuration when:

  • You want a simple, all-in-one deployment
  • You don’t have access to managed databases or external storage
  • You want to minimize external dependencies
  • You’re deploying on a single VPS or local machine

For production environments with high availability requirements, consider using the Stateless Docker Compose configuration with managed databases instead.

# Default Docker Compose - Builds images locally
# Usage: docker compose up -d
#
# This is the recommended way to run Isekai for self-hosting.
# Includes MinIO for S3-compatible storage out of the box.
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: isekai
POSTGRES_PASSWORD: isekai
POSTGRES_DB: isekai
ports:
- '5433:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U isekai']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
minio:
image: minio/minio:latest
container_name: isekai-minio
command: server /data --console-address ":9001"
ports:
- '9000:9000' # S3 API
- '9001:9001' # Web Console
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- minio_data:/data
healthcheck:
test: ['CMD', 'mc', 'ready', 'local']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Creates the default bucket on startup
minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:9000 minioadmin minioadmin;
mc mb myminio/isekai-uploads --ignore-existing;
mc anonymous set download myminio/isekai-uploads;
exit 0;
"
redis:
image: redis:7-alpine
command: redis-server --appendonly yes --appendfsync everysec
ports:
- '6379:6379'
volumes:
- redis_data:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
backend:
build:
context: .
dockerfile: apps/isekai-backend/Dockerfile
ports:
- '4000:4000'
environment:
# Database & Cache
DATABASE_URL: postgresql://isekai:isekai@postgres:5432/isekai
REDIS_URL: redis://redis:6379
# URLs
FRONTEND_URL: http://localhost:3000
PORT: 4000
NODE_ENV: production
# Security (CHANGE THESE!)
SESSION_SECRET: ${SESSION_SECRET}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
# DeviantArt OAuth (REQUIRED - Create your own app at deviantart.com/developers)
DEVIANTART_CLIENT_ID: ${DEVIANTART_CLIENT_ID}
DEVIANTART_CLIENT_SECRET: ${DEVIANTART_CLIENT_SECRET}
DEVIANTART_REDIRECT_URI: ${DEVIANTART_REDIRECT_URI:-http://localhost:4000/api/auth/deviantart/callback}
# S3-Compatible Storage (defaults to local MinIO)
S3_ENDPOINT: ${S3_ENDPOINT:-http://minio:9000}
S3_REGION: ${S3_REGION:-us-east-1}
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-minioadmin}
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-minioadmin}
S3_BUCKET_NAME: ${S3_BUCKET_NAME:-isekai-uploads}
S3_PUBLIC_URL: ${S3_PUBLIC_URL:-http://localhost:9000/isekai-uploads}
S3_FORCE_PATH_STYLE: ${S3_FORCE_PATH_STYLE:-true}
env_file:
- .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
minio:
condition: service_healthy
restart: unless-stopped
publisher:
build:
context: .
dockerfile: apps/isekai-publisher/Dockerfile
ports:
- '8000:8000' # Health check endpoint
environment:
# Database & Cache
DATABASE_URL: postgresql://isekai:isekai@postgres:5432/isekai
REDIS_URL: redis://redis:6379
NODE_ENV: production
# DeviantArt OAuth (same credentials as backend)
DEVIANTART_CLIENT_ID: ${DEVIANTART_CLIENT_ID}
DEVIANTART_CLIENT_SECRET: ${DEVIANTART_CLIENT_SECRET}
# S3-Compatible Storage (defaults to local MinIO)
S3_ENDPOINT: ${S3_ENDPOINT:-http://minio:9000}
S3_REGION: ${S3_REGION:-us-east-1}
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-minioadmin}
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-minioadmin}
S3_BUCKET_NAME: ${S3_BUCKET_NAME:-isekai-uploads}
S3_PUBLIC_URL: ${S3_PUBLIC_URL:-http://localhost:9000/isekai-uploads}
S3_FORCE_PATH_STYLE: ${S3_FORCE_PATH_STYLE:-true}
# Publisher Configuration
PUBLISHER_CONCURRENCY: ${PUBLISHER_CONCURRENCY:-5}
PUBLISHER_MAX_ATTEMPTS: ${PUBLISHER_MAX_ATTEMPTS:-7}
PUBLISHER_JOB_TIMEOUT_MS: ${PUBLISHER_JOB_TIMEOUT_MS:-600000}
PUBLISHER_STALE_CHECK_INTERVAL_MS: ${PUBLISHER_STALE_CHECK_INTERVAL_MS:-60000}
PUBLISHER_MAX_STALLED_COUNT: ${PUBLISHER_MAX_STALLED_COUNT:-2}
# Rate Limiter
RATE_LIMITER_ENABLED: ${RATE_LIMITER_ENABLED:-true}
RATE_LIMITER_BASE_DELAY_MS: ${RATE_LIMITER_BASE_DELAY_MS:-3000}
RATE_LIMITER_MAX_DELAY_MS: ${RATE_LIMITER_MAX_DELAY_MS:-300000}
RATE_LIMITER_JITTER_PERCENT: ${RATE_LIMITER_JITTER_PERCENT:-20}
RATE_LIMITER_SUCCESS_DECREASE_FACTOR: ${RATE_LIMITER_SUCCESS_DECREASE_FACTOR:-0.9}
RATE_LIMITER_FAILURE_INCREASE_FACTOR: ${RATE_LIMITER_FAILURE_INCREASE_FACTOR:-2.0}
# Circuit Breaker
CIRCUIT_BREAKER_ENABLED: ${CIRCUIT_BREAKER_ENABLED:-true}
CIRCUIT_BREAKER_THRESHOLD: ${CIRCUIT_BREAKER_THRESHOLD:-3}
CIRCUIT_BREAKER_OPEN_DURATION_MS: ${CIRCUIT_BREAKER_OPEN_DURATION_MS:-300000}
CIRCUIT_BREAKER_PERSIST_TO_REDIS: ${CIRCUIT_BREAKER_PERSIST_TO_REDIS:-true}
# Cache
CACHE_ENABLED: ${CACHE_ENABLED:-true}
CACHE_DEFAULT_TTL: ${CACHE_DEFAULT_TTL:-300}
CACHE_STALE_TTL: ${CACHE_STALE_TTL:-7200}
# Metrics
METRICS_ENABLED: ${METRICS_ENABLED:-true}
METRICS_FLUSH_INTERVAL_MS: ${METRICS_FLUSH_INTERVAL_MS:-60000}
LOG_LEVEL: ${LOG_LEVEL:-info}
# Health Check
HEALTH_CHECK_PORT: 8000
HEALTH_CHECK_ENABLED: true
env_file:
- .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
minio:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ['CMD-SHELL', 'wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1']
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
frontend:
build:
context: .
dockerfile: apps/isekai-frontend/Dockerfile
# No build args needed - config is injected at runtime
ports:
- '3000:80' # Map host 3000 to container 80 (nginx)
environment:
# Runtime environment variables (injected by entrypoint script)
VITE_API_URL: ${VITE_API_URL:-http://localhost:4000/api}
VITE_DEVIANTART_CLIENT_ID: ${DEVIANTART_CLIENT_ID}
VITE_S3_PUBLIC_URL: ${S3_PUBLIC_URL:-http://localhost:9000/isekai-uploads}
env_file:
- .env
depends_on:
- backend
restart: unless-stopped
volumes:
postgres_data:
redis_data:
minio_data:
  • Image: postgres:16-alpine
  • Purpose: Primary database for application data
  • Volume: postgres_data - Persists all user data, drafts, and schedules
  • Health Check: Verifies database is ready before starting dependent services
  • Image: minio/minio:latest
  • Purpose: S3-compatible object storage for uploaded files
  • Volume: minio_data - Persists all uploaded images and files
  • Ports: 9000 (S3 API), 9001 (Web Console)
  • Console: Access at http://localhost:9001 (login: minioadmin / minioadmin)
  • Image: minio/mc:latest
  • Purpose: One-time initialization to create the default bucket
  • Behavior: Creates isekai-uploads bucket and sets it to public download
  • Image: redis:7-alpine
  • Purpose: Session storage, job queues, and caching
  • Volume: redis_data - Persists session data and queue jobs
  • Persistence: Configured with AOF (Append-Only File) for data durability
  • Image: Built from apps/isekai-backend/Dockerfile
  • Purpose: API server, authentication, and job scheduling
  • Dependencies: Waits for PostgreSQL, Redis, and MinIO to be healthy
  • Port: 4000 (configurable via BACKEND_PORT)
  • Image: Built from apps/isekai-publisher/Dockerfile
  • Purpose: Background worker for publishing to DeviantArt
  • Dependencies: Waits for PostgreSQL, Redis, and MinIO to be healthy
  • Port: 8000 (health check endpoint)
  • Image: Built from apps/isekai-frontend/Dockerfile
  • Purpose: User interface (React SPA)
  • Dependencies: Backend API
  • Port: 80 internally, mapped to 3000 externally (configurable via FRONTEND_PORT)

If you prefer to use an external S3-compatible storage provider (Cloudflare R2, AWS S3, etc.) instead of the included MinIO, simply override the S3_* environment variables in your .env file. See the Storage Setup guide for configuration details.