Platform Deploy Basics
10 examples for running Node.js 24 APIs on Kubernetes, ECS, or Cloud Run using 12-factor principles - 7 basic and 3 intermediate.
Prerequisites
mkdir platform-api && cd platform-api
npm init -y
npm pkg set type=module
npm install express@5
npm install -D typescript@5.6For Kubernetes manifests and scaling, see Kubernetes Deployment and HPA & Resource Limits.
Basic Examples
1. Config from Environment
import { z } from "zod";
const envSchema = z.object({
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "production", "test"]),
DATABASE_URL: z.string().url(),
});
export const env = envSchema.parse(process.env);- No config files baked into images
- Validate at boot; crash loop if invalid (fail fast)
- See Configuration Basics
2. Port Binding
import express from "express";
import { env } from "./env.js";
const app = express();
app.get("/health", (_req, res) => res.json({ status: "ok" }));
app.listen(env.PORT, "0.0.0.0");- Platforms inject
PORT; default 3000 for local only - Bind
0.0.0.0inside containers
3. Logs to stdout
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
console.log(JSON.stringify({
method: req.method,
path: req.path,
status: res.statusCode,
ms: Date.now() - start,
}));
});
next();
});- Aggregators collect container stdout
- No log files inside the container filesystem
4. Stateless Process
// Bad: in-memory session store at scale
const sessions = new Map<string, string>();
// Good: Redis or DB session store
import { getSession } from "./session-store.js";- Horizontal scaling requires shared state external to the pod
- Uploads go to S3, not local disk
5. Release vs Run
# Release phase (Job / initContainer)
npx prisma migrate deploy
# Run phase (Deployment CMD)
node dist/main.js- Migrations run once per release, not per replica
- App containers only start the HTTP server
6. Health Endpoints
app.get("/health", (_req, res) => res.json({ status: "ok" }));
app.get("/ready", async (_req, res) => {
const ok = await pingDatabase();
res.status(ok ? 200 : 503).json({ status: ok ? "ready" : "not_ready" });
});- Liveness vs readiness separation - Health & Readiness Probes
7. NODE_ENV=production
env:
- name: NODE_ENV
value: production- Enables production optimizations in frameworks
- Set in Deployment spec, not in Dockerfile
ENValone
Intermediate Examples
8. Kubernetes Deployment Skeleton
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: ghcr.io/acme/api:sha-abc123
ports:
- name: http
containerPort: 3000
envFrom:
- configMapRef:
name: api-config
- secretRef:
name: api-secrets
readinessProbe:
httpGet:
path: /ready
port: http
livenessProbe:
httpGet:
path: /health
port: httpSee Kubernetes Deployment for full manifest.
9. Graceful Shutdown Hook
const server = app.listen(env.PORT, "0.0.0.0");
process.on("SIGTERM", () => {
server.close(() => process.exit(0));
});- K8s sends SIGTERM before pod removal
- See Graceful Shutdown
10. 12-Factor Checklist
| Factor | Node implementation |
|---|---|
| Codebase | One git repo per service |
| Dependencies | package-lock.json + npm ci in image |
| Config | process.env + Zod |
| Backing services | Postgres, Redis as attached resources |
| Build, release, run | CI build image, CD deploy tag, CMD runs node |
| Processes | One container = one node process |
| Port binding | PORT env |
| Concurrency | Scale replicas via HPA |
| Disposability | Fast boot, SIGTERM drain |
| Dev/prod parity | Same Docker image locally and prod |
FAQs
Kubernetes vs ECS vs Cloud Run?
K8s: full control, multi-cloud. ECS: AWS-native, less ops than K8s. Cloud Run: simplest HTTP autoscaler. See ECS Fargate & Cloud Run.
Do we need Helm?
Helm helps when you have many services and environments. Start with plain YAML or Kustomize for one API.
Where do secrets go?
K8s Secrets, ECS task secrets, or external secret operators - ConfigMaps & Secrets.
How many replicas?
Minimum 2 for HA. Set requests/limits from load tests - HPA & Resource Limits.
Can we run PM2 in Kubernetes?
No. K8s is the process supervisor. PM2 is for VMs - PM2 & systemd.
How does this relate to Docker?
Platform deploy runs images built per Docker Best Practices.
Related
- Kubernetes Deployment - replicas and rolling updates
- ConfigMaps & Secrets - env injection
- ECS Fargate & Cloud Run - managed options
- Graceful Shutdown - SIGTERM
- Platform Deploy Best Practices - section checklist
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.