docker-compose.app.yml
Rama main · réplica de lectura del repositorio original.
# App services — deployed by blue-green swap.
# Production deploy.sh requires an immutable image built on a separate machine.
# The build definition remains available for local development and self-hosting.
#
# Usage:
# WEB_PORT=3000 docker compose -f docker-compose.app.yml -p sotto-blue up -d
# WEB_PORT=3010 docker compose -f docker-compose.app.yml -p sotto-green up -d
services:
web:
image: ${SOTTO_WEB_IMAGE:-sotto-web}:${SOTTO_IMAGE_TAG:-local}
build:
context: .
dockerfile: apps/web/Dockerfile
args:
COMMIT_SHA: ${COMMIT_SHA:-dev}
RUNTIME_UID: '1000'
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-}
NEXT_PUBLIC_VAPID_PUBLIC_KEY: ${NEXT_PUBLIC_VAPID_PUBLIC_KEY:-}
restart: unless-stopped
deploy:
resources:
limits:
memory: 1g
ports:
- '127.0.0.1:${WEB_PORT:?Set WEB_PORT}:3000'
env_file:
- .env
environment:
NODE_ENV: production
HOSTNAME: '0.0.0.0'
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL}
REDIS_URL: ${REDIS_URL:?Set REDIS_URL}
BYOK_ENCRYPTION_KEY: ${BYOK_ENCRYPTION_KEY:?Set BYOK_ENCRYPTION_KEY}
SOTTO_CREDENTIAL_SYNC_DIR: /run/cli-credentials
LOCAL_STORAGE_DIR: ${LOCAL_STORAGE_DIR:-/app/apps/web/.sotto/storage}
SIDEDOOR_EXECUTION_DIR: /app/apps/web/.sotto/runtime/executions
CLAUDE_HOME: /home/sotto/.claude
CODEX_HOME: /home/sotto/.codex
# Shared CLI homes have one UID 1000 owner across web and worker images.
volumes:
# Shared with the workers that write it; see docker-compose.workers.yml.
- storage:/app/apps/web/.sotto/storage
- executions:/app/apps/web/.sotto/runtime
- cli-credentials:/run/cli-credentials
- agent-home:/home/sotto/.claude
- agent-codex-home:/home/sotto/.codex
healthcheck:
test:
[
'CMD',
'wget',
'--no-verbose',
'--tries=1',
'--spider',
'http://127.0.0.1:3000/api/v1/health',
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- sotto-network
logging:
driver: json-file
options:
max-size: '50m'
max-file: '5'
networks:
sotto-network:
external: true
name: sotto-network
volumes:
# Host-wide, deliberately NOT stack-scoped: the CLIs rotate their refresh token
# in place and the previous one is retired server-side, so two stacks holding
# separate copies of one login would kill each other's token. One file, one
# lineage, shared by every stack on this host.
agent-home:
external: true
name: agent-claude-home
agent-codex-home:
external: true
name: agent-codex-home
cli-credentials:
external: true
name: ${SOTTO_STACK:?Set SOTTO_STACK}-cli-credentials
# Shared by both blue and green and by the worker stack, so it is named
# rather than project-scoped. Not external: whichever stack starts first
# creates it.
storage:
name: ${SOTTO_STACK:?Set SOTTO_STACK}-storage
executions:
name: ${SOTTO_STACK:?Set SOTTO_STACK}-executions