Twelve-Factor Apps: Durable Principles & Modern Limits

The twelve factors are best understood as a system of deployment boundaries—source, artifact, config, release, process, state, and platform—not as a complete cloud-native checklist or a demand to use one particular tool.

The Twelve-Factor App is a 2011-era methodology, last updated in 2017, for software delivered as a service. Its lasting value is not that every application must obey twelve slogans. It is that a deployable service should have explicit boundaries between source, build artifact, deploy configuration, release identity, running processes, durable state, and the platform.

Those boundaries remove hidden inputs. If dependencies leak from a server, a rebuild is not reproducible. If configuration is baked into an image, the same artifact cannot move safely between environments. If a process owns durable state, replacement and horizontal scaling become data-loss operations. If an administrator runs a migration with a different checkout or dependency set, the database can move ahead of the application that owns it.

Twelve-factor release and runtime boundaries Version-controlled source becomes a build and then a content-addressed artifact. Deploy configuration and secret references combine with the artifact to identify a release. A deployment controller launches supervised service, worker, and one-off job processes that connect to external durable state and emit telemetry to a platform collector. Source revision reviewed + traceable Build declared inputs Immutable artifact digest + provenance build once Deploy configuration values + secret references Release identity artifact + config + policy Platform controller deploy · route · supervise Runtime processes service · worker · one-off job replaceable compute Durable state database · queue · object store explicit lifecycle Telemetry logs · metrics traces compile package selects emits uses

The durable model is a chain of identities and contracts. Configuration selects a release without mutating the artifact; the platform supervises runtime processes; durable state and telemetry cross explicit boundaries.

The mechanism behind the checklist #

The factors form a causal system:

  1. A traceable source revision and exact dependencies make a build reproducible.
  2. The build produces an immutable artifact. In an OCI workflow, a digest identifies its exact manifest, configuration, and layers.
  3. Deploy-specific configuration selects how that artifact will run. Artifact digest plus configuration revision and rollout policy identify a release.
  4. The platform launches one or more processes from that release, routes traffic, supplies configuration, supervises lifecycle, and captures output.
  5. Replaceable processes keep durable data in backing services. Their restart, reschedule, or duplication must therefore preserve correctness.
  6. The same release runs normal services, workers, and one-off administrative commands, so operational actions do not invent a second software supply path.

Break any link and a later property becomes unreliable. Mutable tags break release identity. Runtime package installation breaks artifact reproducibility. Local sessions break process replacement. A hand-written production script breaks parity even if the web process itself is perfectly packaged.

The twelve factors, translated #

FactorDurable constraintModern readingFailure it prevents
I. CodebaseEvery deployable is traceable to version-controlled source.One repository is not required. A monorepo can produce many independently identified artifacts; a deployable may also be assembled from several repositories if provenance records every input.Unknown source, divergent copies, and changes that cannot be reproduced.
II. DependenciesDeclare and isolate every runtime dependency.Use lockfiles, hermetic or controlled builds, vendored tools where necessary, and a software bill of materials. “It exists on this host” is not a dependency specification.Works-on-one-machine behavior and undeclared host coupling.
III. ConfigKeep deploy-varying values outside source and immutable artifacts.Environment variables are one transport, not the rule itself. Mounted files, configuration services, and secret stores are valid when ownership, validation, reload, and failure behavior are explicit.Rebuilds per environment, leaked credentials, and untraceable variation.
IV. Backing servicesAddress external services through explicit resource bindings.A locator can change without a code edit only when the substitute preserves the required transactions, ordering, consistency, latency, and failure semantics.Provider details spreading through business code and resources that cannot be rebound.
V. Build, release, runBuild once; name releases; do not mutate a running artifact.Promote an artifact by digest, attach provenance, and treat configuration or policy changes as new releases even when the bytes are unchanged.Production-only builds, mutable deployments, and rollbacks to an unknown state.
VI. ProcessesCompute processes do not own durable session or business state.Local memory and disk may be caches or scratch space. Correctness must survive restart, relocation, retry, and concurrent replicas. Stateful workloads remain valid but need an explicit state lifecycle.Sticky routing, data loss on replacement, and scale-out that changes behavior.
VII. Port bindingA service exposes a clear network contract to its environment.The process usually listens on an assigned interface and port; a proxy, service mesh, or ingress can terminate external protocols without becoming part of application construction.Hidden coupling to a particular host web server or deployment topology.
VIII. ConcurrencySeparate workload types and scale them deliberately.Service replicas, queue workers, scheduled work, and run-to-completion Jobs have different scaling and retry contracts. Internal threads or async tasks do not remove process-level resource and failure boundaries.Scaling the wrong bottleneck and mixing unrelated lifecycle policies.
IX. DisposabilityStart predictably and stop safely.Handle termination signals, stop accepting work, drain in-flight operations within a grace period, and make retried jobs idempotent. Startup speed does not replace readiness.Dropped work, duplicate side effects, and failed rollouts during replacement.
X. Dev/prod parityExercise production contracts before production.Parity means compatible versions, protocols, schemas, security policy, and failure behavior. Laptop and production topology need not be identical.Adapters hiding semantic differences and tests proving the wrong environment.
XI. LogsApplications emit events; the platform routes and stores them.Standard streams are a useful transport. Production observability also needs structured fields, timestamps, severity, resource identity, trace correlation, retention, access control, and alerting.Per-host log files, lost events after replacement, and uncorrelated text streams.
XII. Admin processesOne-off work uses the same artifact, config contract, and dependencies as the service.Run migrations, repairs, and consoles as audited Jobs with explicit identity, timeout, retry, concurrency, and rollback rules.Snowflake shells and maintenance code that does not match the running release.

Artifact, config, and release are different identities #

Let artifact A be identified by an immutable digest, and let deploy configuration have revision C. A release record can be modeled as

text
R = (artifact_digest A, config_revision C, rollout_policy P)

This is more precise than “version 42” pointing at a mutable image tag. If the artifact changes, there is a new release. If a feature flag, database endpoint, or resource policy changes, there is also a new release even when A does not. A rollback must select a previously known tuple; it must not rebuild old source and hope to obtain the same bytes.

SLSA provenance answers a related but different question: where, when, and how was A produced, and from which inputs? Release records make deployment selection traceable; provenance makes the artifact's production traceable. Neither substitutes for the other.

Environment variables have a specific lifecycle cost: a process receives a snapshot when it starts. Updating the source object does not rewrite that process environment; a restart or explicit reload mechanism is required. Mounted configuration can update, but the application still needs a defined observation, validation, and failure policy. “Externalized config” therefore describes ownership and mutability boundaries, not one syntax.

Stateless compute does not mean a stateless system #

A useful replacement test is:

If any one service replica disappears between two instructions, can another replica continue without missing durable information or applying an externally visible effect twice?

Passing that test requires more than moving a session to Redis. Workers may receive the same job again after a timeout. HTTP clients may retry after losing a response. A graceful termination can exceed its deadline and become an abrupt kill. Transactions, idempotency keys, deduplication records, leases, and resumable workflows are the mechanisms that make disposability true.

Stateful workloads are not twelve-factor failures. A database, durable queue, or replicated log deliberately owns state and may require stable network identity and storage. Kubernetes expresses that with different workload controllers: Deployments for interchangeable replicas, StatefulSets for stable identity and storage, and Jobs for run-to-completion work. The architectural rule is to distinguish those contracts instead of pretending every process is interchangeable.

The platform contract is wider than a port #

Port binding gives the router a stable way to reach a process, but orchestration needs several independent signals:

  • a startup probe says initialization has completed enough for other probes to begin;
  • a readiness probe says the instance should currently receive traffic;
  • a liveness probe says restarting the instance is more likely to help than letting it continue;
  • a termination signal plus grace period gives the instance a chance to become unready, drain, and exit;
  • resource requests let the scheduler place work, while limits constrain consumption and can cause throttling or out-of-memory termination.

These signals are not interchangeable. A dependency outage may make a service temporarily unready while the process remains healthy. Using the same dependency check as liveness can make every replica restart during the outage, amplifying the incident the probe was supposed to detect.

Similarly, emitting to stdout solves collection at the process boundary, not diagnosis. Kubernetes explicitly treats a separate cluster-level logging backend as necessary for durable analysis. OpenTelemetry's log data model adds the resource and trace context required to connect an event to a service instance and request. Metrics, traces, profiles, alert rules, retention, and privacy controls still need their own design.

Worked deployment trace #

Suppose commit 8d6e… builds image digest sha256:ab42…. CI records its dependencies, test result, and provenance. Production configuration revision cfg-107 names a PostgreSQL endpoint, a queue, a feature policy, and references secret versions without embedding their values. The deployment controller records:

text
release-204 = (sha256:ab42…, cfg-107, rolling-25-percent)

The sequence is then traceable:

  1. A migration Job starts from sha256:ab42… with cfg-107, acquires a migration lock, and exits successfully.
  2. New service replicas start from the same digest. Startup remains false while local initialization runs; readiness becomes true only after the instance can serve its contract.
  3. The router sends traffic only to ready replicas. Structured events carry release, resource, trace, and span identifiers to the platform collector.
  4. During replacement, an old replica becomes unready, receives termination, stops accepting new work, completes or safely abandons in-flight work, and exits before the grace deadline.
  5. A worker killed after committing an external effect receives the job again. Its idempotency record turns the retry into a no-op instead of a duplicate charge.

If error rate rises, rollback selects the previous recorded release tuple. It does not rebuild an old branch. Database compatibility must already support the overlap between old and new application versions; twelve-factor names the release boundary but does not solve schema evolution.

Where strict readings fail #

  • One codebase is too literal. Repository topology is an organizational choice. What matters is a complete mapping from deployable artifact to reviewed source and build inputs.
  • Environment variables are not universally superior. They are awkward for large structured values, rotation, binary material, dynamic updates, and least-privilege projection. Preserve the separation, choose the transport deliberately.
  • Dev/prod parity is contract parity. Reproducing production scale on a laptop is impossible and unnecessary. Reproduce protocol versions, schemas, security boundaries, and failure semantics in tests and staging.
  • Horizontal scaling is not automatic. A stateless handler can still contend on one database, violate rate limits, reorder messages, or create a retry storm. Capacity and concurrency control remain system properties.
  • Twelve factors are not a security or reliability certification. The original list omits identity, authorization, encryption, supply-chain integrity, health signaling, resource management, data lifecycle, and recovery objectives.

Review checklist #

  1. Can every running instance be mapped to an immutable artifact digest and source provenance?
  2. Are all dependencies declared, versioned, and available without relying on a prepared host?
  3. Which values vary by deploy, who owns them, how are they validated, and how do updates reach processes?
  4. Which processes are replaceable, which components own durable state, and what happens on retry or abrupt death?
  5. Are readiness, liveness, startup, termination, and resource contracts distinct and tested?
  6. Can backing services be rebound only within a verified semantic contract?
  7. Do logs carry enough structured context to join them with metrics and traces after a replica disappears?
  8. Do migrations and repairs use the exact release artifact, with auditable identity and concurrency controls?
  9. Can rollback select a known release without rebuilding, and are data/schema changes compatible with it?
  10. Which production requirements lie outside the twelve factors, and where are they specified?

The useful question is not “is this app twelve-factor?” It is “which hidden deployment inputs and lifecycle assumptions still prevent us from reproducing, replacing, scaling, observing, or rolling back it safely?”

Sources

  1. docsThe Twelve-Factor Apppdfnotes
  2. docsOCI Image Format Specification
  3. docsSLSA v1.2 — Provenance
  4. docsKubernetes — Workload Management
  5. docsKubernetes — ConfigMaps
  6. docsKubernetes — Good Practices for Secrets
  7. docsKubernetes — Pod Lifecycle
  8. docsKubernetes — Probes
  9. docsKubernetes — Resource Management for Pods and Containers
  10. docsKubernetes — Logging Architecture
  11. docsOpenTelemetry — Logs Data Model

Searches titles, aliases, headings, summaries, and full text.↑↓ move ↵ open esc close