fly-trader / deploy /docker-compose.vault.yml
bryceweiner's picture
release 1 (7ab2355a0c)
ad0694f verified
Raw History Blame Contribute Delete
3.8 kB
# The hosted $FLY vault fly (docs/vault/SPEC.md). Installed by bootstrap-host.sh as /srv/fly/docker-compose.yml and
# driven by the updater (fly_update.py): FLY_RELEASE picks the image tags it built from a verified release.
# Nothing listens on a public interface: the console is on 127.0.0.1 (reach it with an SSH tunnel over Tailscale) and
# the containers only make outbound connections.
#
# fly-signer the ONLY container that can read the two hot keys; it checks and signs, over a Unix socket in
# /srv/fly/run (a directory only it and the brain's group can enter). Read-only filesystem, no capabilities.
# fly the brain: engine, console, vault worker, data feeds. No key is mounted into it.
# db Postgres on an internal network (no route out), its password generated by bootstrap-host.sh.
#
# Settings: /srv/fly/custody.env (public: the treasury multisig, its limits, the float and caps; read by both),
# /srv/fly/signer.env (the signer's own RPC URL), /srv/fly/vault.env (the brain's API keys, relay, Telegram, backups),
# /srv/fly/db.env (the database password). All root-only (0600) on the host.
services:
db:
image: postgres:17@sha256:d74eeac9a635390a49bc21bd49fccd973de707e2a53a76ac49b552b8712ec46f
env_file: /srv/fly/db.env
environment:
POSTGRES_USER: fly
POSTGRES_DB: fly_trader
volumes:
- pgdata:/var/lib/postgresql/data
networks: [internal]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U fly -d fly_trader"]
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
security_opt: ["no-new-privileges:true"]
logging: {driver: json-file, options: {max-size: "20m", max-file: "5"}}
fly-signer:
image: fly-signer:${FLY_RELEASE:-none}
env_file: [/srv/fly/custody.env, /srv/fly/signer.env]
environment:
BOT_PRIVATE_KEY_FILE: /run/secrets/bot_key
PAYOUT_PRIVATE_KEY_FILE: /run/secrets/payout_key
SIGNER_SOCKET: /run/fly-signer/sock
SIGNER_DB: /srv/signer/ledger.sqlite
volumes:
- /srv/fly/secrets/bot_key:/run/secrets/bot_key:ro
- /srv/fly/secrets/payout_key:/run/secrets/payout_key:ro
- /srv/fly/signer:/srv/signer
- /srv/fly/run:/run/fly-signer
networks: [egress] # its own RPC and Jupiter's price API; nothing reaches in
read_only: true
tmpfs: [/tmp]
restart: unless-stopped
security_opt: ["no-new-privileges:true"]
cap_drop: [ALL]
mem_limit: 512m
pids_limit: 64
logging: {driver: json-file, options: {max-size: "20m", max-file: "5"}}
fly:
image: fly-trader:${FLY_RELEASE:-none}
env_file: [/srv/fly/custody.env, /srv/fly/vault.env, /srv/fly/db.env]
environment:
DATA_DIR: /app/data
LOG_DIR: /app/logs
SIGNER_SOCKET: /run/fly-signer/sock
VAULT_ENABLED: "1"
AUTO_UPDATE: "0" # releases come through the host updater (signed, 24 h hold on code), never from inside
volumes:
- /srv/fly/data:/app/data
- /srv/fly/logs:/app/logs
- /srv/fly/run:/run/fly-signer
- ${FLY_RELEASES:-/srv/fly/releases}:/releases:ro
networks: [internal, egress]
ports:
- "127.0.0.1:8501:8501"
depends_on:
db:
condition: service_healthy
fly-signer:
condition: service_healthy
restart: unless-stopped
stop_grace_period: 150s
security_opt: ["no-new-privileges:true"]
cap_drop: [ALL]
mem_limit: 48g # a dedicated 128 GB box; lower it on a smaller one (16 GB: 14g)
logging: {driver: json-file, options: {max-size: "50m", max-file: "5"}}
networks:
internal:
internal: true # the database has no route to anything but its two clients
egress: {}
volumes:
pgdata: