The Twelve-Factor App
The Twelve-Factor App is a methodology for building modern, deployable software-as-a-service applications — twelve principles derived from observing what makes apps portable, scalable, maintainable, and straightforward to deploy. Published by Heroku's founders, it predates containers but describes exactly what makes containerized, cloud-native apps work. Each factor is a concrete practice, and Node apps that follow them are easier to deploy, easier to scale, and less prone to environment-specific failures. This page summarizes all twelve and shows how each applies to Node.
The twelve factors — at a glance
Factor | The rule | |
|---|---|---|
I | Codebase | One repo, many deploys — never one repo per env |
II | Dependencies | Declare all deps in |
III | Config | Store config in the environment (not the code) |
IV | Backing services | Treat DB, Redis, S3 as attached resources — swappable via URL |
V | Build, Release, Run | Strict separation: build once, release = build + config, run is stateless |
VI | Processes | Execute as stateless, share-nothing processes |
VII | Port binding | Export services by binding to a port ( |
VIII | Concurrency | Scale out via the process model (cluster, replicas), not threads |
IX | Disposability | Fast startup, graceful shutdown; handle SIGTERM |
X | Dev/prod parity | Keep dev, staging, prod as similar as possible |
XI | Logs | Treat logs as event streams — write to stdout, never manage files |
XII | Admin processes | Run admin tasks (migrations) as one-off processes in the same environment |
The factors most relevant to Node
Factor VI — stateless processes
// ❌ State stored in process memory — lost on restart, invisible to other instances:
const sessionCache = new Map<string, Session>() // wiped on every deploy
// ✅ State in an external backing service — survives restarts, shared across instances:
// Store sessions in Redis:
await redis.set(`session:${id}`, JSON.stringify(session), { EX: 3600 })
const session = JSON.parse(await redis.get(`session:${id}`) ?? 'null')Factor IX — disposability
// FAST STARTUP: minimize module-level work — defer DB connections to first use
// or warm them early but don't block startup on slow external calls.
// GRACEFUL SHUTDOWN: respond to SIGTERM without dropping in-flight requests:
process.on('SIGTERM', async () => {
server.close(async () => { // stop accepting, finish in-flight
await db.end() // release resources
process.exit(0)
})
setTimeout(() => process.exit(1), 10_000).unref() // hard cap
})