version: "3.9" services: control-api: image: ${IMAGE_PREFIX:-cloudlysis}/control-api:${IMAGE_TAG:-dev} environment: CONTROL_API_ADDR: "0.0.0.0:8080" CONTROL_PLACEMENT_PATH: "/etc/control/placement.json" CONTROL_SWARM_STATE_PATH: "/etc/control/swarm_state.json" CONTROL_SELF_URL: "${CONTROL_SELF_URL:-http://control-api:8080}" # S3 document storage (Hetzner Object Storage in production). CONTROL_S3_ENDPOINT: "${CONTROL_S3_ENDPOINT:?missing}" CONTROL_S3_PUBLIC_ENDPOINT: "${CONTROL_S3_PUBLIC_ENDPOINT:-}" CONTROL_S3_REGION: "${CONTROL_S3_REGION:?missing}" CONTROL_S3_ACCESS_KEY_ID_FILE: "/run/secrets/control_s3_access_key_id" CONTROL_S3_SECRET_ACCESS_KEY_FILE: "/run/secrets/control_s3_secret_access_key" CONTROL_S3_FORCE_PATH_STYLE: "${CONTROL_S3_FORCE_PATH_STYLE:-false}" CONTROL_S3_INSECURE: "${CONTROL_S3_INSECURE:-false}" CONTROL_S3_BUCKET_DOCS: "${CONTROL_S3_BUCKET_DOCS:?missing}" CONTROL_S3_PREFIX_DOCS: "${CONTROL_S3_PREFIX_DOCS:-docs/}" secrets: - control_s3_access_key_id - control_s3_secret_access_key configs: - source: control_placement target: /etc/control/placement.json - source: control_swarm_state target: /etc/control/swarm_state.json networks: - internal ports: - target: 8080 published: 8080 protocol: tcp mode: ingress deploy: replicas: 2 restart_policy: condition: on-failure update_config: parallelism: 1 order: start-first failure_action: rollback control-ui: image: ${IMAGE_PREFIX:-cloudlysis}/control-ui:${IMAGE_TAG:-dev} environment: VITE_CONTROL_API_URL: "${VITE_CONTROL_API_URL:-http://control-api:8080}" networks: - public - internal ports: - target: 80 published: 8081 protocol: tcp mode: ingress deploy: replicas: 2 restart_policy: condition: on-failure configs: control_placement: file: ../../config/placement/dev.json control_swarm_state: file: ../../swarm/dev.json secrets: control_s3_access_key_id: external: true control_s3_secret_access_key: external: true networks: public: driver: overlay internal: driver: overlay