Twelve-Factor Apps skill for Codex

One build, any environment, kill it anytime. Codex audits each of the twelve factors with concrete repository probes and records evidence by file and line instead of a generic checklist. It moves environment values into validated config, adds graceful SIGTERM handling that survives Kubernetes rollouts, sends logs to stdout and runs migrations as a separate release step, then proves it by running one image against two environments.

  • Architecture
  • $twelve-factor-apps
  • 153 lines
  • 2 files
  • ~1,850 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- twelve-factor-apps

Installs to ~/.agents/skills/twelve-factor-apps/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $twelve-factor-apps, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Audits and fixes an application against the twelve-factor methodology for containers, Kubernetes and PaaS - config in environment variables, stateless processes, backing services as attached resources, build/release/run separation, port binding, graceful SIGTERM shutdown, logs to stdout, dev/prod parity and one-off admin processes - with grep-based checks and code fixes. Use when preparing an app for containers or a PaaS, when pods lose data on restart, deploys drop requests, config is hard-coded per environment, or someone asks for a twelve-factor review.

Use it when

  • An app is moving to containers, Kubernetes or a PaaS
  • Config differs per environment through code branches or rebuilt images
  • Rolling deploys drop requests or pods lose data on restart
  • You want an evidence-based readiness checklist for a service

Use something else when

How to check the result

The same image starts against two different env files without a rebuild, and a SIGTERM during a slow request lets it finish and exit 0 within the grace period.

The mistake it prevents

Using a shell-form CMD or npm start as the container entrypoint, so SIGTERM never reaches the app and every deploy kills in-flight requests.

~/.agents/skills/twelve-factor-apps/SKILL.md

--- name: twelve-factor-apps description: Audits and fixes an application against the twelve-factor methodology for containers, Kubernetes and PaaS - config in environment variables,… license: MIT ---

Done well means the same immutable build artifact runs in every environment with only environment-provided config changing, any instance can be killed at any moment without losing data or dropping in-flight requests, and the app scales by adding processes. The twelve factors are a checklist toward that, not a religion; note justified exceptions.

Before auditing

  1. Read AGENTS.md, the Dockerfile, compose files, Kubernetes manifests or Helm charts, Procfile, and CI deploy jobs. These show how the app actually runs.
  2. Find config loading (settings.py, config/*.yml, appsettings.*.json, .env handling) and the process entry point (signal handling, server start).
  3. Note the platform: Kubernetes, ECS, Cloud Run, Heroku-style PaaS, VMs. Some fixes (graceful shutdown timing, health probes) depend on it.

Audit checklist with evidence

Run the probes, record pass, fail or exception with file paths.

#FactorPass whenProbe
1CodebaseOne repo (or monorepo package) per deployable; many deploys of itCheck for copy-pasted forks per customer or environment
2DependenciesAll declared and locked; nothing relies on host toolsLockfile present and used in build; probe A for implicit host tools
3ConfigEnvironment-specific values come from env or a secret store, not codeProbe B; look for if ENV == "production" branches
4Backing servicesDB, cache, queue, SMTP reached by URL/credentials from config; swappableHard-coded hostnames or local-socket assumptions
5Build, release, runBuild once; release = build + config; no code changes at runtimeImages rebuilt per environment, migrations or asset builds at container start
6ProcessesStateless; no sticky sessions; local disk only for scratchProbe C; in-memory session stores
7Port bindingApp serves HTTP itself on $PORTRequires an external app server injected at deploy
8ConcurrencyScale by process type (web, worker, scheduler)One process doing web plus cron plus queue work
9DisposabilityFast start, graceful SIGTERM, idempotent jobsSignal handlers; startup time; shutdown test below
10Dev/prod paritySame backing service types and versions in dev and prodSQLite in dev with Postgres in prod; compose image tags vs prod versions
11LogsUnbuffered event stream to stdout/stderr; no log files or rotation in the appProbe D
12Admin processesMigrations and one-off tasks run as separate processes from the same releaseMigration scripts run by hand from laptops or baked into web startup
rg -n "apt-get install|brew install" scripts/ Makefile                     # A: implicit host tools
rg -n "localhost|127\.0\.0\.1|\.internal|prod|staging" src -g '!**/*test*'  # B: env-specific values in code
rg -n "writeFile|/tmp/|uploads/|open\(.*['\"]w" src                         # C: local disk state
rg -n "FileHandler|RotatingFileHandler|transports\.File|\.log['\"]" src     # D: log files

Add the modern extras every reviewer asks about: health endpoints (separate liveness and readiness), secrets from a secret manager rather than plain env in manifests, telemetry (metrics and traces), and a non-root container user.

Fixes that come up most

Config

# settings.py: fail fast on missing config; no per-environment branches
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    redis_url: str
    log_level: str = "INFO"
    feature_x_enabled: bool = False

settings = Settings()  # reads env vars; raises at startup if DATABASE_URL is missing

Keep a committed .env.example with every key and no real values. Never commit .env. Config is what varies between deploys; constants that never vary (page size limits, route tables) stay in code.

Graceful shutdown

The orchestrator sends SIGTERM, waits a grace period (Kubernetes default 30 s), then SIGKILLs. On SIGTERM: fail the readiness check, stop accepting new connections, finish in-flight requests, close pools, exit 0.

// Node HTTP server
const server = app.listen(process.env.PORT ?? 3000);
process.on("SIGTERM", () => {
  ready = false;                         // readiness endpoint now returns 503
  server.close(async () => {             // stops accepting, waits for in-flight
    await db.end();
    process.exit(0);
  });
  setTimeout(() => process.exit(1), 25_000).unref(); // stay under the grace period
});
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
defer stop()
go func() { _ = srv.ListenAndServe() }()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
_ = srv.Shutdown(shutdownCtx)

Container gotcha: a shell-form CMD npm start makes sh PID 1, which does not forward SIGTERM. Use exec form (CMD ["node", "server.js"]) or an init such as tini, and avoid npm start as the entrypoint in production.

Kubernetes gotcha: endpoints are removed asynchronously, so new requests can arrive after SIGTERM. A short preStop sleep (5 to 10 s) or keeping the listener open briefly avoids dropped requests during rollouts.

Logs

Log one JSON object per line to stdout; let the platform ship and rotate. In Python, set PYTHONUNBUFFERED=1 in the image or logs arrive late and out of order.

Admin processes and migrations

Run migrations as a separate release step (Kubernetes Job, Helm hook, PaaS release phase), not in every web container’s startup, where N replicas race to migrate. Running migrations or one-off scripts against production is an outward-facing, potentially destructive action: confirm with the user before executing any of them.

Justified exceptions

  • Large or structured config (routing tables, feature matrices) can live in mounted files from a ConfigMap or config service; the principle is “outside the build”, not “only env”.
  • Secrets in plain environment variables leak into crash dumps and child processes; mounting them from a secret manager is better than the original factor.
  • Stateful services (databases, brokers) are not twelve-factor apps; do not force them.

Verification

  • Build the image once and run it with two different env files; it must start and point at different backing services with no rebuild.
  • Shutdown test: start the app, send a slow request, send SIGTERM (docker kill --signal=SIGTERM <id>), confirm the request completes and the process exits 0 within the grace period.
  • Kill a running instance under light load (hey or k6 against a local or staging copy) and confirm zero failed requests and no lost uploads or sessions.
  • rg probes from the checklist come back clean or each hit is documented as acceptable.

Output template

## Twelve-factor review: <app>
| # | Factor | Status | Evidence | Fix |
|---|---|---|---|---|
| 3 | Config | Fail | `src/db.ts:12` hard-codes `prod-db.internal` | Read `DATABASE_URL`; added to `.env.example` |
| 9 | Disposability | Fail | shell-form CMD, no SIGTERM handler | Exec-form CMD, graceful shutdown in `server.ts` |

**Exceptions accepted:** <factor, reason>
**Verification run:** <tests and results>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Twelve-Factor Apps"
  short_description: "One build, any environment, kill it anytime"
  default_prompt: "$twelve-factor-apps Do a twelve-factor review of this service before we move it to Kubernetes"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem