환경변수
name: contextstt
services:
mysql:
image: ${MYSQL_IMAGE:-mysql:8.4}
restart: unless-stopped
environment:
MYSQL_DATABASE: "${DB_NAME:?DB_NAME is required}"
MYSQL_USER: "${DB_USERNAME:?DB_USERNAME is required}"
MYSQL_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}"
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD is required}"
TZ: Asia/Seoul
volumes:
- mysql-data:/var/lib/mysql
networks:
- database
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root --password=\"$${MYSQL_ROOT_PASSWORD}\" --silent"]
interval: 5s
timeout: 5s
retries: 10
start_period: 30s
logging:
options:
max-size: 10m
max-file: "5"
backend:
image: ${BACKEND_IMAGE:-contextstt-backend:local}
init: true
restart: unless-stopped
read_only: true
tmpfs:
- /tmp:size=64m,mode=1777
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
DB_HOST: mysql
DB_PORT: 3306
DB_NAME: "${DB_NAME:?DB_NAME is required}"
DB_USERNAME: "${DB_USERNAME:?DB_USERNAME is required}"
DB_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}"
FLYWAY_BASELINE_ON_MIGRATE: ${FLYWAY_BASELINE_ON_MIGRATE:-false}
JPA_SHOW_SQL: ${JPA_SHOW_SQL:-false}
JPA_FORMAT_SQL: ${JPA_FORMAT_SQL:-false}
AUTH_RATE_LIMIT_MAXIMUM_TRACKED_KEYS: ${AUTH_RATE_LIMIT_MAXIMUM_TRACKED_KEYS:-20000}
AUTH_RATE_LIMIT_SIGNUP_CAPACITY: ${AUTH_RATE_LIMIT_SIGNUP_CAPACITY:-5}
AUTH_RATE_LIMIT_SIGNUP_WINDOW: ${AUTH_RATE_LIMIT_SIGNUP_WINDOW:-1m}
AUTH_RATE_LIMIT_LOGIN_CAPACITY: ${AUTH_RATE_LIMIT_LOGIN_CAPACITY:-10}
AUTH_RATE_LIMIT_LOGIN_WINDOW: ${AUTH_RATE_LIMIT_LOGIN_WINDOW:-1m}
STT_GOOGLE_ENABLED: ${STT_GOOGLE_ENABLED:-false}
GOOGLE_CLOUD_PROJECT: ${GOOGLE_CLOUD_PROJECT:-}
STT_GOOGLE_LOCATION: ${STT_GOOGLE_LOCATION:-asia-northeast1}
STT_GOOGLE_MODEL: ${STT_GOOGLE_MODEL:-long}
STT_MAX_AUDIO_BYTES: ${STT_MAX_AUDIO_BYTES:-10485760}
CLAUDE_API_ENABLED: ${CLAUDE_API_ENABLED:-false}
CLAUDE_API_KEY: ${CLAUDE_API_KEY:-}
CLAUDE_API_BASE_URL: ${CLAUDE_API_BASE_URL:-https://api.anthropic.com}
CLAUDE_API_MODEL: ${CLAUDE_API_MODEL:-claude-sonnet-5}
CLAUDE_API_MAX_TOKENS: ${CLAUDE_API_MAX_TOKENS:-2048}
CLAUDE_API_TIMEOUT_SECONDS: ${CLAUDE_API_TIMEOUT_SECONDS:-30}
OPENROUTER_API_ENABLED: ${OPENROUTER_API_ENABLED:-false}
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
OPENROUTER_API_BASE_URL: ${OPENROUTER_API_BASE_URL:-https://openrouter.ai/api/v1}
OPENROUTER_GEMINI_MODEL: ${OPENROUTER_GEMINI_MODEL:-google/gemini-3.7-flash}
OPENROUTER_DEEPSEEK_MODEL: ${OPENROUTER_DEEPSEEK_MODEL:-deepseek/deepseek-v4-flash}
OPENROUTER_API_MAX_TOKENS: ${OPENROUTER_API_MAX_TOKENS:-2048}
OPENROUTER_API_TIMEOUT_SECONDS: ${OPENROUTER_API_TIMEOUT_SECONDS:-30}
OPENROUTER_SITE_URL: ${OPENROUTER_SITE_URL:-}
OPENROUTER_APP_TITLE: ${OPENROUTER_APP_TITLE:-Context STT}
CONTEXT_ANALYSIS_MAX_PREVIOUS_UTTERANCES: ${CONTEXT_ANALYSIS_MAX_PREVIOUS_UTTERANCES:-20}
CONTEXT_ANALYSIS_MAX_PREVIOUS_CHARACTERS: ${CONTEXT_ANALYSIS_MAX_PREVIOUS_CHARACTERS:-12000}
CONTEXT_ANALYSIS_RATE_LIMIT_CAPACITY: ${CONTEXT_ANALYSIS_RATE_LIMIT_CAPACITY:-10}
CONTEXT_ANALYSIS_RATE_LIMIT_WINDOW: ${CONTEXT_ANALYSIS_RATE_LIMIT_WINDOW:-1m}
CONTEXT_ANALYSIS_RATE_LIMIT_MAXIMUM_TRACKED_USERS: ${CONTEXT_ANALYSIS_RATE_LIMIT_MAXIMUM_TRACKED_USERS:-10000}
JWT_SECRET: "${JWT_SECRET:?JWT_SECRET is required}"
JWT_ACCESS_TOKEN_VALIDITY_MS: ${JWT_ACCESS_TOKEN_VALIDITY_MS:-3600000}
SERVER_PORT: 8080
TZ: Asia/Seoul
depends_on:
mysql:
condition: service_healthy
networks:
- database
- outbound
stop_grace_period: 30s
logging:
options:
max-size: 10m
max-file: "5"
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- outbound
depends_on:
- backend
logging:
options:
max-size: 10m
max-file: "5"
networks:
database:
internal: true
outbound:
volumes:
mysql-data:
caddy-data:
caddy-config:
YAML
복사



