# CashPilot - Self-hosted passive income orchestrator # https://github.com/GeiserX/CashPilot # # UI + Worker on the same server. For multi-server fleet, see docker-compose.fleet.yml # # Start / update: docker compose pull && docker compose up -d # # Images are PINNED to a major.minor tag, which is what the lines below say. # `docker compose pull && up -d` therefore gets the newest patch of that series, # not the newest release overall — to move to a newer series, edit the tags. # # This header used to claim the file tracked `latest` while pinning 1.4, and the # string `:latest` it told you to replace did not appear anywhere. Following the # quickstart therefore installed a version many releases behind, and issue #188 # is someone hitting a first-run bug that had been fixed for months because of # it. Keep this comment honest about what the file actually does. # See https://hub.docker.com/r/drumsergio/cashpilot/tags for available tags. # NOTE: versions before 1.0.0 predate the per-worker fleet-key auth cutover and # are not compatible with a worker running 1.x -- pin UI and worker together. # Building from source? Use: docker compose -f docker-compose.build.yml up -d services: cashpilot-ui: image: drumsergio/cashpilot:1.43 pull_policy: always container_name: cashpilot-ui # Published on loopback by default: the dashboard can command the Docker-socket # worker, so it must not be exposed to your LAN/the internet out of the box. # To reach it from another machine, point CASHPILOT_BIND_ADDR at a specific # interface (e.g. a Tailscale/VPN IP) or, better, run an authenticating reverse # proxy in front. CASHPILOT_BIND_ADDR=0.0.0.0 publishes on all interfaces. ports: - "${CASHPILOT_BIND_ADDR:-127.0.0.1}:8080:8080" volumes: - cashpilot_data:/data - cashpilot_fleet:/fleet environment: - TZ=${TZ:-UTC} - CASHPILOT_SECRET_KEY=${CASHPILOT_SECRET_KEY:-} # Optional: only needed to RESTORE credentials onto a fresh volume. # An existing /data/.fernet_key always wins, so setting this is safe. - CASHPILOT_ENCRYPTION_KEY=${CASHPILOT_ENCRYPTION_KEY:-} - CASHPILOT_ALLOW_EPHEMERAL_KEY=${CASHPILOT_ALLOW_EPHEMERAL_KEY:-false} - CASHPILOT_API_KEY=${CASHPILOT_API_KEY:-} - CASHPILOT_ADMIN_API_KEY=${CASHPILOT_ADMIN_API_KEY:-} init: true # The image declares its own HEALTHCHECK, so `docker ps` will show # "(unhealthy)" when the app stops answering. NOTHING ACTS ON THAT BY # DEFAULT: `restart: unless-stopped` restarts a container that EXITS, and a # container failing its healthcheck is still running. It will sit there # unhealthy indefinitely. (Docker Swarm reschedules unhealthy tasks; plain # Compose does not.) # # To act on it, either alert on it -- see the CashPilotDown rule in # docs/guides/prometheus-metrics.md -- or add an autoheal sidecar, which is # documented in the same place along with the fact that it needs the Docker # socket, i.e. root on the host. restart: unless-stopped security_opt: - no-new-privileges:true tmpfs: - /tmp:size=64M cashpilot-worker: image: drumsergio/cashpilot-worker:1.43 pull_policy: always container_name: cashpilot-worker expose: - "8081" # GPU passthrough. COMMENTED OUT ON PURPOSE: Docker refuses to start a # container when a listed device does not exist, so uncommenting this on a # host with no GPU breaks the worker outright (verified: `docker run # --device /dev/does-not-exist` fails). # # Uncomment on a Linux host that HAS a GPU. Without it the worker cannot # see the hardware, so CashPilot reports the GPU as *unknown* and cannot # tell you whether Salad, Nosana, io.net or Vast.ai would earn here. Worse, # a GPU service deployed without the device starts, looks healthy, and # earns nothing -- the same shape as the Mysterium /dev/net/tun failure. # # Intel / AMD -- the kernel exposes the GPU as a DRM render node: # devices: # - /dev/dri:/dev/dri # # NVIDIA is a DIFFERENT mechanism. /dev/dri does nothing for it, and # installing the NVIDIA Container Toolkit is only the prerequisite -- the # toolkit alone does NOT hand the GPU to a Compose service. You must also # ask for it, with EITHER of these (both are Compose-spec attributes): # # gpus: all # # deploy: # resources: # reservations: # devices: # - driver: nvidia # count: all # capabilities: [gpu] # # Both of these also fail on a host with no NVIDIA GPU (verified: exit 1, # "could not select device driver"), which is why they are commented out # too. Pick ONE; do not set both. volumes: - /var/run/docker.sock:/var/run/docker.sock # SEPARATE FROM THE UI'S /data, DELIBERATELY. This container has the # Docker socket, which is root on the host. The UI's /data holds # cashpilot.db and .fernet_key -- the credential store and the only key # that can decrypt it -- and the component that can start any container # on the machine has no business being able to read them. # # /fleet IS shared on purpose: it holds only the shared enrolment key, # which both sides need by definition. # # Consolidating these into one volume is the obvious "simplification" and # it silently removes that boundary. tests/test_beads_batch_69.py fails if # anyone does. - cashpilot_worker_data:/data - cashpilot_fleet:/fleet environment: - TZ=${TZ:-UTC} - CASHPILOT_UI_URL=http://cashpilot-ui:8080 - CASHPILOT_API_KEY=${CASHPILOT_API_KEY:-} - CASHPILOT_WORKER_NAME=local init: true restart: unless-stopped security_opt: - no-new-privileges:true tmpfs: - /tmp:size=64M volumes: cashpilot_data: cashpilot_worker_data: cashpilot_fleet: