compose-farm
★ 319A self-hosted alternative to Vercel Heroku
About
Community Ratings
No ratings yetNew this week
Read recap →Features
- multi-host Docker Compose execution
- SSH-based agentless operation
- declarative stack reconciliation
- stack migration between hosts
- Traefik file-provider config generation
- configuration of single-host and multi-host setups
- auto-migration
- plugin system
- host resource monitoring
- web UI
Details
- Last Updated
- Nov 10, 2026
- Created
- Dec 11, 2025
- Install Methods
- package-managerdockersource
- Requirements
- Python 3.11+SSH key-based authentication (ssh-agent)DockerDocker Composeshared storage accessible at the same path on all hostsexternal Docker networks on all hosts
- Authentication
- local
- Backup & Export
- native-export
- Privacy & Independence
- No Cloud RequiredOffline Capable
- Deployment
Deployment: Docker Compose ✓2 Containers — light hosting footprintRuns as 2 containers
Run compose-farm yourself
Pick how much you want to manage.
netcupBest match
Full control
A small server, then Docker Compose.
About 20 minutes with Docker
Hetzner
Full control
A small server, then Docker Compose.
About 20 minutes with Docker
DigitalOcean
Full control
A small server, then Docker Compose.
About 20 minutes with Docker
1 more way to host
Some links earn us a commission. netcup pays us one and is our preferred VPS host.
Is this your project? Sponsor a listing
Run compose-farm with Docker Compose
Compose file from the compose-farm project repository.
2 services
no resource limits declared
needs persistent storage (5 volumes)
docker-compose.yml · @ 5be8881 · scanned Nov 3, 2026
services:
cf:
image: ghcr.io/basnijholt/compose-farm:latest
# Run as current user to preserve file ownership on mounted volumes
# Set CF_UID=$(id -u) CF_GID=$(id -g) in your environment or .env file
# Defaults to root (0:0) for backwards compatibility
user: "${CF_UID:-0}:${CF_GID:-0}"
volumes:
# Compose directory (contains compose files AND compose-farm.yaml config)
- ${CF_COMPOSE_DIR:-/opt/stacks}:${CF_COMPOSE_DIR:-/opt/stacks}
# Persist known_hosts and optional dedicated keys. Choose option 1 or 2
# (use the same option for both cf and web services):
# Option 1: Host path (default) - keys at ~/.ssh/compose-farm/id_ed25519
- ${CF_SSH_DIR:-~/.ssh/compose-farm}:${CF_HOME:-/root}/.ssh/compose-farm
# Option 2: Named volume - managed by Docker, shared between services
# - cf-ssh:${CF_HOME:-/root}/.ssh
# Optional: add SSH agent forwarding while keeping a persistent mount above
# - ${SSH_AUTH_SOCK}:/ssh-agent:ro
environment:
- SSH_AUTH_SOCK=/ssh-agent
# Config file path (state stored alongside it)
- CF_CONFIG=${CF_COMPOSE_DIR:-/opt/stacks}/compose-farm.yaml
# HOME must match the user running the container for SSH to find keys
- HOME=${CF_HOME:-/root}
# Default SSH username for hosts without an explicit user.
- USER=${CF_USER:-root}
web:
image: ghcr.io/basnijholt/compose-farm:latest
restart: unless-stopped
command: web --host 0.0.0.0 --port 9000
# Run as current user to preserve file ownership on mounted volumes
user: "${CF_UID:-0}:${CF_GID:-0}"
volumes:
- ${CF_COMPOSE_DIR:-/opt/stacks}:${CF_COMPOSE_DIR:-/opt/stacks}
# Persist known_hosts and optional dedicated keys - use the SAME option as cf above
# Option 1: Host path (default)
- ${CF_SSH_DIR:-~/.ssh/compose-farm}:${CF_HOME:-/root}/.ssh/compose-farm
# Option 2: Named volume
# - cf-ssh:${CF_HOME:-/root}/.ssh
# Optional: add SSH agent forwarding while keeping a persistent mount above
# - ${SSH_AUTH_SOCK}:/ssh-agent:ro
# XDG config dir for backups and image digest logs (persists across restarts)
- ${CF_XDG_CONFIG:-~/.config/compose-farm}:${CF_HOME:-/root}/.config/compose-farm
environment:
- SSH_AUTH_SOCK=/ssh-agent
- CF_CONFIG=${CF_COMPOSE_DIR:-/opt/stacks}/compose-farm.yaml
# Used to detect self-updates and run via SSH to survive container restart
- CF_WEB_STACK=compose-farm
# Remote access requires a password unless CF_WEB_NO_AUTH=1 is explicitly set.
- CF_WEB_USERNAME=${CF_WEB_USERNAME:-admin}
- CF_WEB_PASSWORD=${CF_WEB_PASSWORD:-}
- CF_WEB_NO_AUTH=${CF_WEB_NO_AUTH:-}
# HOME must match the user running the container for SSH to find keys
- HOME=${CF_HOME:-/root}
# Default SSH username for hosts without an explicit user.
- USER=${CF_USER:-root}
labels:
- traefik.enable=true
- traefik.http.routers.compose-farm.rule=Host(`compose-farm.${DOMAIN}`)
- traefik.http.routers.compose-farm.entrypoints=websecure
- traefik.http.routers.compose-farm-local.rule=Host(`compose-farm.local`)
- traefik.http.routers.compose-farm-local.entrypoints=web
- traefik.http.services.compose-farm.loadbalancer.server.port=9000
networks:
- mynetwork
networks:
mynetwork:
external: true
volumes:
cf-ssh:
# Only used if Option 2 is selected above
This file may be out of date. Check the compose-farm website for current setup steps.
Track your self-hosted stack
Bookmark software to try, rate tools you've used, and keep your collection in one place.