Docker Compose conventions
SkillFiles & storageDocker Compose conventions — short service names, no root, no host ports in base compose, override files for local customisations.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Docker Compose conventions skill
What this skill tells your AI
The instructions your AI receives, as published by spinspire/pocketbase-sveltekit-starter in .agents/skills/docker/SKILL.md and read by ahel’s review.
Apply these conventions when creating or modifying docker-compose.yml.
Principles
-
.env(git-ignored) +.env.example(checked in, with doc comments). All secrets and environment-specific values live in.env. The example file documents each variable. -
No root. Every service that doesn't need root gets
user: "${UID:-1000}:${GID:-1000}".- Dockerfiles must make the working directory group-writable (
RUN chmod g+w .). - Postgres is exempt — its official image handles user switching.
- Dockerfiles must make the working directory group-writable (
-
No port forwarding in the base compose file.
ports:only appear in override files. The base file exposes nothing to the host. -
Override file (example only). Write
docker-compose.override-example.ymlwith documented, commented-out options. Users copy it todocker-compose.override.yml(git-ignored) for their local customisations. Never writedocker-compose.override.ymlinto the project — only the example. -
Lightweight images. Prefer
-alpineor-slimvariants. -
Named project. Set
name:at the top so volumes and containers are predictable. Or use COMPOSE_PROJECT_NAME env var. -
Mandatory env vars. Use
${VAR:?required}so compose errors immediately when a required variable is missing. -
Short service names.
pg,bun,ml,py,sk(SvelteKit),pb(PocketBase), etc. If there's a "primary" app, their service should be namedapp. -
Restart policy.
restart: unless-stoppedfor long-lived services,on-failure:Nfor batch jobs. Never usealways(it reanimates after intentionaldocker compose stop). -
Read-only root.
read_only: trueon every service that doesn't need to write to its own filesystem. Mounttmpfsfor paths that must be writable (/tmp,/run). Combine withtmpfsfor the writable paths the app actually needs. -
Log rotation. Every service gets
logging.driver: json-filewithmax-size: 10mandmax-file: 3. Prevents disk fills. -
Pin images. Never
:latest— use explicit version tags or digests. Reproducible builds.
Patterns
Base compose
name: prj
services:
app:
build: .
image: prj-app
user: "${UID:-1000}:${GID:-1000}"
environment:
DB_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@pg:5432/${POSTGRES_DB}
depends_on:
pg:
condition: service_healthy
pg:
image: postgres:17-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:?required}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required}
POSTGRES_DB: ${POSTGRES_DB:?required}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:
Override example
# docker-compose.override.yml
services:
pg:
ports:
- "127.0.0.1:5432:5432"
app:
ports:
- "127.0.0.1:8000:8000"
volumes:
- ./src:/app/src:ro
Dockerfile — writable working dir
WORKDIR /app
RUN chmod g+w .
.env.example
# ── Section ────────────────────────────────────────────────────────
# Description of what this is for.
VAR_NAME=default-value
Production checklist
Before deploying, verify:
-
restart: unless-stoppedon every long-lived service -
deploy.resources.limits.memory+cpusset per service - Health checks on all services with
start_period -
depends_onusescondition: service_healthywhere needed -
read_only: true+tmpfsfor writable paths -
user:set to non-root on every non-Postgres service - No
:latest— all images pinned to versions or digests - Log rotation configured (
max-size/max-file) -
.envin.gitignore,.env.examplechecked in -
name:set at top of compose file - No
ports:in base compose (use override files) -
cap_drop: ALL+ specificcap_add+no-new-privileges:true - Backend/internal networks use
internal: true
Signals
- GitHub stars
- 507
- Forks
- 76
- Last commit
- Aug 2026
ahel review
S4info
community integration — published by spinspire, not docker
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Catalog kind
- skill
- Gateway key
docker-spinspire- Source
- github.com/spinspire/pocketbase-sveltekit-starter