mirror of
https://github.com/basnijholt/compose-farm.git
synced 2026-02-03 14:13:26 +00:00
Clarify single-host vs multi-host docs (#73)
This commit is contained in:
61
README.md
61
README.md
@@ -12,12 +12,16 @@ A minimal CLI tool to run Docker Compose commands across multiple hosts via SSH.
|
||||
> [!NOTE]
|
||||
> Run `docker compose` commands across multiple hosts via SSH. One YAML maps services to hosts. Run `cf apply` and reality matches your config—services start, migrate, or stop as needed. No Kubernetes, no Swarm, no magic.
|
||||
|
||||
Single host? Map everything to `localhost` and Compose Farm just runs locally from each stack folder.
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [Why Compose Farm?](#why-compose-farm)
|
||||
- [How It Works](#how-it-works)
|
||||
- [Requirements](#requirements)
|
||||
- [Single host (local-only)](#single-host-local-only)
|
||||
- [Multi-host](#multi-host)
|
||||
- [Limitations & Best Practices](#limitations--best-practices)
|
||||
- [What breaks when you move a service](#what-breaks-when-you-move-a-service)
|
||||
- [Best practices](#best-practices)
|
||||
@@ -27,6 +31,8 @@ A minimal CLI tool to run Docker Compose commands across multiple hosts via SSH.
|
||||
- [SSH Agent (default)](#ssh-agent-default)
|
||||
- [Dedicated SSH Key (recommended for Docker/Web UI)](#dedicated-ssh-key-recommended-for-dockerweb-ui)
|
||||
- [Configuration](#configuration)
|
||||
- [Single-host example](#single-host-example)
|
||||
- [Multi-host example](#multi-host-example)
|
||||
- [Multi-Host Services](#multi-host-services)
|
||||
- [Config Command](#config-command)
|
||||
- [Usage](#usage)
|
||||
@@ -85,13 +91,24 @@ That's it. No orchestration, no service discovery, no magic.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.11+ (we recommend [uv](https://docs.astral.sh/uv/) for installation)
|
||||
- SSH key-based authentication to your hosts (uses ssh-agent)
|
||||
- Docker and Docker Compose installed on all target hosts
|
||||
- **Shared storage**: All compose files must be accessible at the same path on all hosts
|
||||
- **Docker networks**: External networks must exist on all hosts (use `cf init-network` to create)
|
||||
### Single host (local-only)
|
||||
|
||||
Compose Farm assumes your compose files are accessible at the same path on all hosts. This is typically achieved via:
|
||||
- Python 3.11+ (we recommend [uv](https://docs.astral.sh/uv/) for installation)
|
||||
- Docker and Docker Compose installed
|
||||
- One folder per stack under `compose_dir`
|
||||
|
||||
If you're on a single host, you can skip SSH, shared storage, and Traefik. Compose Farm just runs `docker compose` locally in each stack folder.
|
||||
|
||||
### Multi-host
|
||||
|
||||
- Everything above, plus:
|
||||
- Docker and Docker Compose installed on all target hosts
|
||||
- SSH key-based authentication to your hosts (ssh-agent or `cf ssh setup` dedicated key)
|
||||
- **Shared storage or synced folders** so `compose_dir` is the same path on all hosts (NFS recommended)
|
||||
- **Docker networks**: External networks must exist on all hosts (use `cf init-network` to create)
|
||||
- **Optional for ingress**: Traefik file provider for cross-host routing (labels + published ports, generated via `cf traefik-file`)
|
||||
|
||||
For multi-host setups, Compose Farm assumes your compose files are accessible at the same path on all hosts. This is typically achieved via:
|
||||
|
||||
- **NFS mount** (e.g., `/opt/compose` mounted from a NAS)
|
||||
- **Synced folders** (e.g., Syncthing, rsync)
|
||||
@@ -225,6 +242,24 @@ The keys will persist across restarts.
|
||||
|
||||
Create `~/.config/compose-farm/compose-farm.yaml` (or `./compose-farm.yaml` in your working directory):
|
||||
|
||||
### Single-host example
|
||||
|
||||
No SSH, shared storage, or Traefik file-provider required.
|
||||
|
||||
```yaml
|
||||
compose_dir: /opt/stacks
|
||||
|
||||
hosts:
|
||||
local: localhost # Run locally without SSH
|
||||
|
||||
services:
|
||||
plex: local
|
||||
jellyfin: local
|
||||
traefik: local
|
||||
```
|
||||
|
||||
### Multi-host example
|
||||
|
||||
```yaml
|
||||
compose_dir: /opt/compose # Must be the same path on all hosts
|
||||
|
||||
@@ -235,20 +270,20 @@ hosts:
|
||||
server-2:
|
||||
address: 192.168.1.11
|
||||
# user defaults to current user
|
||||
local: localhost # Run locally without SSH
|
||||
|
||||
services:
|
||||
plex: server-1
|
||||
jellyfin: server-2
|
||||
sonarr: server-1
|
||||
radarr: local # Runs on the machine where you invoke compose-farm
|
||||
|
||||
# Multi-host services (run on multiple/all hosts)
|
||||
autokuma: all # Runs on ALL configured hosts
|
||||
dozzle: [server-1, server-2] # Explicit list of hosts
|
||||
```
|
||||
|
||||
Compose files are expected at `{compose_dir}/{service}/compose.yaml` (also supports `compose.yml`, `docker-compose.yml`, `docker-compose.yaml`).
|
||||
For cross-host HTTP routing, add Traefik labels to your compose files and set `traefik_file` so Compose Farm can generate the file-provider config.
|
||||
|
||||
Each entry in `services:` maps to a folder under `compose_dir` that contains a compose file. Compose files are expected at `{compose_dir}/{service}/compose.yaml` (also supports `compose.yml`, `docker-compose.yml`, `docker-compose.yaml`).
|
||||
|
||||
### Multi-Host Services
|
||||
|
||||
@@ -303,7 +338,7 @@ The CLI is available as both `compose-farm` and the shorter `cf` alias.
|
||||
| `cf up <svc>` | Start service (auto-migrates if host changed) |
|
||||
| `cf down <svc>` | Stop service |
|
||||
| `cf restart <svc>` | down + up |
|
||||
| `cf update <svc>` | pull + down + up |
|
||||
| `cf update <svc>` | pull + build + down + up |
|
||||
| `cf pull <svc>` | Pull latest images |
|
||||
| `cf logs -f <svc>` | Follow logs |
|
||||
| `cf ps` | Show status of all services |
|
||||
@@ -336,11 +371,11 @@ cf pull --all
|
||||
# Restart (down + up)
|
||||
cf restart plex
|
||||
|
||||
# Update (pull + down + up) - the end-to-end update command
|
||||
# Update (pull + build + down + up) - the end-to-end update command
|
||||
cf update --all
|
||||
|
||||
# Update state from reality (discovers running services + captures digests)
|
||||
cf refresh # updates state.yaml and dockerfarm-log.toml
|
||||
cf refresh # updates compose-farm-state.yaml and dockerfarm-log.toml
|
||||
cf refresh --dry-run # preview without writing
|
||||
|
||||
# Validate config, traefik labels, mounts, and networks
|
||||
@@ -1024,6 +1059,8 @@ This makes the config truly declarative: comment out a service, run `cf apply`,
|
||||
|
||||
## Traefik Multihost Ingress (File Provider)
|
||||
|
||||
If everything runs on one host, you can skip this section and rely on Traefik's Docker provider.
|
||||
|
||||
If you run a single Traefik instance on one "front‑door" host and want it to route to
|
||||
Compose Farm services on other hosts, Compose Farm can generate a Traefik file‑provider
|
||||
fragment from your existing compose labels.
|
||||
|
||||
@@ -115,7 +115,7 @@ cli/
|
||||
└─► Validate service exists
|
||||
|
||||
2. Check state
|
||||
└─► Load state.yaml
|
||||
└─► Load compose-farm-state.yaml
|
||||
└─► Is plex already running?
|
||||
└─► Is it on a different host? (migration needed)
|
||||
|
||||
@@ -135,7 +135,7 @@ cli/
|
||||
└─► Run: docker compose up -d
|
||||
|
||||
6. Update state
|
||||
└─► Write new state to state.yaml
|
||||
└─► Write new state to compose-farm-state.yaml
|
||||
|
||||
7. Generate Traefik config (if configured)
|
||||
└─► Regenerate traefik file-provider
|
||||
@@ -232,7 +232,7 @@ Syncs state with reality by querying Docker on each host:
|
||||
docker ps --format '{{.Names}}'
|
||||
```
|
||||
|
||||
Updates state.yaml to match what's actually running.
|
||||
Updates compose-farm-state.yaml to match what's actually running.
|
||||
|
||||
## Compose File Discovery
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ cf apply
|
||||
|
||||
## Shared Storage
|
||||
|
||||
Shared storage is only required for multi-host deployments.
|
||||
|
||||
### NFS Best Practices
|
||||
|
||||
```bash
|
||||
@@ -297,7 +299,7 @@ http:
|
||||
|------|----------|--------|
|
||||
| Compose Farm config | `~/.config/compose-farm/` | Git or copy |
|
||||
| Compose files | `/opt/compose/` | Git |
|
||||
| State file | `~/.config/compose-farm/state.yaml` | Optional (can refresh) |
|
||||
| State file | `~/.config/compose-farm/compose-farm-state.yaml` | Optional (can refresh) |
|
||||
| App data | `/opt/appdata/` | Backup solution |
|
||||
|
||||
### Disaster Recovery
|
||||
@@ -341,15 +343,6 @@ cf ssh status # Check key status
|
||||
cf ssh setup # Re-setup keys
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
|
||||
For more verbose output:
|
||||
|
||||
```bash
|
||||
# See exact commands being run
|
||||
cf --verbose up myservice
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### SSH Keys
|
||||
|
||||
@@ -14,7 +14,7 @@ The Compose Farm CLI is available as both `compose-farm` and the shorter alias `
|
||||
| | `up` | Start services |
|
||||
| | `down` | Stop services |
|
||||
| | `restart` | Restart services (down + up) |
|
||||
| | `update` | Update services (pull + down + up) |
|
||||
| | `update` | Update services (pull + build + down + up) |
|
||||
| | `pull` | Pull latest images |
|
||||
| **Monitoring** | `ps` | Show service status |
|
||||
| | `logs` | Show service logs |
|
||||
|
||||
@@ -27,10 +27,29 @@ Or set the environment variable:
|
||||
export CF_CONFIG=/path/to/config.yaml
|
||||
```
|
||||
|
||||
## Full Example
|
||||
## Examples
|
||||
|
||||
### Single host (local-only)
|
||||
|
||||
```yaml
|
||||
# Required: directory containing compose files
|
||||
compose_dir: /opt/stacks
|
||||
|
||||
# Define local host
|
||||
hosts:
|
||||
local: localhost
|
||||
|
||||
# Map services to the local host
|
||||
services:
|
||||
plex: local
|
||||
sonarr: local
|
||||
radarr: local
|
||||
```
|
||||
|
||||
### Multi-host (full example)
|
||||
|
||||
```yaml
|
||||
# Required: directory containing compose files (same path on all hosts)
|
||||
compose_dir: /opt/compose
|
||||
|
||||
# Optional: auto-regenerate Traefik config
|
||||
@@ -45,7 +64,6 @@ hosts:
|
||||
hp:
|
||||
address: 192.168.1.11
|
||||
user: admin
|
||||
local: localhost
|
||||
|
||||
# Map services to hosts
|
||||
services:
|
||||
@@ -53,7 +71,6 @@ services:
|
||||
plex: nuc
|
||||
sonarr: nuc
|
||||
radarr: hp
|
||||
jellyfin: local
|
||||
|
||||
# Multi-host services
|
||||
dozzle: all # Run on ALL hosts
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Update Demo
|
||||
# Shows updating services (pull + down + up)
|
||||
# Shows updating services (pull + build + down + up)
|
||||
|
||||
Output docs/assets/update.gif
|
||||
Output docs/assets/update.webm
|
||||
|
||||
@@ -4,16 +4,22 @@ icon: lucide/rocket
|
||||
|
||||
# Getting Started
|
||||
|
||||
This guide walks you through installing Compose Farm and setting up your first multi-host deployment.
|
||||
This guide walks you through installing Compose Farm and setting up your first deployment (single host or multi-host).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have:
|
||||
### Single host
|
||||
|
||||
- **[uv](https://docs.astral.sh/uv/)** (recommended) or Python 3.11+
|
||||
- **SSH key-based authentication** to your Docker hosts
|
||||
- **Docker and Docker Compose** installed
|
||||
- **One folder per stack** under `compose_dir`
|
||||
|
||||
### Multi-host
|
||||
|
||||
- Everything above, plus:
|
||||
- **Docker and Docker Compose** installed on all target hosts
|
||||
- **Shared storage** for compose files (NFS, Syncthing, etc.)
|
||||
- **SSH key-based authentication** to your Docker hosts (ssh-agent or `cf ssh setup` key)
|
||||
- **Shared storage or sync** for compose files (NFS, Syncthing, etc.)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -65,6 +71,8 @@ cf --help
|
||||
|
||||
Compose Farm uses SSH to run commands on remote hosts. You need passwordless SSH access.
|
||||
|
||||
Skip this section if you're running a single host with `localhost`.
|
||||
|
||||
### Option 1: SSH Agent (default)
|
||||
|
||||
If you already have SSH keys loaded in your agent:
|
||||
@@ -93,6 +101,8 @@ This creates `~/.ssh/compose-farm/id_ed25519` and copies the public key to each
|
||||
|
||||
## Shared Storage Setup
|
||||
|
||||
This section applies to multi-host deployments only.
|
||||
|
||||
Compose files must be accessible at the **same path** on all hosts. Common approaches:
|
||||
|
||||
### NFS Mount
|
||||
@@ -125,6 +135,23 @@ nas:/volume1/compose /opt/compose nfs defaults 0 0
|
||||
|
||||
Create `~/.config/compose-farm/compose-farm.yaml`:
|
||||
|
||||
#### Single host example
|
||||
|
||||
```yaml
|
||||
# Where compose files are located (one folder per stack)
|
||||
compose_dir: /opt/stacks
|
||||
|
||||
hosts:
|
||||
local: localhost
|
||||
|
||||
services:
|
||||
plex: local
|
||||
sonarr: local
|
||||
radarr: local
|
||||
```
|
||||
|
||||
#### Multi-host example
|
||||
|
||||
```yaml
|
||||
# Where compose files are located (same path on all hosts)
|
||||
compose_dir: /opt/compose
|
||||
@@ -137,16 +164,19 @@ hosts:
|
||||
hp:
|
||||
address: 192.168.1.11
|
||||
# user defaults to current user
|
||||
local: localhost # Run locally without SSH
|
||||
|
||||
# Map services to hosts
|
||||
services:
|
||||
plex: nuc
|
||||
sonarr: nuc
|
||||
radarr: hp
|
||||
jellyfin: local
|
||||
jellyfin: hp
|
||||
```
|
||||
|
||||
Each entry in `services:` maps to a folder under `compose_dir` that contains a compose file.
|
||||
|
||||
For cross-host HTTP routing, add Traefik labels and configure `traefik_file` (see [Traefik Integration](traefik.md)).
|
||||
|
||||
### Validate Configuration
|
||||
|
||||
```bash
|
||||
@@ -276,7 +306,7 @@ cf apply
|
||||
|
||||
```bash
|
||||
cf update --all
|
||||
# Runs: pull + down + up for each service
|
||||
# Runs: pull + build + down + up for each service
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -11,6 +11,7 @@ A minimal CLI tool to run Docker Compose commands across multiple hosts via SSH.
|
||||
Compose Farm lets you manage Docker Compose services across multiple machines from a single command line. Think [Dockge](https://dockge.kuma.pet/) but with a CLI and web interface, designed for multi-host deployments.
|
||||
|
||||
Define which services run where in one YAML file, then use `cf apply` to make reality match your configuration.
|
||||
It also works great on a single host with one folder per stack; just map services to `localhost`.
|
||||
|
||||
## Quick Demo
|
||||
|
||||
@@ -31,6 +32,31 @@ Define which services run where in one YAML file, then use `cf apply` to make re
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Single host
|
||||
|
||||
No SSH, shared storage, or Traefik file-provider required.
|
||||
|
||||
```yaml
|
||||
# compose-farm.yaml
|
||||
compose_dir: /opt/stacks
|
||||
|
||||
hosts:
|
||||
local: localhost
|
||||
|
||||
services:
|
||||
plex: local
|
||||
jellyfin: local
|
||||
traefik: local
|
||||
```
|
||||
|
||||
```bash
|
||||
cf apply # Start/stop services to match config
|
||||
```
|
||||
|
||||
### Multi-host
|
||||
|
||||
Requires SSH plus a shared `compose_dir` path on all hosts (NFS or sync).
|
||||
|
||||
```yaml
|
||||
# compose-farm.yaml
|
||||
compose_dir: /opt/compose
|
||||
@@ -51,6 +77,10 @@ services:
|
||||
cf apply # Services start, migrate, or stop as needed
|
||||
```
|
||||
|
||||
Each entry in `services:` maps to a folder under `compose_dir` that contains a compose file.
|
||||
|
||||
For cross-host HTTP routing, add Traefik labels and configure `traefik_file` to generate file-provider config.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
@@ -110,10 +140,19 @@ cf logs -f plex
|
||||
|
||||
## Requirements
|
||||
|
||||
### Single host
|
||||
|
||||
- [uv](https://docs.astral.sh/uv/) (recommended) or Python 3.11+
|
||||
- SSH key-based authentication to your Docker hosts
|
||||
- Docker and Docker Compose on all target hosts
|
||||
- Shared storage (compose files at same path on all hosts)
|
||||
- Docker and Docker Compose installed
|
||||
- One folder per stack under `compose_dir`
|
||||
|
||||
### Multi-host
|
||||
|
||||
- Everything above, plus:
|
||||
- Docker and Docker Compose installed on all target hosts
|
||||
- SSH key-based authentication to your Docker hosts (ssh-agent or `cf ssh setup` key)
|
||||
- Shared storage or sync so `compose_dir` is the same path on all hosts
|
||||
- Optional for ingress: Traefik file provider (labels + published ports)
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ icon: lucide/globe
|
||||
|
||||
Compose Farm can generate Traefik file-provider configuration for routing traffic across multiple hosts.
|
||||
|
||||
If everything runs on a single host, you can skip this and rely on Traefik's Docker provider.
|
||||
|
||||
## The Problem
|
||||
|
||||
When you run Traefik on one host but services on others, Traefik's docker provider can't see remote containers. The file provider bridges this gap.
|
||||
|
||||
Reference in New Issue
Block a user