Clarify single-host vs multi-host docs (#73)

This commit is contained in:
Bas Nijholt
2025-12-20 14:15:43 -08:00
committed by GitHub
parent 187f83b61d
commit fff064cf03
9 changed files with 158 additions and 40 deletions

View File

@@ -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 "frontdoor" host and want it to route to
Compose Farm services on other hosts, Compose Farm can generate a Traefik fileprovider
fragment from your existing compose labels.

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.