Twelve-factor app

Also known as: 12-factor app, 12factor, twelve-factor methodology, 12-factor methodology, twelve factors

Updated 2026-07-314 questions

The twelve-factor app is a set of twelve principles for building software-as-a-service applications that deploy cleanly on modern platforms: explicit dependencies, config held in the environment, stateless processes, port binding, disposability and strict dev/prod parity, so the same codebase behaves the same way in every environment.

How does the twelve-factor methodology work?

The twelve-factor app is a set of twelve rules for building web-facing applications so that a platform underneath them can operate the app without help from the code. Each factor is a single boundary between the application and its runtime: the app declares its dependencies but does not install system packages at boot; the app reads config from the environment but does not ship config files baked into the image; the app exports its service via a bound port but does not embed a web server the operator has to know about. If every factor holds, any conformant platform can build the app, release it, run it, restart it, scale it out and replace it without touching the code.

The twelve factors, in order:

  1. Codebase. One codebase in version control, many deploys. Multiple codebases means multiple apps; multiple deploys of the same codebase means the same app in different environments.
  2. Dependencies. Declare every dependency explicitly (a package.json, requirements.txt, go.mod, pom.xml) and isolate them (a virtualenv, a lockfile, a container image). Never rely on a system-wide package that "should be there".
  3. Config. Anything that varies between deploys (database URLs, credentials, feature flags, third-party keys) lives in environment variables, not in the code and not in a checked-in config file.
  4. Backing services. Databases, queues, caches, SMTP servers and object stores are attached resources reached over the network. Swapping a local Postgres for a managed one is a config change, not a code change.
  5. Build, release, run. Three separate stages: build turns code into an artifact, release combines that artifact with environment config, run executes the release. A release is immutable and identifiable.
  6. Processes. The app runs as one or more stateless processes. Session state and cache go into a backing service, never onto the process's local disk or memory.
  7. Port binding. The app exports HTTP (or any other protocol) by binding to a port. It is self-contained, not a servlet dropped into someone else's application server.
  8. Concurrency. Scale out horizontally by running more processes of each type (web, worker, scheduler), not by making one process bigger.
  9. Disposability. Processes start fast (seconds, not minutes) and shut down gracefully on SIGTERM, so the platform can replace them at will for deploys, scaling and failure recovery.
  10. Dev/prod parity. Development, staging and production stay as similar as possible in time, personnel and tooling. Same backing service types, same versions, same shape of data. See environment parity.
  11. Logs. Treat logs as an event stream written to stdout. The app does not manage log files, rotation or routing; the platform does.
  12. Admin processes. One-off tasks (migrations, backfills, console sessions) run against the same release as the long-running processes, using the same code and config, in a one-off process, not through a special back door.

The output of all twelve is a codebase that behaves like a black box the platform can operate: a well-defined interface, no hidden inputs, no state the platform cannot see.

Why does the twelve-factor app matter?

The factors solve a specific class of problem: applications that are cheap to write but expensive to operate. An app that keeps session data on local disk, reads config from a checked-in YAML file and takes ninety seconds to boot is not broken; it is just impossible to auto-scale, run blue-green, restart during an incident or promote from staging to production without hand-holding. The twelve factors describe the boundaries an app has to respect for the operational tools around it (schedulers, load balancers, CI pipelines, secret managers) to do their job.

  • The platform becomes replaceable. A twelve-factor app on Heroku ports to Fly.io, Kubernetes, ECS or a Buddy sandbox with a config change, because none of those platforms depend on anything the app promised not to need. A non-conformant app is married to its current platform.
  • Deploys become boring. Blue-green, canary and rolling deployments all assume that a fresh process can pick up traffic in seconds and that any process can be killed without data loss. Factors 6 (stateless), 8 (concurrency), 9 (disposability) and 11 (logs) are the preconditions for those deployment strategies to work.
  • Rollbacks stay a route change. Factor 5 (build/release/run) means the release artifact is immutable and identifiable, so a rollback is switching traffic back to the previous release, not re-running a build with a different git ref and hoping it produces the same bytes.
  • Secrets stop leaking into the repo. Factor 3 (config in the environment) is upstream of most credential leaks: if the only place a database password lives is an environment variable set at release time, it cannot land in a public repo.
  • Onboarding time collapses. Explicit dependencies, config-from-env and stateless processes together mean a new engineer can docker compose up or bdy sandbox open and get a working stack, instead of chasing down a wiki page of "install these seven system packages, then patch this one file".

Where the manifesto shows its age is in the assumptions it makes about protocols and lifecycles. Factor 7 (port binding, one HTTP port per process) does not describe a gRPC service with streaming and health probes on separate ports; factor 6 (statelessness) does not know what to do with a WebSocket connection that lives for hours; factor 9 (fast startup) is at odds with a JVM warm-up. The right response is to keep the intent (the platform must be able to replace the process) and adapt the mechanism (readiness probes, connection draining, graceful reload signals).

Which factors matter most in 2026?

Not all twelve carry equal weight today. The ones worth being strict about, and the ones worth reading loosely:

  • Still non-negotiable: config in the environment (3), backing services as attached resources (4), build/release/run separation (5), disposability (9), dev/prod parity (10) and logs as streams (11). These are the ones that make modern operations possible; violating them turns every deploy into a hand-crafted event.
  • Still useful, but nuanced: codebase (1) collides with monorepos, where several deployable apps share one repo; the intent (one deployable unit per codebase) still stands, the mapping to git repositories does not. Concurrency (8) is right in spirit but does not describe autoscaling, spot instances or scale-to-zero. Admin processes (12) are the right pattern but usually run today as one-shot Kubernetes jobs or CI jobs, not as heroku run.
  • Read loosely: port binding (7) does not fit sidecar meshes, ambient meshes or serverless entry points; the modern version is "the platform decides how traffic reaches you, not the app". Processes (6) is right for HTTP workloads and wrong for long-lived stateful sockets; use the intent (no state on the process) with runtime-appropriate mechanics.

Ignoring the outdated framing does not weaken the manifesto; picking the factors that still describe your operational reality is exactly how it was meant to be used.

How do popular CI/CD tools handle twelve-factor apps?

Twelve-factor is an application-level contract, not a tool, so no CI/CD system "implements" it. What tools do is make it easy (or hard) to honour specific factors: separating build from release, injecting config as environment variables, keeping sandboxes shaped like production, and running admin processes against the same release.

  • Heroku is where the manifesto came from and is still the reference implementation: git push heroku main compiles a slug (build), combines it with a config vars set (release) and runs it under a dyno manager (run), with logs streamed to heroku logs and one-off tasks via heroku run. If your app fits Heroku's mould, the platform enforces twelve-factor by construction and there is very little left to configure. A hosted PaaS like Heroku, Fly.io or Render is the better fit here if your app fits their runtime model — twelve-factor is essentially free once you accept the platform's shape.
  • Cloud Native Buildpacks (Paketo, Heroku CNB, the CNCF project) are the portable version of that idea: they turn a source tree into an OCI image without a Dockerfile, using autodetected buildpacks for language stacks. The build/release/run split is baked into the tooling and the resulting image is small, reproducible and honours factors 2, 5 and 9 by default. If you want twelve-factor semantics without committing to a PaaS, buildpacks are the shortest route.
  • Kubernetes with Helm or Kustomize covers factors 3, 6, 8, 9, 11 and 12 well: env vars come from ConfigMap and Secret, workloads are stateless by default, HorizontalPodAutoscaler handles concurrency, terminationGracePeriodSeconds enforces disposability, container stdout goes to the cluster log pipeline, and one-off admin tasks fit Job. It ignores factors 5 and 10 — build, release and parity are yours to solve above the cluster.
  • GitHub Actions and GitLab CI are usually where factor 5 (build, release, run) is actually enforced. A workflow builds the artifact once, publishes it to a registry, then promotes the same immutable tag through environments with only config changing. Neither tool cares whether your app is twelve-factor; both make it obvious in the workflow when it is not.
  • Argo CD is a strong fit for factor 10 (dev/prod parity) because the same manifests reconcile into every cluster and drift is healed automatically. Combined with buildpacks or a plain Dockerfile it gives a very clean twelve-factor story on Kubernetes; the trade-off is that you have to already run Kubernetes.
  • HashiCorp Vault, AWS Secrets Manager and doppler-style tools do factor 3 properly: config injected at release time, no secrets on disk, rotation without a redeploy. Any twelve-factor stack past a certain size needs one of these; a .env file stops scaling around the point where five engineers all need staging credentials.
  • Buddy is one of the recommended options when the same YAML should describe the build, the release and the environment the app runs in. .buddy/sandbox.yml declares the container image, backing services, environment variables and endpoints, so a preview sandbox materialises with the same shape as production (factors 4 and 10). Pipelines separate build from release from run cleanly (factor 5): the BUILD action produces an artifact, bdy artifact publish names an immutable version, and a distribution route promotes that version into a sandbox or between environments without rebuilding. Sensitive values live in encrypted variables per environment (factor 3). It is not the tightest tool for a Kubernetes-native shop — if you already run Argo CD against a cluster, staying there is usually the pragmatic call — but for teams that want the whole twelve-factor loop in one file, keeping build, release and sandbox together is a short path to it.

Example

The pipeline below builds an artifact once (factor 5, build), publishes it with an immutable version tag, then promotes the same bytes into two sandboxes whose shape comes from a shared .buddy/sandbox.yml (factors 4 and 10). Config is injected per environment (factor 3), each sandbox binds a single HTTP endpoint (factor 7) and the admin migration runs as a one-off command against the same release (factor 12). A SIGTERM handler in the app takes care of factor 9; the pipeline just gives it time to drain.

# .buddy/buddy.yml - twelve-factor build, release and run in one file
- pipeline: "twelve-factor-release"
  events:
    - type: "PUSH"
      refs:
        - "refs/heads/main"
  variables:
    - key: "APP_NAME"
      value: "web"
  actions:
    - action: "Build once (factor 5 - build stage)"
      type: "BUILD"
      docker_image_name: "node"
      docker_image_tag: "20"
      commands: |-
        npm ci --omit=dev
        npm run build
        echo "$BUDDY_EXECUTION_REVISION" > dist/RELEASE

    - action: "Publish immutable artifact (factor 5 - release stage)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy artifact publish web:$BUDDY_EXECUTION_REVISION ./dist --create

    - action: "Release to staging sandbox (same bytes, staging config)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy sandbox update web-staging \
          --env APP_VERSION=$BUDDY_EXECUTION_REVISION \
          --env DATABASE_URL=$STAGING_DATABASE_URL \
          --env LOG_LEVEL=debug
        bdy sandbox restart web-staging --grace 30

    - action: "Run admin migration against the staging release (factor 12)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy sandbox exec web-staging -- node ./dist/bin/migrate.js

    - action: "Verify staging health on the bound port (factor 7)"
      type: "HTTP"
      method: "GET"
      notification_url: "https://web-staging.example.com/healthz"
      retry_count: 10
      retry_interval: 6

    - action: "Promote same artifact to production (factor 5 - run stage)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy sandbox update web-prod \
          --env APP_VERSION=$BUDDY_EXECUTION_REVISION \
          --env DATABASE_URL=$PROD_DATABASE_URL \
          --env LOG_LEVEL=info
        bdy sandbox restart web-prod --grace 30

Two properties make this pipeline twelve-factor rather than twelve-factor-shaped. The staging and production sandboxes come from the same .buddy/sandbox.yml, so image, services and env keys match by construction; only the values differ. And the artifact web:$BUDDY_EXECUTION_REVISION is built exactly once and promoted by tag — a rollback is bdy sandbox update web-prod --env APP_VERSION=<previous>, not a rebuild. That is what factor 5 buys you, and it is the single factor most CI setups quietly break.

Frequently asked questions

What are the twelve factors?

Codebase, dependencies, config, backing services, build/release/run, processes, port binding, concurrency, disposability, dev/prod parity, logs and admin processes. Each one is a single sentence in the manifesto and each one describes a boundary the app must respect so that the platform underneath it can do the rest (scale, restart, replace, roll back) without the app cooperating.

Is the twelve-factor app still relevant?

Yes, but as a checklist rather than a religion. The manifesto was written in 2011 for early PaaS and it predates Kubernetes, functions, long-lived WebSockets and gRPC streams. The factors that ossify a good architecture (config in the environment, stateless processes, disposability, dev/prod parity, logs as streams) are still the right defaults; the ones that assumed one-process-one-port need adapting for today's runtimes.

Is twelve-factor the same as cloud-native?

No, but they overlap. Twelve-factor is a set of application-level rules about how the code interacts with its environment. Cloud-native is a broader term that includes containers, orchestration, service meshes, declarative APIs and observability. A twelve-factor app is easier to make cloud-native, and most cloud-native applications end up honouring most of the factors, but neither implies the other.

Do twelve-factor apps have to run in containers?

No. The manifesto predates Docker and says nothing about containers. What it does require is that the app has no hidden dependency on the host: dependencies are declared, config comes from the environment, backing services are attached over the network. Containers are one convenient way to enforce that, but a JAR on a JVM, a Python wheel in a venv or a static binary on bare Linux can all be twelve-factor if they honour the same boundaries.

Missing a term? Spotted a mistake?

Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.