# Compose Farm Examples

Real-world examples demonstrating compose-farm patterns for multi-host Docker deployments.

## Services

| Service | Type | Demonstrates |
|---------|------|--------------|
| [traefik](traefik/) | Infrastructure | Reverse proxy, Let's Encrypt, file-provider |
| [mealie](mealie/) | Single container | Traefik labels, resource limits, environment vars |
| [uptime-kuma](uptime-kuma/) | Single container | Docker socket, user mapping, custom DNS |
| [paperless-ngx](paperless-ngx/) | Multi-container | Redis + App stack (SQLite) |
| [autokuma](autokuma/) | Multi-host | Demonstrates `all` keyword (runs on every host) |

## Key Patterns

### External Network

All services connect to a shared external network for inter-service communication:

```yaml
networks:
  mynetwork:
    external: true
```

Create it on each host with consistent settings:

```bash
compose-farm init-network --network mynetwork --subnet 172.20.0.0/16
```

### Traefik Labels (Dual Routes)

Services expose two routes for different access patterns:

1. **HTTPS route** (`websecure` entrypoint): For your custom domain with Let's Encrypt TLS
2. **HTTP route** (`web` entrypoint): For `.local` domains on your LAN (no TLS needed)

This pattern allows accessing services via:
- `https://mealie.example.com` - from anywhere, with TLS
- `http://mealie.local` - from your local network, no TLS overhead

```yaml
labels:
  # HTTPS route for custom domain (e.g., mealie.example.com)
  - traefik.enable=true
  - traefik.http.routers.myapp.rule=Host(`myapp.${DOMAIN}`)
  - traefik.http.routers.myapp.entrypoints=websecure
  - traefik.http.services.myapp.loadbalancer.server.port=8080
  # HTTP route for .local domain (e.g., myapp.local)
  - traefik.http.routers.myapp-local.rule=Host(`myapp.local`)
  - traefik.http.routers.myapp-local.entrypoints=web
```

> **Note:** `.local` domains require local DNS (e.g., Pi-hole, Technitium) to resolve to your Traefik host.

### Environment Variables

Each service has a `.env` file for secrets and domain configuration.
Edit these files to set your domain and credentials:

```bash
# Example: set your domain
echo "DOMAIN=example.com" > mealie/.env
```

Variables like `${DOMAIN}` are substituted at runtime by Docker Compose.

### NFS Volume Mounts

All data is stored on shared NFS storage at `/mnt/data/`:

```yaml
volumes:
  - /mnt/data/myapp:/app/data
```

This allows services to migrate between hosts without data loss.

### Multi-Host Services

Services that need to run on every host (e.g., monitoring agents):

```yaml
# In compose-farm.yaml
services:
  autokuma: all  # Runs on every configured host
```

### Multi-Container Stacks

Database-backed apps with multiple services:

```yaml
services:
  redis:
    image: redis:7
  app:
    depends_on:
      - redis
```

> **NFS + PostgreSQL Warning:** PostgreSQL should NOT run on NFS storage due to
> fsync and file locking issues. Use SQLite (safe for single-writer on NFS) or
> keep PostgreSQL data on local volumes (non-migratable).

### AutoKuma Labels (Optional)

The autokuma example demonstrates compose-farm's **multi-host feature** - running the same service on all hosts using the `all` keyword. AutoKuma itself is not part of compose-farm; it's just a good example because it needs to run on every host to monitor local Docker containers.

[AutoKuma](https://github.com/BigBoot/AutoKuma) automatically creates Uptime Kuma monitors from Docker labels:

```yaml
labels:
  - kuma.myapp.http.name=My App
  - kuma.myapp.http.url=https://myapp.${DOMAIN}
```

## Quick Start

```bash
cd examples

# 1. Create the shared network on all hosts
compose-farm init-network

# 2. Start Traefik first (the reverse proxy)
compose-farm up traefik

# 3. Start other services
compose-farm up mealie uptime-kuma

# 4. Check status
compose-farm ps

# 5. Generate Traefik file-provider config for cross-host routing
compose-farm traefik-file --all

# 6. View logs
compose-farm logs mealie

# 7. Stop everything
compose-farm down --all
```

## Configuration

The `compose-farm.yaml` shows a multi-host setup:

- **primary** (192.168.1.10): Runs Traefik and heavy services
- **secondary** (192.168.1.11): Runs lighter services
- **autokuma**: Runs on ALL hosts to monitor local containers

When Traefik runs on `primary` and a service runs on `secondary`, compose-farm
automatically generates file-provider config so Traefik can route to it.

## Traefik File-Provider

When services run on different hosts than Traefik, use `traefik-file` to generate routing config:

```bash
# Generate config for all services
compose-farm traefik-file --all -o traefik/dynamic.d/compose-farm.yml

# Or configure auto-generation in compose-farm.yaml:
traefik_file: /opt/stacks/traefik/dynamic.d/compose-farm.yml
traefik_service: traefik
```

With `traefik_file` configured, compose-farm automatically regenerates the config after `up`, `down`, `restart`, and `update` commands.
