The full methodology is documented at 12factor.net.
- Portable across execution environments without source code changes
- Suitable for deployment on modern cloud platforms
- Consistent between development and production
- Designed for continuous deployment
- Easy to scale horizontally
The twelve factors
I. Codebase
I. Codebase
A codebase is always tracked in a version control system such as Git. Every app has one codebase, but it can be deployed many times (staging, production, etc.).
- Codebase = repository
- One-to-one relationship between codebase and app (app = service)
II. Dependencies
II. Dependencies
All dependencies must be explicitly declared and isolated. The app must not rely on the implicit existence of system tools, libraries, or packages.Consider a FastAPI backend service:There is no guarantee that FastAPI exists on the system where your app will run. In Python:
api.py
- Declaration — list dependencies in
requirements.txtand install withpip install -r requirements.txt:requirements.txt - Isolation — use
virtualenv(python -m venv venv) to create an isolated environment with its own dependency versions.
III. Config
III. Config
Store configuration in environment variables. Configuration differs between deployments (staging vs. production), so it must not be hard-coded.The following example violates this factor by hard-coding database credentials:Instead, store configuration in a Then read the values from environment variables at runtime:This allows different configurations per deployment without exposing credentials in source control.
api.py
.env file:.env
api.py
IV. Backing services
IV. Backing services
Treat all backing services as attached resources. The app makes no distinction between local and third-party services.Examples of backing services:
- Databases (MySQL, PostgreSQL)
- Caching systems (Redis)
- Messaging/queueing systems (Kafka, RabbitMQ)
- SMTP mail servers
It makes no distinction between local and third party services. — 12factor.netIf your PostgreSQL database is an attached resource, you can swap between a local instance and a cloud-hosted one simply by changing the connection URL in your config — no code changes required.
V. Build, release, run
V. Build, release, run
Strictly separate the build, release, and run stages.
Build stage
Transform source code into an executable or binary. The build stage compiles assets and binaries based on vendor dependencies and code. (Examples: Docker image build, Maven, setuptools.)
Release stage
Combine the build artifact with the deployment config to produce a release object.
executable + config = release objectEvery release must have a unique release ID (e.g. a timestamp 2024-05-11-13-30-10 or an incrementing version v1, v2, v3). A release cannot be modified once created — any change must produce a new release.VI. Processes
VI. Processes
Execute the app as one or more stateless, share-nothing processes. Any data that needs to persist must be stored in a backing service.The following example violates this factor by storing state in process memory:With multiple instances, each process maintains its own
api.py
total_visit counter and they go out of sync.Similarly, storing user session data in process memory or the filesystem means a user redirected to a different instance loses their session.VII. Port binding
VII. Port binding
Export services via port binding. A 12-factor app is completely self-contained and does not rely on a specific web server injected at runtime.Flask binds to port
api.py
5000 by default. During local development you access the service at http://localhost:5000. The port is read from the PORT environment variable, making it configurable per deployment.VIII. Concurrency
VIII. Concurrency
Scale out via the process model. Applications should scale horizontally by running multiple instances, not vertically by adding resources to a single server.Scaling vertically (adding RAM or CPU to one server) requires downtime. Instead, add more servers and spin up more application instances. Use a load balancer to distribute traffic across instances.
IX. Disposability
IX. Disposability
Maximize robustness with fast startup and graceful shutdown. Processes are disposable — they can be started or stopped at any moment.Handling
- Fast startup: Avoid complex provisioning scripts. Processes should be ready to serve requests as quickly as possible.
- Graceful shutdown: When the process manager sends a
SIGTERMsignal, stop accepting new requests and complete all in-flight requests before exiting.
Processes shut down gracefully when they receive a SIGTERM signal from the process manager. — 12factor.netFor example, when you run
docker stop, Docker sends SIGTERM to the container. If the container does not stop within the grace period, Docker sends SIGKILL to force termination.Signal sequence:
SIGTERM → grace period → SIGKILL → container process terminated.SIGTERM correctly prevents impact on users who are waiting for a response.X. Dev/prod parity
X. Dev/prod parity
Keep development, staging, and production environments as similar as possible by applying CI/CD principles.Gaps between environments typically fall into three categories:
| Area | Traditional app | 12-factor app |
|---|---|---|
| Time | Weeks or months between code and deploy | Hours or minutes |
| Personnel | Developers write code; ops engineers deploy it | Same person does both |
| Tools | Different tools in dev vs. production (SQLite vs. MySQL) | As similar as possible |
The twelve-factor developer resists the urge to use different backing services between development and production. — 12factor.netBy adopting Continuous Integration, Continuous Delivery, and Continuous Deployment (CI/CD), you minimise all three gaps and catch environment-specific issues early.
XI. Logs
XI. Logs
Treat logs as event streams. The app itself should not concern itself with routing or storing logs.
- Do not write logs to a local file. In containerised environments, a container can be terminated at any moment and its local logs will be lost.
- Do not tightly couple the app to a specific logging solution.
A twelve-factor app never concerns itself with routing or storage of its output stream. — 12factor.netInstead:
- Write logs to
stdoutin a structured format (JSON). - Let the execution environment (container orchestrator, log agent) capture and forward the stream to a centralised logging system (e.g. Fluentd, Datadog, CloudWatch) for querying and analysis.
XII. Admin processes
XII. Admin processes
Run admin and management tasks as one-time processes. These tasks should be:
- Stored in source control alongside application code
- Run in the same environment as the production application
- Executed as a separate process, not mixed into the long-running app process
- Database migrations
- One-time data fixes (e.g. resetting an inaccurate visit counter in PostgreSQL)
- Script-based data imports or exports
Run admin tasks on the same systems as the production application to ensure they use the same environment, config, and dependency versions.