From e769526b139d80ba26c51cb192e6ae0ed46107d4 Mon Sep 17 00:00:00 2001 From: Bill Ballou Date: Tue, 30 Dec 2025 16:39:42 -0500 Subject: [PATCH] Initial commit: docker-compose-config skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add skill for Docker Compose configuration management with: - Multi-host environment structure with per-hostname overrides - External volume mount patterns with env variable substitution - Reverse proxy network configuration for SWAG integration - Reference docs for examples and network topology 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- SKILL.md | 168 +++++++++++++++++++++++++++++ references/examples.md | 238 +++++++++++++++++++++++++++++++++++++++++ references/networks.md | 178 ++++++++++++++++++++++++++++++ 3 files changed, 584 insertions(+) create mode 100644 SKILL.md create mode 100644 references/examples.md create mode 100644 references/networks.md diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..276c3a9 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,168 @@ +--- +name: docker-compose-config +description: Docker Compose configuration management for multi-host server deployments. Use when creating, modifying, or managing docker-compose.yml files with environment-specific configurations, external volume mounts, and reverse proxy networks. Triggers on tasks involving Docker Compose files, environment overrides, multi-host deployments, or service configuration for self-hosted applications. +--- + +# Docker Compose Configuration Management + +This skill provides guidance for managing Docker Compose configurations across multiple server environments with per-host overrides. + +## Directory Structure + +Each project follows this structure: + +``` +/docker/config/ +├── / +│ ├── docker-compose.yml # Main service definitions +│ ├── docker-compose.override.yml # Current host overrides (gitignored) +│ ├── .env # Environment variables (gitignored) +│ └── environments/ +│ └── / # Per-host configs +│ ├── .env +│ └── docker-compose.override..yml +``` + +## Compose File Conventions + +### Service Definition Pattern + +```yaml +name: + +services: + : + container_name: + image: /:@sha256: + environment: + PUID: ${UID} + PGID: ${GID} + # Service-specific vars use env substitution + VAR_NAME: ${VAR_NAME} + volumes: + - ${DATA_PATH}:/app/data + - /etc/localtime:/etc/localtime:ro + ports: + - ${SERVICE_PORT:-default}:internal_port + restart: unless-stopped + networks: + - default + - reverse_proxy + +networks: + reverse_proxy: + name: reverse_proxy + attachable: true +``` + +### Key Patterns + +1. **Image pinning**: Use SHA256 digests for reproducibility + ```yaml + image: ghcr.io/org/image:v1.0.0@sha256:abc123... + ``` + +2. **Port defaults**: Always provide defaults for ports + ```yaml + ports: + - ${APP_PORT:-8080}:8080 + ``` + +3. **Volume mounts**: Use environment variables for paths + ```yaml + volumes: + - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${DB_DATA_LOCATION}/data:/var/lib/postgresql/data + ``` + +4. **Proxy network**: Services needing reverse proxy access join `reverse_proxy` network + ```yaml + networks: + - default + - reverse_proxy + + networks: + reverse_proxy: + name: reverse_proxy + attachable: true + ``` + +## Environment File Conventions + +### Structure (.env) + +```bash +# Permissions +UID=1000 +GID=1000 + +# Paths +UPLOAD_LOCATION=/mnt/user/pictures +DB_DATA_LOCATION=/mnt/user/appdata/ + +# Ports +APP_PORT=8080 + +# Secrets +DB_PASSWORD= + +# Database +DB_HOSTNAME=database +DB_USERNAME=postgres +DB_DATABASE_NAME= +``` + +### Per-Host Variations + +- `environments//.env` overrides paths for specific hosts +- Common overrides: mount paths (`/mnt/user/` vs `/mnt/main/`), ports + +## Common Commands + +```bash +# Start a project +docker compose -f /docker-compose.yml up -d + +# Start with host-specific override +docker compose -f /docker-compose.yml \ + -f /environments//docker-compose.override..yml up -d + +# View logs +docker compose -f /docker-compose.yml logs -f + +# Stop services +docker compose -f /docker-compose.yml down +``` + +## Adding a New Host Environment + +1. Create `environments//` directory +2. Copy existing host's `.env` as template +3. Update paths and port mappings for the host +4. Create override compose file if device mappings differ + +## Service Dependencies + +Use `depends_on` with health checks for proper startup order: + +```yaml +depends_on: + database: + condition: service_healthy +``` + +## Healthchecks + +```yaml +healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/"] + start_period: 5m + interval: 30s + timeout: 20s + retries: 10 +``` + +## References + +- See [examples.md](references/examples.md) for complete service configurations +- See [networks.md](references/networks.md) for network topology details diff --git a/references/examples.md b/references/examples.md new file mode 100644 index 0000000..577a5a4 --- /dev/null +++ b/references/examples.md @@ -0,0 +1,238 @@ +# Docker Compose Configuration Examples + +## Multi-Service Application (Immich-style) + +Complete example with app server, machine learning, Redis, and PostgreSQL: + +```yaml +name: immich + +services: + immich-server: + container_name: immich_server + image: ghcr.io/immich-app/immich-server:v2.3.1 + environment: + DB_USERNAME: ${DB_USERNAME} + DB_PASSWORD: ${DB_PASSWORD} + DB_DATABASE_NAME: ${DB_DATABASE_NAME} + REDIS_HOSTNAME: immich_redis + volumes: + - ${UPLOAD_LOCATION}:/usr/src/app/upload + - /etc/localtime:/etc/localtime:ro + ports: + - ${APP_PORT:-2283}:2283 + depends_on: + - redis + - database + restart: always + networks: + - default + - reverse_proxy + + immich-machine-learning: + container_name: immich_machine_learning + image: ghcr.io/immich-app/immich-machine-learning:v2.3.1 + volumes: + - model-cache:/cache + env_file: + - .env + restart: always + networks: + - default + + redis: + container_name: immich_redis + image: registry.redict.io/redict:7.3.6@sha256:2a99f322eed7... + restart: always + volumes: + - redis-data:/data + networks: + - default + + database: + container_name: immich_postgres + image: ghcr.io/immich-app/postgres:16-vectorchord0.3.0@sha256:5b434f184ec... + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' + volumes: + - ${DB_DATA_LOCATION}/data:/var/lib/postgresql/data + restart: always + networks: + - default + +volumes: + model-cache: + redis-data: + +networks: + reverse_proxy: + name: reverse_proxy + attachable: true +``` + +## Reverse Proxy Service (SWAG-style) + +```yaml +services: + swag: + image: lscr.io/linuxserver/swag:5.2.2@sha256:c8afbd137c2f... + container_name: swag + cap_add: + - NET_ADMIN + environment: + - PUID=${PUID} + - PGID=${PGID} + - TZ=America/New_York + - URL=${SWAG_URL} + - VALIDATION=dns + - SUBDOMAINS=wildcard + - DNSPLUGIN=cloudflare + - PROPAGATION=20 + - EMAIL=user@domain.com + - ONLY_SUBDOMAINS=${ONLY_SUBDOMAINS:-false} + - EXTRA_DOMAINS=${EXTRA_DOMAINS:-} + - STAGING=false + volumes: + - ${SWAG_DATA_PATH}:/config + ports: + - 1443:443 + - 81:80 + restart: unless-stopped + networks: + - default + - reverse_proxy + +networks: + reverse_proxy: + name: reverse_proxy + attachable: true +``` + +## Media Server with GPU (Jellyfin-style) + +```yaml +services: + jellyfin: + image: lscr.io/linuxserver/jellyfin:10.11.4@sha256:234ea8d508b2... + container_name: jellyfin + runtime: nvidia + environment: + PUID: ${UID} + PGID: ${GID} + UMASK: "022" + JELLYFIN_PublishedServerUrl: ${HOST_IP} + NVIDIA_VISIBLE_DEVICES: all + ports: + - ${JELLYFIN_PORT:-8096}:8096 + - ${JELLYFIN_LOCAL_PORT:-7359}:7359 + - ${JELLYFIN_DNLA_PORT:-1900}:1900 + volumes: + - ${MEDIA_PATH}:/data/media + - ${BASE_SERVER_PATH}/jellyfin:/config + - ${TRANSCODE_PATH}jellyfin:/config/cache/transcodes + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8096/"] + start_period: 5m + interval: 30s + timeout: 20s + retries: 10 +``` + +## Service with VPN (Deluge-style) + +```yaml +services: + deluge: + image: binhex/arch-delugevpn:latest@sha256:2ff474cba3af... + container_name: deluge + cap_add: + - NET_ADMIN + environment: + PUID: ${UID} + PGID: ${GID} + UMASK: "000" + NAME_SERVERS: ${VPN_NAME_SERVERS} + DEBUG: false + env_file: + - .env + volumes: + - ${SEEDBOX_PATH}:/data + - ${BASE_SERVER_PATH}/deluge:/config + ports: + - ${DELUGE_PORT_1:-8112}:8112 + - ${DELUGE_PORT_2:-58846}:58846 +``` + +## Database with Healthcheck (PostgreSQL-style) + +```yaml +services: + database: + image: postgres:18.1@sha256:5ec39c188013... + container_name: app-db + restart: unless-stopped + environment: + POSTGRES_USER: ${DB_USER:-appuser} + POSTGRES_PASSWORD: ${DB_PASS} + ports: + - ${DB_PORT:-5432}:5432 + volumes: + - ${BASE_SERVER_PATH}/app/data:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -d postgres -U $${DB_USER:-appuser}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s +``` + +## Environment File Examples + +### Main .env + +```bash +# Permissions +UID=1000 +GID=1000 + +# Paths +UPLOAD_LOCATION=/mnt/main/pictures +DB_DATA_LOCATION=/docker/appdata/immich + +# Ports +APP_PORT=2283 + +# Secrets +DB_PASSWORD=GeneratedSecurePassword123 + +# Database +DB_HOSTNAME=database +DB_USERNAME=postgres +DB_DATABASE_NAME=immich +``` + +### Host-specific .env (environments/mabel/.env) + +```bash +# Permissions (same across hosts) +UID=1000 +GID=1000 + +# Paths (host-specific mount points) +UPLOAD_LOCATION=/mnt/user/pictures +DB_DATA_LOCATION=/mnt/user/appdata/immich + +# Ports (may vary by host) +APP_PORT=2283 + +# Secrets (same across hosts) +DB_PASSWORD=GeneratedSecurePassword123 + +# Database +DB_HOSTNAME=database +DB_USERNAME=postgres +DB_DATABASE_NAME=immich +``` diff --git a/references/networks.md b/references/networks.md new file mode 100644 index 0000000..41872bb --- /dev/null +++ b/references/networks.md @@ -0,0 +1,178 @@ +# Docker Compose Network Configuration + +## Network Topology + +``` +┌─────────────────────────────────────────────────────────────┐ +│ reverse_proxy network │ +│ (attachable, shared across all projects) │ +│ │ +│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ SWAG │ │ immich-srv │ │ grist-srv │ ... │ +│ │ :443/80 │ │ :2283 │ │ :8484 │ │ +│ └─────────┘ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ │ │ + │ │ │ + External Internal Internal + Traffic Services Services + +┌───────────────────┐ ┌───────────────────┐ +│ immich_default │ │ grist_default │ +│ │ │ │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ immich-ml │ │ │ │ grist │ │ +│ └─────────────┘ │ │ └─────────────┘ │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ redis │ │ │ │ postgres │ │ +│ └─────────────┘ │ │ └─────────────┘ │ +│ ┌─────────────┐ │ └───────────────────┘ +│ │ postgres │ │ +│ └─────────────┘ │ +└───────────────────┘ +``` + +## Network Types + +### 1. Project Default Network + +Automatically created by Compose. Services within same project communicate here. + +```yaml +services: + app: + networks: + - default # Implicit, connects to _default + + database: + networks: + - default # Same network, can reach 'app' by service name +``` + +### 2. Reverse Proxy Network + +Shared external network for services needing reverse proxy access. + +```yaml +services: + web-app: + networks: + - default # Internal communication + - reverse_proxy # Accessible by SWAG + +networks: + reverse_proxy: + name: reverse_proxy + attachable: true +``` + +### 3. Host Network Mode + +For services requiring direct host network access (e.g., Plex discovery): + +```yaml +services: + plex: + network_mode: host + # No 'ports' mapping needed - uses host ports directly +``` + +## Network Configuration Patterns + +### Standard Web Service + +```yaml +services: + myapp: + networks: + - default + - reverse_proxy + +networks: + reverse_proxy: + name: reverse_proxy + attachable: true +``` + +### Internal-Only Service (Database) + +```yaml +services: + postgres: + networks: + - default + # No reverse_proxy - not externally accessible +``` + +### Service with Multiple Networks + +```yaml +services: + api-gateway: + networks: + - default + - reverse_proxy + - monitoring + +networks: + reverse_proxy: + name: reverse_proxy + attachable: true + monitoring: + name: monitoring + attachable: true +``` + +## Service Discovery + +Services on the same network can reach each other by container name: + +```yaml +services: + app: + container_name: myapp_server + environment: + REDIS_HOST: myapp_redis # Uses container_name + DB_HOST: myapp_postgres + + redis: + container_name: myapp_redis + + database: + container_name: myapp_postgres +``` + +## External Network Declaration + +When connecting to pre-existing external networks: + +```yaml +networks: + reverse_proxy: + external: true + name: reverse_proxy +``` + +vs creating if not exists: + +```yaml +networks: + reverse_proxy: + name: reverse_proxy + attachable: true +``` + +## SWAG Proxy Configuration + +Services on `reverse_proxy` network are accessible to SWAG at their container name and internal port: + +| Service | Container Name | Internal URL | +|---------|---------------|--------------| +| Immich | immich_server | http://immich_server:2283 | +| Grist | grist | http://grist:8484 | + +SWAG nginx config example: +```nginx +upstream_app immich_server; +upstream_port 2283; +```