Skip to content

12-Factor App Compliance

Plate aims to be fully compliant with the 12-factor app methodology. The 12-factor principles guide the design towards portable, self-contained, and operationally simple services — which aligns directly with the Plate goal of being easy to deploy and operate.

This document records the current compliance state and known gaps.

Evaluation

# Factor Status Notes
1 Codebase Single Git repository on GitLab; one codebase deployed to multiple targets (Docker, systemd)
2 Dependencies All dependencies declared in package.json and pinned via package-lock.json; npm ci in both Dockerfile stages ensures exact versions. Minor: --legacy-peer-deps is a temporary workaround for a TypeScript 6 upstream conflict
3 Config All runtime configuration via environment variables (src/config.ts); validateConfig() rejects invalid config at startup with a full error list; configs/.env.example documents every variable. dotenv is loaded unconditionally but never overwrites existing env vars — real environment variables always win, the .env file only fills gaps, and defaults in config.ts cover anything still unset
4 Backing services Snackbox is treated as an attached resource addressed entirely by SNACKBOX_API_URL; swapping instances requires only an env var change
5 Build, release, run Multi-stage Dockerfile separates build (TypeScript compilation) and runtime (compiled dist/ only); no compilation at runtime; npm start runs the pre-built output
6 Processes Stateless per-request handling; no session state or disk writes. The in-memory SimpleCache is process-local — each instance holds its own cache, which is correct 12-factor behavior; cache misses result in Snackbox API calls, not stale data
7 Port binding Two self-contained HTTP servers: main app on PORT (default 3000) and Prometheus metrics on METRICS_ADDRESS (default localhost:9091); no external app server required
8 Concurrency Scales horizontally by running multiple container instances behind a load balancer; no shared state between instances; container-level replication is the recommended scaling mechanism
9 Disposability SIGTERM and SIGINT trigger graceful shutdown: idle connections are dropped immediately on both servers, active connections drain within SHUTDOWN_TIMEOUT (default 10 s), then closeAllConnections() forces closure on both. process.exit(0) is called only after both the main server and the metrics server have confirmed closure. Docker HEALTHCHECK enables orchestrator-level health routing
10 Dev/prod parity Same Node.js 24-alpine base image in all environments; "engines" field enforces minimum version; Docker Compose provides a one-command local stack matching production topology; LOG_FORMAT switches automatically between pretty (dev) and json (prod)
11 Logs All output written to stdout via Pino; no log files or rotation in the application. JSON format in production is compatible with log aggregation tooling. Aggregation is left to the operator (Docker log driver, journald, Loki, etc.)
12 Admin processes One-off CLI scripts ship alongside the application and load config from the same environment variables: npm run theme:validate validates a theme directory, npm run generate:types regenerates types from the Snackbox OpenAPI spec. Plate is read-only — no database migrations or write operations

Summary

Fully compliant (12 / 12): all factors pass.

Minor deviations (non-blocking):

Deviation Factor Impact
Per-process in-memory cache VI — Processes Expected behavior; operators running multiple instances should size CACHE_MAX_ENTRIES and TTLs to account for independent caches