feat: Docker production setup (Portainer + Traefik)
Adds multi-stage Dockerfile (runner + worker targets), docker-compose.prod.yml with Traefik labels, auto-migration on deploy, and next.config.ts standalone output mode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: curio
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- ./docker/init.sql:/docker-entrypoint-initdb.d/01_init.sql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
migrate:
|
||||
build:
|
||||
context: .
|
||||
target: worker
|
||||
command: ["node_modules/.bin/drizzle-kit", "migrate"]
|
||||
environment:
|
||||
DATABASE_URL: ${DATABASE_URL}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
restart: on-failure
|
||||
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
target: runner
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
migrate:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: ${DATABASE_URL}
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
AUTH_SECRET: ${AUTH_SECRET}
|
||||
AUTH_URL: ${AUTH_URL}
|
||||
AUTH_GITHUB_ID: ${AUTH_GITHUB_ID:-}
|
||||
AUTH_GITHUB_SECRET: ${AUTH_GITHUB_SECRET:-}
|
||||
AUTH_GOOGLE_ID: ${AUTH_GOOGLE_ID:-}
|
||||
AUTH_GOOGLE_SECRET: ${AUTH_GOOGLE_SECRET:-}
|
||||
AUTH_OIDC_ISSUER: ${AUTH_OIDC_ISSUER:-}
|
||||
AUTH_OIDC_CLIENT_ID: ${AUTH_OIDC_CLIENT_ID:-}
|
||||
AUTH_OIDC_CLIENT_SECRET: ${AUTH_OIDC_CLIENT_SECRET:-}
|
||||
AUTH_OIDC_DISPLAY_NAME: ${AUTH_OIDC_DISPLAY_NAME:-}
|
||||
LLM_GENERATOR_PROVIDER: ${LLM_GENERATOR_PROVIDER}
|
||||
LLM_GENERATOR_MODEL: ${LLM_GENERATOR_MODEL}
|
||||
LLM_GRADER_PROVIDER: ${LLM_GRADER_PROVIDER}
|
||||
LLM_GRADER_MODEL: ${LLM_GRADER_MODEL}
|
||||
LLM_CHEAP_GRADER_PROVIDER: ${LLM_CHEAP_GRADER_PROVIDER:-}
|
||||
LLM_CHEAP_GRADER_MODEL: ${LLM_CHEAP_GRADER_MODEL:-}
|
||||
LLM_EMBED_PROVIDER: ${LLM_EMBED_PROVIDER:-}
|
||||
LLM_EMBED_MODEL: ${LLM_EMBED_MODEL:-}
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
|
||||
GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY:-}
|
||||
GROQ_API_KEY: ${GROQ_API_KEY:-}
|
||||
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
|
||||
XAI_API_KEY: ${XAI_API_KEY:-}
|
||||
AZURE_API_KEY: ${AZURE_API_KEY:-}
|
||||
AZURE_OPENAI_RESOURCE_NAME: ${AZURE_OPENAI_RESOURCE_NAME:-}
|
||||
COHERE_API_KEY: ${COHERE_API_KEY:-}
|
||||
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-}
|
||||
LANGFUSE_HOST: ${LANGFUSE_HOST:-}
|
||||
LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY:-}
|
||||
LANGFUSE_SECRET_KEY: ${LANGFUSE_SECRET_KEY:-}
|
||||
UPSTASH_REDIS_REST_URL: ${UPSTASH_REDIS_REST_URL:-}
|
||||
UPSTASH_REDIS_REST_TOKEN: ${UPSTASH_REDIS_REST_TOKEN:-}
|
||||
SESSION_TOKEN_BUDGET: ${SESSION_TOKEN_BUDGET:-}
|
||||
CONTENT_SAFETY_MODE: ${CONTENT_SAFETY_MODE:-}
|
||||
SMTP_HOST: ${SMTP_HOST:-}
|
||||
SMTP_PORT: ${SMTP_PORT:-}
|
||||
SMTP_USER: ${SMTP_USER:-}
|
||||
SMTP_PASS: ${SMTP_PASS:-}
|
||||
EMAIL_FROM: ${EMAIL_FROM:-}
|
||||
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
|
||||
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
|
||||
AWS_REGION: ${AWS_REGION:-}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.curio.rule=Host(`${DOMAIN}`)"
|
||||
- "traefik.http.routers.curio.entrypoints=websecure"
|
||||
- "traefik.http.routers.curio.tls=true"
|
||||
- "traefik.http.routers.curio.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.curio.loadbalancer.server.port=3000"
|
||||
|
||||
worker:
|
||||
build:
|
||||
context: .
|
||||
target: worker
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
migrate:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: ${DATABASE_URL}
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
LLM_GENERATOR_PROVIDER: ${LLM_GENERATOR_PROVIDER}
|
||||
LLM_GENERATOR_MODEL: ${LLM_GENERATOR_MODEL}
|
||||
LLM_GRADER_PROVIDER: ${LLM_GRADER_PROVIDER}
|
||||
LLM_GRADER_MODEL: ${LLM_GRADER_MODEL}
|
||||
LLM_CHEAP_GRADER_PROVIDER: ${LLM_CHEAP_GRADER_PROVIDER:-}
|
||||
LLM_CHEAP_GRADER_MODEL: ${LLM_CHEAP_GRADER_MODEL:-}
|
||||
LLM_EMBED_PROVIDER: ${LLM_EMBED_PROVIDER:-}
|
||||
LLM_EMBED_MODEL: ${LLM_EMBED_MODEL:-}
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
|
||||
GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY:-}
|
||||
GROQ_API_KEY: ${GROQ_API_KEY:-}
|
||||
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
|
||||
XAI_API_KEY: ${XAI_API_KEY:-}
|
||||
AZURE_API_KEY: ${AZURE_API_KEY:-}
|
||||
AZURE_OPENAI_RESOURCE_NAME: ${AZURE_OPENAI_RESOURCE_NAME:-}
|
||||
COHERE_API_KEY: ${COHERE_API_KEY:-}
|
||||
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-}
|
||||
LANGFUSE_HOST: ${LANGFUSE_HOST:-}
|
||||
LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY:-}
|
||||
LANGFUSE_SECRET_KEY: ${LANGFUSE_SECRET_KEY:-}
|
||||
SESSION_TOKEN_BUDGET: ${SESSION_TOKEN_BUDGET:-}
|
||||
CONTENT_SAFETY_MODE: ${CONTENT_SAFETY_MODE:-}
|
||||
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
|
||||
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
|
||||
AWS_REGION: ${AWS_REGION:-}
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
redisdata:
|
||||
|
||||
networks:
|
||||
default:
|
||||
external: true
|
||||
name: ${TRAEFIK_NETWORK:-traefik}
|
||||
Reference in New Issue
Block a user