Agent skill
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.
Install this agent skill to your Project
npx add-skill https://github.com/delorenj/skills/tree/main/stacks-deploy
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 usesimage:. - Runtime data lives at
/home/delorenj/data/<service>/, not in stacks or git repos. network_mode: hostservices cannot join the proxy network. Use a companion service for Traefik routing.
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:orimage:? - 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.exampleas 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:
- The host-networked service listens on a host port (e.g., 31243)
- A companion web-facing service (e.g., nginx) joins the
proxynetwork - The companion proxies API routes to
http://host.docker.internal:<port> - 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: hostorprivileged: 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.deploy]
description = "Pull and restart in stacks deployment"
run = """
cd ~/docker/stacks/<category>/<service>
docker compose pull <service-name>
docker compose up -d <service-name>
"""
Reference Files
references/traefik-labels.md- Label templates, middleware patterns, host-networking workaroundreferences/stacks-layout.md- Monorepo structure, aggregator format, naming conventionsreferences/deployment-modes.md- Mode comparison with real examples from existing stacks
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
openclaw-upgrade
Upgrade OpenClaw installations to the latest version. Handles global npm installs, local development setups, release channels (stable/beta/dev), configuration preservation, and platform-specific requirements. Use when updating OpenClaw, switching release channels, or troubleshooting update issues.
vercel-composition-patterns
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.
security-monitor
Real-time security monitoring for Clawdbot. Detects intrusions, unusual API calls, credential usage patterns, and alerts on breaches.
event-driven-architecture
Kafka, RabbitMQ, SQS/SNS, event sourcing, CQRS, saga patterns, dead letter queues, and idempotency. Use when designing asynchronous systems, implementing message-driven workflows, or building event streaming pipelines.
shadcn-ui
Expert guidance for integrating and building applications with shadcn/ui components, including component discovery, installation, customization, and best practices.
just-fucking-cancel
Find and cancel unwanted subscriptions by analyzing bank transactions. Detects recurring charges, calculates annual waste, and helps you cancel with direct URLs and browser automation. Use when: 'cancel subscriptions', 'audit subscriptions', 'find recurring charges', 'what am I paying for', 'save money', 'subscription cleanup', 'stop wasting money'. Supports CSV import (Apple Card, Chase, Amex, Citi, Bank of America, Capital One, Mint, Copilot) OR Plaid API for automatic transaction pull. Outputs interactive HTML audit with one-click cancel workflow. Pairs with Plaid integration for real-time transaction access without CSV exports.
Didn't find tool you were looking for?