agentleFS
MartinKChenMartinKChen/harness-claude-code/skills/pattern-engineer-container/SKILL.md

pattern-engineer-container

Skill0 starsChanged 5 months agoMITView on GitHub

Containerized setups: every Dockerfile is multi-stage (`base`/`build`/`final`), pinned (no `:latest`) and vetted via `docker scout`, non-root with writable paths redirected, no in-image virtualenvs, `.dockerignore` required. Backends `alembic upgrade head` in entrypoint before exec'ing the server; expose fast `/healthz`. Frontend nginx puts API `location` blocks ABOVE the SPA `try_files` fallback. Secrets are runtime env vars. Activate on Dockerfile, compose, `.dockerignore`.

  • Reads credentials
  • Installs packages

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 0 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-engineer-container.md in the consuming project (resolve …

    line 14
  2. Very specific:

    Pinned tags, never :latest: Use immutable tags (node:20.11.1-alpine, python:3.12.4-slim, nginx:1.27.0-alpine).

    line 21
  3. Very specific:

    Vet base images with docker scout: before pinning. Reject any image with MEDIUM+ CVEs that have a fix available.

    line 22
  4. Very specific:

    Run alembic upgrade head (or framework equivalent) in the entrypoint script, BEFORE exec'ing the server.

    line 31
  5. Very specific:

    Use exec on the server line so PID 1 is the server (not the shell) and SIGTERM forwards correctly.

    line 33
  6. Very specific:

    Every backend exposes HTTP /healthz: 200 on normal boot, no auth, no DB / external-dep call, <100ms.

    line 37
  7. Very specific:

    Read from .env (gitignored) or host environment / secret manager.

    line 56
  8. Very specific:

    Default to internal-only. ports: publishes to the host (use sparingly, user-facing only).

    line 61
  9. Very specific:

    Prefer docker compose (V2 plugin) over the legacy docker-compose binary.

    line 73

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

pattern-engineer-container

When to activate

Activate when editing Dockerfile, docker-compose.yaml / .yml, compose.yaml / .yml, .dockerignore, or scaffolding container artifacts. Skip for Kubernetes / Helm / non-Docker runtimes without an explicit Docker tie-in.

Project memory overlay

After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-engineer-container.md in the consuming project (resolve MAIN_ROOT="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")"). If present, load it as an additive overlay to the rules below; if absent, skip silently. See memory-convention for the full contract (additivity, severity floor, conflict surfacing).

Patterns

Dockerfile shape

  • Multi-stage, always at least three: base (system deps + runtime), build (compile / bundle), final (minimal runtime).
  • Pinned tags, never :latest. Use immutable tags (node:20.11.1-alpine, python:3.12.4-slim, nginx:1.27.0-alpine). Digest pinning (@sha256:...) for production base images.
  • Vet base images with docker scout before pinning. Reject any image with MEDIUM+ CVEs that have a fix available; switch to the fixed tag.
  • Run as non-root in final: RUN addgroup -S app && adduser -S app -G app then USER app.
  • Every writable path is user-writable. Redirect PID files, cache, nginx *_temp_path (and client_body_temp_path, proxy_temp_path, fastcgi_temp_path when in use) to /tmp/... via config, AND recursively chown the app directory in the build stage before USER.
  • No virtual environments inside images. Install Python deps system-wide (uv pip install --system); no .venv / uv venv in the Dockerfile.
  • .dockerignore is mandatory. Exclude .git, node_modules, .env*, dist/, build/, coverage/, *.log, IDE folders.
  • Layer ordering: copy dep manifests + install deps before copying source, so dep layers cache across source edits.

Backend entrypoint

  • Run alembic upgrade head (or framework equivalent) in the entrypoint script, BEFORE exec'ing the server.
  • Copy the migration CLI into the final stage explicitly (or copy from build stage); alembic in the build stage is not enough.
  • Use exec on the server line so PID 1 is the server (not the shell) and SIGTERM forwards correctly.

/healthz endpoint

  • Every backend exposes HTTP /healthz: 200 on normal boot, no auth, no DB / external-dep call, <100ms.
  • Separate /readyz path (different URL) for readiness checks that DO touch the DB.

Frontend nginx

  • API location blocks go ABOVE the SPA try_files fallback. Putting try_files $uri $uri/ /index.html at the top catches /api/... too and returns index.html as the response body.
  • Order: /healthz → API prefixes (each with its own location) → root location / with try_files.
  • try_files $uri $uri/ /index.html is mandatory for any SPA — without $uri/, directory-path requests 404.
  • Pre-push hook's container:smoke-api-proxy probe catches misconfig by Content-Type (text/html on an API probe = SPA catch-all intercepted).

External-dependency doubles (for E2E)

  • Compose includes a stand-in for every external dependency the E2E flow touches — mail catcher, object-store emulator, fake gateway, broker. A spec can't verify an integration whose counterpart was never started.
  • Connection scheme matches the client's requirement (e.g. an async vs sync driver prefix), and every service that builds the client applies the same transformation — a rewrite present in the worker but missing in the web server fails only once a real connection is opened.
  • Reverse-proxy ordering (restated from Frontend nginx): API / upstream location blocks sit ABOVE the SPA try_files fallback, or client routes 404 and API calls return the app shell.

Secrets

  • Runtime env vars only. Never COPY / ARG / ENV baked into the image.
  • Read from .env (gitignored) or host environment / secret manager.
  • For prod: secrets: (file-based) in Swarm; platform secret store (Vercel / Railway / Fly / SSM) on managed hosts.

Networking

  • Default to internal-only. ports: publishes to the host (use sparingly, user-facing only).
  • expose: documents intra-network ports (optional but useful).
  • Bind to 127.0.0.1 ("127.0.0.1:5432:5432") when publishing dev-only ports.

Volumes

  • Named volume (db_data:/var/lib/postgresql/data): persistent state across restarts (DBs, queues, caches).
  • Bind mount (./src:/app/src): development source for live reload.
  • Anonymous volume (/app/node_modules): preserve container-generated content from bind-mount shadowing.

docker compose commands

Prefer docker compose (V2 plugin) over the legacy docker-compose binary.

OperationCommand
Follow logsdocker compose logs -f <service>
Exec shelldocker compose exec <service> sh
Rebuild changed imagesdocker compose up --build
Force full rebuilddocker compose build --no-cache <service>
Stop + removedocker compose down
Stop + remove + wipe named volumes (DESTRUCTIVE)docker compose down -v

Templates

AssetPurpose
templates/DockerfileThree-stage Node Dockerfile (base + build + final), pinned node:20.11.1-alpine, non-root app user, tini as PID 1.
templates/docker-compose.yamlapp + db (Postgres 16.3-alpine) services with healthcheck, named db_data volume, host-bound 127.0.0.1:3000 mapping.
templates/.dockerignoreExcludes .git, node_modules, .env*, build outputs, IDE folders, and Dockerfile* / docker-compose* from the build context.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.