diff --git a/README.md b/README.md index e19a6bf..5c65650 100644 --- a/README.md +++ b/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. + - [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 ` | Start service (auto-migrates if host changed) | | `cf down ` | Stop service | | `cf restart ` | down + up | -| `cf update ` | pull + down + up | +| `cf update ` | pull + build + down + up | | `cf pull ` | Pull latest images | | `cf logs -f ` | 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. diff --git a/docs/architecture.md b/docs/architecture.md index 0c9ff1b..a4b0876 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -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 diff --git a/docs/best-practices.md b/docs/best-practices.md index 90ee4e2..3f9c245 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -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 diff --git a/docs/commands.md b/docs/commands.md index 8c9bc5a..d50131b 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -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 | diff --git a/docs/configuration.md b/docs/configuration.md index b3ce7a4..6a4a0b6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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 diff --git a/docs/demos/update.tape b/docs/demos/update.tape index f951532..4f23406 100644 --- a/docs/demos/update.tape +++ b/docs/demos/update.tape @@ -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 diff --git a/docs/getting-started.md b/docs/getting-started.md index 80b73ee..15e1abd 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -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 diff --git a/docs/index.md b/docs/index.md index f1f3a25..577fce4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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 diff --git a/docs/traefik.md b/docs/traefik.md index f41818d..d213975 100644 --- a/docs/traefik.md +++ b/docs/traefik.md @@ -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.