services: echo-server: build: context: . dockerfile: Dockerfile image: echo-agent-server:local restart: unless-stopped env_file: - path: .env required: true environment: ECHO_HOST: 0.0.0.0 ECHO_PORT: 8787 ECHO_DB_PATH: /app/data/echo.db ECHO_STORAGE_DIR: /app/data/storage ECHO_MODEL_DIR: /app/data/models ECHO_JWT_SECRET_FILE: /run/secrets/echo_jwt_secret ECHO_MASTER_KEY_FILE: /run/secrets/echo_master_key ECHO_ADMIN_PASSWORD_FILE: /run/secrets/echo_admin_password ECHO_CHAT_KEY_FILE: /run/secrets/echo_chat_key ECHO_OCR_KEY_FILE: /run/secrets/echo_ocr_key ECHO_VLM_KEY_FILE: /run/secrets/echo_vlm_key ECHO_TRANSCRIBE_KEY_FILE: /run/secrets/echo_transcribe_key ECHO_BACKUP_DIR: /app/backups ECHO_ANTIVIRUS_HOST: clamav ECHO_ANTIVIRUS_PORT: 3310 ECHO_ANTIVIRUS_REQUIRED: "true" ECHO_REQUIRE_CHAT: "true" ECHO_REQUIRE_OCR: "true" ECHO_REQUIRE_VLM: "true" ECHO_REQUIRE_TRANSCRIPTION: "true" # The bundled Caddy is the only proxy hop in front of Fastify. ECHO_TRUST_PROXY_HOPS: "1" depends_on: clamav: condition: service_healthy secrets: - echo_jwt_secret - echo_master_key - echo_admin_password - echo_chat_key - echo_ocr_key - echo_vlm_key - echo_transcribe_key volumes: - echo_data:/app/data - echo_backups:/app/backups expose: - "8787" healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8787/api/v1/health').then(async r=>{const j=await r.json();process.exit(r.ok&&j.models?.productionReady?0:1)}).catch(()=>process.exit(1))"] interval: 15s timeout: 3s start_period: 30s retries: 4 security_opt: - no-new-privileges:true cap_drop: - ALL read_only: true tmpfs: - /tmp:size=512m,mode=1777 networks: - echo_internal clamav: image: clamav/clamav:1.4 restart: unless-stopped volumes: - clamav_data:/var/lib/clamav expose: - "3310" healthcheck: test: ["CMD", "clamdscan", "--ping", "1"] interval: 30s timeout: 10s start_period: 120s retries: 5 security_opt: - no-new-privileges:true networks: - echo_internal caddy: image: caddy:2.10-alpine restart: unless-stopped depends_on: echo-server: condition: service_healthy environment: ECHO_PUBLIC_DOMAIN: ${ECHO_PUBLIC_DOMAIN:?set ECHO_PUBLIC_DOMAIN in .env.production} ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config security_opt: - no-new-privileges:true networks: - echo_internal secrets: echo_jwt_secret: file: ./deploy/secrets/echo_jwt_secret echo_master_key: file: ./deploy/secrets/echo_master_key echo_admin_password: file: ./deploy/secrets/echo_admin_password echo_chat_key: file: ./deploy/secrets/echo_chat_key echo_ocr_key: file: ./deploy/secrets/echo_ocr_key echo_vlm_key: file: ./deploy/secrets/echo_vlm_key echo_transcribe_key: file: ./deploy/secrets/echo_transcribe_key volumes: echo_data: echo_backups: clamav_data: caddy_data: caddy_config: networks: echo_internal: driver: bridge