# Compose Standard

> Use when the user asks to write, edit, or review a local Docker Compose file (compose.yaml / docker-compose.yml) or a local service stack.

- Skill: `painhardcore/compose-standard` (Agent Skill, multi-file: 14 files)
- Install (CLI): `npx skillmds@latest add painhardcore/compose-standard`
- Raw SKILL.md: https://api.skillmd.com/api/skills/painhardcore/compose-standard/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: painhardcore (https://skillmd.com/u/painhardcore)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/painhardcore/compose-standard

---


# compose-standard

Lean local Compose stacks. Not a platform generator.

## Scope

Not here: Make wrapper names such as `up` / `down` / `logs` (`makefile-standard`), image build internals (`dockerfile-standard`), GitHub Actions (`ci-standard`), Go layout and golangci (`go-project-standard`). K8s, Swarm, reverse proxies as a product, and runtime secret distribution are out of scope entirely.

## Workflow

### 1. Inspect needs, not possibilities

Inventory: app entrypoints, existing Compose, Dockerfiles, `.env*` and README, the DB, cache, or queue the project **already** uses, Make compose targets, ports. Ignore sidecars nothing points to.

### 2. Apply standard

Read [`references/rules.yaml`](references/rules.yaml), the source of truth for rule IDs. Full rules: [`references/compose-standard.md`](references/compose-standard.md).

Greenfield: adapt a template from [`assets/`](assets/) and replace every `Adapt…` marker. Never ship an unfinished placeholder.

- App alone: [`assets/compose.app-only.yaml.template`](assets/compose.app-only.yaml.template)
- App plus one data service: [`assets/compose.yaml.template`](assets/compose.yaml.template)

Migrate: [`references/migration-guide.md`](references/migration-guide.md), incrementally, dropping unused services, networks, and volumes.

### 3. YAGNI

Never unconditional: custom networks; multiple overlay files; Redis, Nginx, or adminer by reflex; `profiles`; `deploy:` keys; a Swarm or K8s translation; digest pins on every local dependency image.

### 4. Validate

```bash
python3 scripts/validate_compose.py /path/to/compose.yaml
# structural check when Docker is available:
docker compose -f /path/to/compose.yaml config --quiet
```

Errors = fix. Warnings = human review. A static check is not proof the stack runs.

Validator logic is hardcoded today; the `checks:` blocks in `rules.yaml` are illustrative, not executed.

### 5. Report

- Services included and the evidence for each
- Filename chosen, `compose.yaml` or a legacy name kept
- Healthcheck and `depends_on` decisions
- Env and secrets approach, including any `.env.example` you added
- Bind mount against named volume choices
- Validation result, what you skipped, exceptions taken

## Hard rules

- Prefer `compose.yaml` for greenfield. Keep a legacy name only when callers depend on it.
- **MUST NOT** use the obsolete top-level `version:`.
- **MUST NOT** put a secret, password, or token literal in the file. Use `${VAR}` or `env_file`, and **never** a `${VAR:-secret}` default.
- Third-party `image:` uses a controlled tag, never floating `latest`.
- The project app uses `build:`. Image internals belong to `dockerfile-standard`.
- Where startup order matters: `healthcheck` plus `depends_on: condition: service_healthy`, or document why not.
- Networks, volumes, and profiles only when evidenced.
- Apps must not depend on this repo at runtime.

## Exceptions

```yaml
# compose-standard: except <rule-id> — <reason>
```

Every ignored MUST needs an exception line with a reason. An ignored SHOULD needs a brief rationale when it is not obvious.

