delorenj/skills

stacks-deploy

Generate Docker stack deployment entries for the stacks monorepo at ~/docker/stacks/. Use when: (1) deploying a new containerized service to the homelab, (2) restructuring an existing stack entry, (3) migrating a git repo's compose file to deployment-only, (4) adding Traefik reverse proxy routing for a service at *.delo.sh, (5) integrating a service into the top-level aggregator compose.yml.

First seen May 18, 2026

Installation

$ npx skills add delorenj/skills --skill stacks-deploy

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from delorenj/skills · top by installs.

npx skills add delorenj/skills

Browse all from delorenj/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 15
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,290 B
  • docs SUMMARY.md 415 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 5 installs

SKILL.md

stacks-deploy

Generates minimal deployment stack entries from containerized git repos. Separates build concerns (git repo) from deployment topology (stacks monorepo) behind the delo.sh Traefik proxy.

Invariants

  • No source code in stacks. Config files, scripts, and .env only.
  • No submodules. Links between repos are image references + mise tasks.
  • No build: in stacks compose files. Everything uses image:.
  • Runtime data lives at /home/delorenj/data/<service>/, not in stacks or git repos.
  • network_mode: host services cannot join the proxy network. Use a companion service for Traefik routing.
  • Secrets resolve from 1Password at deploy time, never live in the repo. Commit .env.template with op://... references; resolve via ~/docker/stacks/_lib/op-inject.sh (see references/secret-injection.md). .env is always gitignored.

Workflow

Step 1: Analyze Source Repo

Read the source repo's compose file(s), Dockerfile(s), and any deployment docs.

Identify for each service:

  • Does it use build: or image:?
  • What ports does it expose?
  • What volumes does it need? (config files vs runtime data vs source mounts)
  • Does it need special networking? (network_mode: host, privileged, GPU passthrough)
  • Which service(s) are web-facing (need Traefik labels)?

Step 2: Classify Deployment Mode

Mode When Example
Image reference Custom code, needs build: in source repo ssbnk-watcher
Config-only Public base image + mounted configs ssbnk-web (nginx:alpine)
Hybrid Mix of custom and public images ssbnk (watcher + nginx + alpine)

For services with custom images, the source repo must publish the image (Docker Hub, GHCR, or local tag).

Step 3: Extract Deployment Artifacts

From the source repo, identify files needed at deploy time:

Copy to stacks entry:

  • Config files mounted into containers (nginx confs, prometheus configs, etc.)
  • Scripts mounted as volumes (cron scripts, entrypoints)
  • .env.example as template for .env

Do NOT copy:

  • Source code, Dockerfiles, test files, docs, CI configs
  • Compiled binaries, node_modules, build artifacts

Create at /home/delorenj/data/<service>/:

  • Runtime data directories (databases, uploads, caches, logs)
  • Any directory that containers write to at runtime

Step 4: Determine Stacks Category

Category Purpose Path
utils Developer tools, internal services ~/docker/stacks/utils/
websites Public-facing web applications ~/docker/stacks/websites/
ai AI/ML services, agents ~/docker/stacks/ai/
monitoring Observability, metrics, alerts ~/docker/stacks/monitoring/
persistence Databases, search engines, caches ~/docker/stacks/persistence/
media Media servers, streaming ~/docker/stacks/media/

Step 5: Generate Stack Entry

Create the directory structure:

~/docker/stacks/<category>/<service>/
  compose.yml        # image: references only
  .env               # runtime configuration
  [configs/]         # mounted config files
  [scripts/]         # mounted scripts

compose.yml Template

services:
  <service-name>:
    image: <registry>/<image>:<tag>
    container_name: <service-name>
    restart: unless-stopped
    volumes:
      - /home/delorenj/data/<service>/<subdir>:/container/path
      - ./configs/some.conf:/etc/some.conf:ro
    environment:
      - KEY=${ENV_VAR}
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.<service>.entrypoints=websecure"
      - "traefik.http.routers.<service>.rule=Host(`<service>.delo.sh`)"
      - "traefik.http.routers.<service>.tls=true"
      - "traefik.http.routers.<service>.tls.certresolver=letsencrypt"
      - "traefik.http.services.<service>.loadbalancer.server.port=<port>"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

Host Networking Pattern

When a service needs network_mode: host (clipboard, raw sockets, etc.), it CANNOT have Traefik labels or join the proxy network. Instead:

  1. The host-networked service listens on a host port (e.g., 31243)
  2. A companion web-facing service (e.g., nginx) joins the proxy network
  3. The companion proxies API routes to http://host.docker.internal:<port>
  4. The companion needs extra_hosts: ["host.docker.internal:host-gateway"]

Step 6: Apply Traefik Integration

Standard label set (see references/traefik-labels.md). Additional patterns:

  • Auth-protected: Add traefik.http.routers.<svc>.middlewares=google-auth@file
  • Multi-subdomain: Define separate routers for each subdomain
  • Custom headers: Define middleware in traefik dynamic config at ~/docker/core/traefik/traefik-data/dynamic/<service>.yml

Step 7: Update Aggregator

Each stacks category has a top-level compose.yml that uses extends:

# ~/docker/stacks/<category>/compose.yml
services:
  <service-name>:
    extends:
      file: ./<service>/compose.yml
      service: <service-name>

Limitations of extends:

  • Cannot extend services with network_mode: host or privileged: true
  • Networks must be re-declared at the aggregator level
  • Named volumes must be declared at the aggregator level

Services incompatible with extends should be noted in a comment and run standalone.

Step 8: Generate Mise Tasks in Source Repo

For services with custom images, add to the source repo's mise.toml:

[tasks.build]
description = "Build the Docker image"
run = "docker build -t <user>/<image>:latest -f <dockerfile> <context>/"

[tasks.inject]
description = "Resolve secrets from 1Password into <service>/.env"
run = """
cd ~/docker/stacks/<category>/<service>
~/docker/stacks/_lib/op-inject.sh
"""

[tasks.deploy]
description = "Re-inject secrets, pull and restart in stacks deployment"
depends = ["inject"]
run = """
cd ~/docker/stacks/<category>/<service>
docker compose pull <service-name>
docker compose up -d <service-name>
"""

The inject step is required whenever the stack uses .env.template with op://... references (preferred pattern — see references/secret-injection.md). Skip it only for stacks that legitimately have no secrets.

Reference Files

  • references/traefik-labels.md - Label templates, middleware patterns, host-networking workaround
  • references/stacks-layout.md - Monorepo structure, aggregator format, naming conventions
  • references/deployment-modes.md - Mode comparison with real examples from existing stacks
  • references/secret-injection.md - 1Password + op inject pattern. Commit .env.template, resolve .env at deploy via ~/docker/stacks/_lib/op-inject.sh. Migration recipe for existing stacks included.