Skip to content
Innovialabs

The Twelve-Factor App: Principles for Deployable Applications

Basics4 min

In short

The Twelve-Factor App is a set of twelve rules that let an app run the same way in any environment: one codebase, config in environment variables, stateless processes, logs to stdout and minimal differences between dev and prod.

The short answer

The Twelve-Factor App is a set of twelve principles for web apps and services deployed to the cloud, containers or plain servers. The goal is simple: the app should build, configure and run the same way everywhere — a developer laptop, staging and production — and scale by adding more processes.

The methodology is language- and framework-agnostic. Its ideas underpin Docker, Kubernetes and most PaaS platforms.

All twelve factors

#FactorWhat it means
1CodebaseOne codebase in version control, many deploys
2DependenciesDependencies are declared explicitly and isolated
3ConfigConfiguration lives in environment variables
4Backing servicesDatabases, caches, queues are attached resources via URL
5Build, release, runBuild, release and run stages are strictly separated
6ProcessesProcesses are stateless and store nothing locally
7Port bindingThe service binds to a port and exports HTTP itself
8ConcurrencyScale out by running more processes
9DisposabilityFast startup and graceful shutdown
10Dev/prod parityEnvironments are kept as similar as possible
11LogsLogs are an event stream written to stdout
12Admin processesOne-off tasks run with the same code and environment

Config via environment variables

Anything that differs between environments — database URL, API keys, feature flags — lives outside the code. A simple test: could you make the repository public right now without leaking a single secret?

import os

DATABASE_URL = os.environ["DATABASE_URL"]
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
DEBUG = os.environ.get("DEBUG", "false") == "true"

Locally, values can sit in a .env file listed in .gitignore; on servers they come from the orchestrator or a secrets manager. Avoid config.production.py files with hardcoded values: they leak secrets and multiply "special" environments.

Stateless processes

An app process should keep nothing important between requests. Sessions, uploaded files and caches belong in Redis, the database or object storage.

  • A user uploads an avatar — it goes to S3-compatible storage, not an uploads/ folder on disk.
  • A session lives in Redis or a signed cookie, not in process memory.

Then any instance can be restarted or removed, and load can be spread across copies without sticky sessions.

Logs as event streams

The app does not manage log files. It writes events to stdout/stderr, and the environment handles collection, rotation and storage: Docker, systemd, an agent like Fluent Bit or Vector, a backend like Loki or ELK.

console.log(JSON.stringify({ level: "info", msg: "order created", orderId }));

Structured JSON is easy to filter and search. Rotating files inside a container, on the other hand, leads to lost logs and full disks.

Dev/prod parity

A twelve-factor app narrows three gaps: time (code reaches production quickly), personnel (developers take part in deploys) and tools.

A classic mistake is SQLite locally and PostgreSQL in production. Small SQL differences only surface in production. The fix is to run the same services at the same major versions locally with Docker Compose.

The remaining factors in practice

  • Dependencies: lock files (package-lock.json, poetry.lock) committed, no reliance on globally installed tools.
  • Build, release, run: the image is built once, tagged, and that exact image moves from staging to production. No editing code on the server.
  • Port binding: the app starts its own HTTP server on the port from PORT; nginx or a load balancer sits in front.
  • Disposability: handle SIGTERM — finish in-flight requests, close connections, then exit.
  • Admin processes: migrations and scripts run as separate processes from the same release, e.g. docker compose run app python manage.py migrate.

Common mistakes

  • Secrets committed to the repo or baked into a Docker image.
  • User files stored on a container's local disk.
  • Different database versions in dev and prod.
  • Manual hotfixes on the production server, bypassing the release process.

FAQ

No. Start with the highest-value ones: config in the environment, stateless processes, logs to stdout and identical services in dev and prod. The rest usually fall into place once you adopt containers and CI/CD.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.