Skip to content
LearnTwelve-factor config

What is twelve-factor config?

Also known as12-factor appconfig in the environment
Example. Kiteline is a fictional company.
On this page

Twelve-factor config is the third principle of the Twelve-Factor App methodology: everything that changes between deploys of the same code, such as database addresses, credentials and per-environment hostnames, is kept out of the code and supplied to the program through environment variables.

What the principle says

The Twelve-Factor App defines config as "everything that is likely to vary between deploys (staging, production, developer environments, etc)". Its test for whether you have separated config from code is memorable: could the codebase "be made open source at any moment, without compromising any credentials"? If not, there is a hardcoded secret somewhere.

It then picks environment variables as the carrier, because they "are easy to change between deploys without changing any code" and work the same in every language and operating system.

Why teams still follow it

  • One build, many deploys. The same artifact runs in staging and production with different values, so what you tested is what you ship.
  • No secrets in the repository. Reading process.env.DATABASE_URL names a setting without containing it.
  • Tool independence. A container platform, a systemd unit, a CI job and a developer's shell can all set environment variables.

Where it falls short

The methodology says where config goes, not how values get there safely. Environment variables are visible to the process and anything it starts, can show up in crash reports and debug pages, and are often filled from a .env file that sits on disk and travels by chat. Following the principle with a plaintext file on every laptop meets the letter and misses the point.

It also takes a position many teams don't follow. Grouping settings into named environments such as development, test and production "does not scale cleanly", the text argues; each variable should be "independently managed for each deploy." In practice, most teams group by environment anyway, because access, review and audit are easier to reason about per environment than per variable.

Common mistakes

  • Config that changes code paths. if (env === "production") checks scattered through the code reintroduce the coupling the principle removes. Prefer specific settings such as PAYMENTS_MODE=live.
  • Build-time config treated as run-time. Variables that a front-end build inlines are fixed in the artifact; see public environment variable.
  • Inheriting everything. A program that receives the whole parent environment also receives credentials it was never meant to have.

How Crunu applies it

Crunu delivers config the twelve-factor way, at start time through the process environment, and adds the parts the principle leaves open. crunu run decrypts an environment in memory and starts the program with its variables. By default the program gets only a short allowlist from the parent, such as PATH, HOME and LANG, plus the environment's variables, and never the credentials crunu itself uses:

Shell
crunu run --env payments-api/staging -- ./bin/server

--inherit-env passes the whole parent environment when you need it. With --restart on-change, a changed value restarts the program with the new variables, so config changes without a new build.

Crunu does group values into deployment environments, unlike the methodology's advice, because each environment is also an encryption key and an access list. You can combine several with repeated --env flags when a deploy needs values from more than one. The team side of this is in how to share .env files with your team.

Sources

From the blog

  • Servers

    One .env file, four parsers: Node, Python, Docker, systemd

    The same .env line can give three different values. We ran one file through Node, dotenv, python-dotenv, Compose and docker run, and read systemd's rules.

    Sharing .env files guide

  • Migration

    Exporting secrets from Doppler or Infisical

    The CLI commands that turn a Doppler config or an Infisical environment into a .env file, the defaults that change what you get, and what stays behind.

    Sharing .env files guide

  • Practices

    Keeping .env.example in sync with every environment

    A .env.example drifts because nothing breaks when it's wrong. Compare names, never values, fail CI on missing variables, and compare real environments.

    Sharing .env files guide