# docker-compose.prod.yml — deploy su VPS produzione. # # Differenze vs docker-compose.yml (dev): # - Niente `build:`, solo `image:` dal registry Gitea. # - Tag `latest` (Watchtower polla per nuove versioni). # - Aggiunge servizio `watchtower` che auto-aggiorna i container etichettati # `com.centurylinklabs.watchtower.enable=true` quando il tag latest cambia. # - Auth registry: `docker login git.tielogic.xyz` una sola volta sull'host # (Watchtower legge ~/.docker/config.json bind-mounted in /config.json). # # Uso sul VPS: # docker login git.tielogic.xyz # docker compose -f docker-compose.prod.yml --env-file .env up -d # # Override variabili in `.env` accanto al compose: # ACME_EMAIL=adrianodalpastro@tielogic.com # WRITE_ALLOWLIST="127.0.0.1/32 ::1/128 172.16.0.0/12" # GATEWAY_HTTP_PORT=80 # GATEWAY_HTTPS_PORT=443 # IMAGE_TAG=latest # o sha-XXXXXXX per pin specifico networks: internal: driver: bridge volumes: caddy-data: caddy-config: secrets: deribit_credentials: file: ./secrets/deribit.json hyperliquid_wallet: file: ./secrets/hyperliquid.json bybit_credentials: file: ./secrets/bybit.json alpaca_credentials: file: ./secrets/alpaca.json macro_credentials: file: ./secrets/macro.json sentiment_credentials: file: ./secrets/sentiment.json core_token: file: ./secrets/core.token observer_token: file: ./secrets/observer.token x-common-security: &common-security cap_drop: [ALL] security_opt: - no-new-privileges:true restart: unless-stopped networks: [internal] labels: com.centurylinklabs.watchtower.enable: "true" volumes: - ${AUDIT_LOG_DIR:-/var/log/cerbero-mcp}:/var/log/cerbero-mcp:rw x-image-prefix: &image_prefix git.tielogic.xyz/adriano/cerbero-mcp services: gateway: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/gateway:${IMAGE_TAG:-latest} restart: unless-stopped networks: [internal] security_opt: - no-new-privileges:true labels: com.centurylinklabs.watchtower.enable: "true" ports: - "${GATEWAY_HTTP_PORT:-80}:80" - "${GATEWAY_HTTPS_PORT:-443}:443" environment: ACME_EMAIL: ${ACME_EMAIL:-adrianodalpastro@tielogic.com} WRITE_ALLOWLIST: ${WRITE_ALLOWLIST:-127.0.0.1/32 ::1/128 172.16.0.0/12} volumes: - ./gateway/Caddyfile:/etc/caddy/Caddyfile:ro - ./gateway/public:/srv:ro - caddy-data:/data - caddy-config:/config depends_on: mcp-deribit: { condition: service_healthy } mcp-hyperliquid: { condition: service_healthy } mcp-bybit: { condition: service_healthy } mcp-alpaca: { condition: service_healthy } mcp-macro: { condition: service_healthy } mcp-sentiment: { condition: service_healthy } healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost/"] interval: 30s timeout: 5s retries: 3 mcp-deribit: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-deribit:${IMAGE_TAG:-latest} <<: *common-security user: "1000:1000" read_only: true tmpfs: - /tmp:rw,size=64M,mode=1777 secrets: [deribit_credentials, core_token, observer_token] environment: CREDENTIALS_FILE: /run/secrets/deribit_credentials CORE_TOKEN_FILE: /run/secrets/core_token OBSERVER_TOKEN_FILE: /run/secrets/observer_token DERIBIT_TESTNET: "${DERIBIT_TESTNET:-true}" ROOT_PATH: /mcp-deribit AUDIT_LOG_FILE: /var/log/cerbero-mcp/deribit.audit.jsonl mcp-hyperliquid: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-hyperliquid:${IMAGE_TAG:-latest} <<: *common-security user: "1000:1000" read_only: true tmpfs: - /tmp:rw,size=64M,mode=1777 secrets: [hyperliquid_wallet, core_token, observer_token] environment: HYPERLIQUID_WALLET_FILE: /run/secrets/hyperliquid_wallet CORE_TOKEN_FILE: /run/secrets/core_token OBSERVER_TOKEN_FILE: /run/secrets/observer_token HYPERLIQUID_TESTNET: "${HYPERLIQUID_TESTNET:-true}" ROOT_PATH: /mcp-hyperliquid AUDIT_LOG_FILE: /var/log/cerbero-mcp/hyperliquid.audit.jsonl mcp-bybit: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-bybit:${IMAGE_TAG:-latest} <<: *common-security user: "1000:1000" read_only: true tmpfs: - /tmp:rw,size=64M,mode=1777 secrets: [bybit_credentials, core_token, observer_token] environment: BYBIT_CREDENTIALS_FILE: /run/secrets/bybit_credentials CORE_TOKEN_FILE: /run/secrets/core_token OBSERVER_TOKEN_FILE: /run/secrets/observer_token BYBIT_TESTNET: "${BYBIT_TESTNET:-true}" ROOT_PATH: /mcp-bybit AUDIT_LOG_FILE: /var/log/cerbero-mcp/bybit.audit.jsonl PORT: "9019" mcp-alpaca: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-alpaca:${IMAGE_TAG:-latest} <<: *common-security user: "1000:1000" read_only: true tmpfs: - /tmp:rw,size=64M,mode=1777 secrets: [alpaca_credentials, core_token, observer_token] environment: ALPACA_CREDENTIALS_FILE: /run/secrets/alpaca_credentials CORE_TOKEN_FILE: /run/secrets/core_token OBSERVER_TOKEN_FILE: /run/secrets/observer_token ALPACA_PAPER: "${ALPACA_PAPER:-true}" ROOT_PATH: /mcp-alpaca AUDIT_LOG_FILE: /var/log/cerbero-mcp/alpaca.audit.jsonl PORT: "9020" mcp-macro: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-macro:${IMAGE_TAG:-latest} <<: *common-security user: "1000:1000" read_only: true tmpfs: - /tmp:rw,size=64M,mode=1777 secrets: [macro_credentials, core_token, observer_token] environment: MACRO_CREDENTIALS_FILE: /run/secrets/macro_credentials CORE_TOKEN_FILE: /run/secrets/core_token OBSERVER_TOKEN_FILE: /run/secrets/observer_token ROOT_PATH: /mcp-macro mcp-sentiment: image: ${IMAGE_PREFIX:-git.tielogic.xyz/adriano/cerbero-mcp}/mcp-sentiment:${IMAGE_TAG:-latest} <<: *common-security user: "1000:1000" read_only: true tmpfs: - /tmp:rw,size=64M,mode=1777 secrets: [sentiment_credentials, core_token, observer_token] environment: SENTIMENT_CREDENTIALS_FILE: /run/secrets/sentiment_credentials CORE_TOKEN_FILE: /run/secrets/core_token OBSERVER_TOKEN_FILE: /run/secrets/observer_token ROOT_PATH: /mcp-sentiment # ======================================================== # WATCHTOWER — auto-update container con label enable=true # ======================================================== watchtower: image: containrrr/watchtower:latest restart: unless-stopped networks: [internal] volumes: - /var/run/docker.sock:/var/run/docker.sock - ${HOME}/.docker/config.json:/config.json:ro environment: WATCHTOWER_LABEL_ENABLE: "true" WATCHTOWER_CLEANUP: "true" WATCHTOWER_POLL_INTERVAL: "${WATCHTOWER_POLL_INTERVAL:-300}" WATCHTOWER_INCLUDE_RESTARTING: "false" WATCHTOWER_NOTIFICATIONS_LEVEL: info WATCHTOWER_LOG_LEVEL: info command: --interval ${WATCHTOWER_POLL_INTERVAL:-300}