Twelve-Factor App
Build cloud-native applications with twelve-factor methodology - environment config, stateless processes, disposability, and dev/prod parity.
skill: Twelve-Factor App category: Backend tags: [Twelve-Factor, Cloud Native, DevOps, Configuration, Stateless] readingTime: 5
The twelve-factor methodology defines the practices that make web applications portable, scalable, and maintainable in cloud environments. Not all twelve factors apply to every application, but factors I (codebase), III (config), VI (processes), XI (logs) are nearly universal - violating them creates operational headaches.
Workflow
- Factor III: Store config in the environmentConfig that varies between deployments (API keys, DB URLs) must not be in code. Hard-coded config requires code changes and deployments to change behavior.
- Factor VI: Execute the app as stateless processesStateless processes scale horizontally - add more instances to handle more load. Stateful processes (storing session in memory) break horizontal scaling and lose data on restart.
- Factor IX: Maximize robustness with disposabilityProcesses should start fast and shut down gracefully - cloud environments restart containers frequently (deploys, scaling, health check failures). Graceful shutdown completes in-flight requests.
- Factor XI: Treat logs as event streamsApplications should write logs to stdout - the infrastructure (Docker, Kubernetes, systemd) captures and routes them. Applications should not manage log files, rotation, or shipping.
- Factor X: Dev/prod parityDifferences between development and production environments cause "works on my machine" bugs. Minimize the gap - same database, same OS, same configuration management.
Output Format
Twelve-factor: zod env validation at startup, Redis session for stateless processes, SIGTERM graceful shutdown, JSON logs to stdout, and Docker Compose dev/prod parity.
Config: z.object({ DATABASE_URL, JWT_SECRET }).parse(process.env)
State: express-session → RedisStore (not in-memory)
Shutdown: process.on("SIGTERM") → server.close() → db.end() → process.exit(0)
Logs: winston Console transport → stdout → infrastructure routes
Dev: docker-compose with same postgres/redis versions as productionExamples
Edge Cases & Gotchas
- SIGTERM vs. SIGKILL - SIGTERM (software terminate) can be caught for graceful shutdown. SIGKILL cannot - it immediately kills the process. Kubernetes sends SIGTERM then waits terminationGracePeriodSeconds (default 30s), then SIGKILL.
- NODE_ENV=production for performance - Express disables certain features (stack traces, view cache) in production mode. Always set NODE_ENV=production in production containers.
- Log aggregation pipelines - stdout logs are ephemeral - containers are killed and logs are lost. In Kubernetes, use Fluentd/Fluentbit DaemonSet to ship pod logs to Elasticsearch/Loki/Datadog before pod termination.
- Config vs. secrets - The twelve-factor "config in environment" rule applies to both config and secrets. But for secrets, environment variables should be sourced from Secrets Manager, not hardcoded in docker-compose or k8s deployment manifests.
When to Break These Rules
- Use file-based config (config.yaml) for configuration that is complex, hierarchical, and doesn't vary per environment - twelve-factor envvars are best for per-environment secrets and URLs, not complex nested config trees.
- Write log files locally for applications that need audit logs with strict retention requirements - ship to external store but also keep a local copy with proper rotation (logrotate).
Reference Files
https://12factor.net/https://www.heroku.com/