* restart: Match Docker Compose semantics Change `cf restart` from doing `down + up` to using `docker compose restart`, matching the Docker Compose command behavior. This provides command naming parity with Docker Compose. Users who want the old behavior can use `cf down mystack && cf up mystack`. - Update restart implementation to use `docker compose restart` - Remove traefik regeneration from restart (no longer recreates containers) - Update all documentation and help text - Remove restart from self-update SSH handling (no longer involves down) * web: Clarify Update tooltip uses 'recreate' not 'restart' Avoid confusion now that 'restart' means something different. * web: Fix Update All tooltip to use 'recreates'
8.8 KiB
icon
| icon |
|---|
| lucide/globe |
Traefik Integration
Compose Farm can generate Traefik file-provider configuration for routing traffic across multiple hosts.
The Problem
When you run Traefik on one host but stacks on others, Traefik's docker provider can't see remote containers. The file provider bridges this gap.
Internet
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Host: nuc │
│ │
│ ┌─────────┐ │
│ │ Traefik │◄─── Docker provider sees local containers │
│ │ │ │
│ │ │◄─── File provider sees remote stacks │
│ └────┬────┘ (from compose-farm.yml) │
│ │ │
└───────┼─────────────────────────────────────────────────────┘
│
├────────────────────┐
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Host: hp │ │ Host: nas │
│ │ │ │
│ plex:32400 │ │ jellyfin:8096 │
└───────────────┘ └───────────────┘
How It Works
- Your compose files have standard Traefik labels
- Compose Farm reads labels and generates file-provider config
- Traefik watches the generated file
- Traffic routes to remote stacks via host IP + published port
Setup
Step 1: Configure Traefik File Provider
Add directory watching to your Traefik config:
# traefik.yml or docker-compose.yml command
providers:
file:
directory: /opt/traefik/dynamic.d
watch: true
Or via command line:
services:
traefik:
command:
- --providers.file.directory=/dynamic.d
- --providers.file.watch=true
volumes:
- /opt/traefik/dynamic.d:/dynamic.d:ro
Step 2: Add Traefik Labels to Services
Your compose files use standard Traefik labels:
# /opt/compose/plex/docker-compose.yml
services:
plex:
image: lscr.io/linuxserver/plex
ports:
- "32400:32400" # IMPORTANT: Must publish port!
labels:
- traefik.enable=true
- traefik.http.routers.plex.rule=Host(`plex.example.com`)
- traefik.http.routers.plex.entrypoints=websecure
- traefik.http.routers.plex.tls.certresolver=letsencrypt
- traefik.http.services.plex.loadbalancer.server.port=32400
Important: Services must publish ports for cross-host routing. Traefik connects via host_ip:published_port.
Step 3: Generate File Provider Config
cf traefik-file --all -o /opt/traefik/dynamic.d/compose-farm.yml
This generates:
# /opt/traefik/dynamic.d/compose-farm.yml
http:
routers:
plex:
rule: Host(`plex.example.com`)
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: plex
services:
plex:
loadBalancer:
servers:
- url: http://192.168.1.11:32400
Auto-Regeneration
Configure automatic regeneration in compose-farm.yaml:
compose_dir: /opt/compose
traefik_file: /opt/traefik/dynamic.d/compose-farm.yml
traefik_stack: traefik
hosts:
nuc:
address: 192.168.1.10
hp:
address: 192.168.1.11
stacks:
traefik: nuc # Traefik runs here
plex: hp # Routed via file-provider
grafana: hp
With traefik_file set, these commands auto-regenerate the config:
cf upcf downcf updatecf apply
traefik_stack Option
When set, stacks on the same host as Traefik are skipped in file-provider output. Traefik's docker provider handles them directly.
traefik_stack: traefik # traefik runs on nuc
stacks:
traefik: nuc # NOT in file-provider (docker provider)
portainer: nuc # NOT in file-provider (docker provider)
plex: hp # IN file-provider (cross-host)
Label Syntax
Routers
labels:
# Basic router
- traefik.http.routers.myapp.rule=Host(`app.example.com`)
- traefik.http.routers.myapp.entrypoints=websecure
# With TLS
- traefik.http.routers.myapp.tls=true
- traefik.http.routers.myapp.tls.certresolver=letsencrypt
# With middleware
- traefik.http.routers.myapp.middlewares=auth@file
Services
labels:
# Load balancer port
- traefik.http.services.myapp.loadbalancer.server.port=8080
# Health check
- traefik.http.services.myapp.loadbalancer.healthcheck.path=/health
Middlewares
Middlewares should be defined in a separate file (not generated by Compose Farm):
# /opt/traefik/dynamic.d/middlewares.yml
http:
middlewares:
auth:
basicAuth:
users:
- "user:$apr1$..."
Reference in labels:
labels:
- traefik.http.routers.myapp.middlewares=auth@file
Variable Substitution
Labels can use environment variables:
labels:
- traefik.http.routers.myapp.rule=Host(`${DOMAIN}`)
Compose Farm resolves variables from:
- Stack's
.envfile - Current environment
# /opt/compose/myapp/.env
DOMAIN=app.example.com
Port Resolution
Compose Farm determines the target URL from published ports:
ports:
- "8080:80" # Uses 8080
- "192.168.1.11:8080:80" # Uses 8080 on specific IP
If no suitable port is found, a warning is shown.
Complete Example
compose-farm.yaml
compose_dir: /opt/compose
traefik_file: /opt/traefik/dynamic.d/compose-farm.yml
traefik_stack: traefik
hosts:
nuc:
address: 192.168.1.10
hp:
address: 192.168.1.11
nas:
address: 192.168.1.100
stacks:
traefik: nuc
plex: hp
jellyfin: nas
grafana: nuc
nextcloud: nuc
/opt/compose/plex/docker-compose.yml
services:
plex:
image: lscr.io/linuxserver/plex
container_name: plex
ports:
- "32400:32400"
labels:
- traefik.enable=true
- traefik.http.routers.plex.rule=Host(`plex.example.com`)
- traefik.http.routers.plex.entrypoints=websecure
- traefik.http.routers.plex.tls.certresolver=letsencrypt
- traefik.http.services.plex.loadbalancer.server.port=32400
# ... other config
Generated compose-farm.yml
http:
routers:
plex:
rule: Host(`plex.example.com`)
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: plex
jellyfin:
rule: Host(`jellyfin.example.com`)
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: jellyfin
services:
plex:
loadBalancer:
servers:
- url: http://192.168.1.11:32400
jellyfin:
loadBalancer:
servers:
- url: http://192.168.1.100:8096
Note: grafana and nextcloud are NOT in the file because they're on the same host as Traefik (nuc).
Combining with Existing Config
If you have existing Traefik dynamic config:
# Move existing config to directory
mkdir -p /opt/traefik/dynamic.d
mv /opt/traefik/dynamic.yml /opt/traefik/dynamic.d/manual.yml
# Generate Compose Farm config
cf traefik-file --all -o /opt/traefik/dynamic.d/compose-farm.yml
# Update Traefik to watch directory
# --providers.file.directory=/dynamic.d
Traefik merges all YAML files in the directory.
Troubleshooting
Stack Not Accessible
-
Check port is published:
ports: - "8080:80" # Must be published, not just exposed -
Check label syntax:
cf check mystack -
Verify generated config:
cf traefik-file mystack -
Check Traefik logs:
docker logs traefik
Config Not Regenerating
-
Verify traefik_file is set:
cf config show | grep traefik -
Check file permissions:
ls -la /opt/traefik/dynamic.d/ -
Manually regenerate:
cf traefik-file --all -o /opt/traefik/dynamic.d/compose-farm.yml
Variable Not Resolved
-
Check .env file exists:
cat /opt/compose/myservice/.env -
Test variable resolution:
cd /opt/compose/myservice docker compose config