Compose helper
A safe docker compose wrapper for local development, plus the agent rules and skill that stop AI assistants from wiping volumes or hanging on logs
npx -y skills add jpbaking/compose-helper --skill compose-helperAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when creating or modifying docker-compose.yaml / docker-compose.yml in a project that contains a compose-helper script (compose-helper.sh or compose-helper.ps1), or when building, starting, stopping, or debugging services in such a project. Covers the "--profile build" convention for services that build local images, exact script command semantics, env-file discovery, and a safe edit-build-verify workflow. Trigger on: docker-compose edits, adding a service, adding a Dockerfile/build block, "image not found" errors after start, or any docker compose lifecycle task in a compose-helper project.
SKILL.md
6.3 KB, as published. Nobody here has run it
compose-helper: compose file authoring and the build profile
compose-helper is a thin wrapper around docker compose that lives next to
docker-compose.yaml. Every invocation it makes is:
docker compose -p <project> -f <compose-file> [--env-file .env|.config/.env] <args>
<project>= the script's directory name, unless overridden byDCH_PROJECT_NAMEincompose-helper.env.- Compose file =
docker-compose.yaml, falling back todocker-compose.yml. - Env file =
.envif present, else.config/.env, else none.
Always go through the script (never raw docker compose) so the project
name stays consistent. Any call with 2+ arguments passes straight through to
docker compose with those pinned options — that is the escape hatch for
config, ps, exec, non-following logs, etc.
The --profile build convention (IMPORTANT)
The build, rebuild, and up commands run:
docker compose ... --profile build build --pull
while start/restart run plain up -d / down + up -d without the
build profile. This means the compose file must separate "services that
produce an image" from "services that run":
services:
# BUILDER: exists only to produce a local image. Never runs.
my-app-builder:
profiles: ["build"] # only visible to `--profile build build`
build:
context: ./my-app # dir containing the Dockerfile
image: my-app:local # REQUIRED: the tag the build produces
# RUNTIME: consumes the image. No build block.
my-app:
image: my-app:local # must match the builder's image: tag
restart: unless-stopped
env_file: .env # optional; see env section below
ports:
- "8080:8080"
# Pulled images need no profile and no builder.
postgres:
image: postgres:16
restart: unless-stopped
volumes:
- pg-data:/var/lib/postgresql/data
volumes:
pg-data:
Rules when creating or editing docker-compose.yaml
- Every service with a
build:block getsprofiles: ["build"]AND an explicitimage: <name>:localtag. Withoutimage:, compose derives a tag from the project name and the runtime service can't find it. - Runtime services never have a
build:block. They reference the builder'simage:tag exactly. If you add abuild:block to an unprofiled service,startmay trigger an implicit build and break the build/run separation. - Never
depends_ona builder service. Builder services are only ever built, never started; a runtime service depending on one will fail to start (the dependency never becomes "started"). - The profile name is exactly
build— the scripts hard-code--profile build. Do not invent other profile names for builders. - One builder can feed multiple runtime services: give them all the same
image:tag. - If nothing is built locally (all images pulled), skip the convention
entirely —
--profile buildtargeting zero services is harmless. - Suffix builder service names with
-builder(convention, not enforced) so intent is obvious.
Why this pattern exists
start/restartare guaranteed fast and side-effect-free: they never compile anything or touch the network for builds.build --pullrefreshes base images instead of serving stale layer-cache parents, and only touches services that opted into thebuildprofile.- Runtime config stays declarative: every runtime service is just
image: + settings, identical in shape whether the image is local or pulled.
Env files — two separate concerns
| File | Purpose |
|---|---|
.env (or .config/.env fallback) | ${VAR} substitution inside docker-compose.yaml; auto-passed as --env-file |
compose-helper.env | Configures the script itself: DCH_PROJECT_NAME, DCH_STOP_TIMEOUT (default 30), DCH_LOGS_TAIL (default 10) |
When you reference ${VAR} in the compose file, define it in .env (or
.config/.env), never in compose-helper.env. To pass variables into a
container's environment, additionally use environment: or env_file: on
that service — --env-file alone only does substitution.
Edit → build → verify workflow
After creating or modifying docker-compose.yaml (Linux/macOS shown; use
.\compose-helper.ps1 on Windows — both scripts are feature-equivalent):
# 1. Validate syntax, including build-profile services (pass-through form):
./compose-helper.sh --profile build config --quiet
# 2. Build local images (only build-profile services are targeted):
./compose-helper.sh build
# 3. Start runtime services detached:
./compose-helper.sh start
# 4. Verify state and read bounded logs (pass-through; no -f, returns):
./compose-helper.sh ps
./compose-helper.sh logs --tail=100
./compose-helper.sh logs --tail=100 <service>
Steps 2+3 can be combined as ./compose-helper.sh rebuild.
Pitfalls
- Do not run
./compose-helper.sh upor... logs(single-arg) in an automated session — both end inlogs -fand block forever. Userebuild+ pass-throughlogs --tail=N. downdeletes named volumes. Only use it when the user explicitly wants a data wipe; otherwisestop.- "pull access denied" / "image not found" on
startusually means a builder's image was never built or itsimage:tag doesn't match the runtime service — run./compose-helper.sh buildand diff the two tags. - Changes to Dockerfiles or build contexts do nothing until
buildorrebuild—start/restartnever rebuild. - Two-or-more arguments always bypass the shorthand commands
(
up -d≠ theupshorthand). Single unknown arguments (likeps) also pass through.