Deploying Express.js Apps
Production Node.js Express servers with PM2, Docker, or serverless adapters, plus clustering and graceful shutdown.
PlatformsIntermediate
Why
Express is the most battle-tested Node.js web framework with the largest middleware ecosystem. Deploying it as a long-running Node process (PM2/Docker) gives full control over the runtime, unlike serverless which has cold starts and timeout limits.
When
Use Express for REST APIs, BFF layers, and apps needing WebSockets or long-running tasks. Avoid for edge deploys — use Hono instead.
How
1. Build — tsc or esbuild to compile TS to dist/
2. PM2 — pm2 start dist/server.js -i max (cluster mode, all cores)
3. Docker — node:20-alpine image, CMD ["node", "dist/server.js"]
4. Serverless — @vendia/serverless-express adapter for Lambda
5. Clustering — use cluster module or PM2 for multi-core utilization
6. Graceful shutdown — listen SIGTERM, close server, exit cleanly
7. Reverse proxy — Nginx/Caddy in front for TLS, rate limiting, static
Example
// src/server.ts — Express with graceful shutdown
import express from "express";
const app = express();
app.get("/health", (req, res) => res.json({ ok: true }));
const server = app.listen(3000, () => console.log("listening :3000"));
process.on("SIGTERM", () => {
server.close(() => process.exit(0));
});
// ecosystem.config.js — PM2 cluster
module.exports = {
apps: [{
name: "api", script: "dist/server.js",
instances: "max", exec_mode: "cluster",
max_memory_restart: "500M",
}],
};
// Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY dist/ ./dist/
COPY node_modules/ ./node_modules/
EXPOSE 3000
CMD ["node", "dist/server.js"]
Common Problems
- ⚠Single core only — no cluster mode, one Node process can't use multi-core CPU
- ⚠Memory leak crashes — no max_memory_restart, OOM kills process with no restart
- ⚠Dropped connections on deploy — no graceful shutdown, in-flight requests terminated
Best Practices
- ✓Use PM2 cluster mode or Node cluster — Express is single-threaded, need multiple processes for multi-core
- ✓Always implement graceful shutdown on SIGTERM — drain connections before exit for zero-downtime deploys
- ✓Set up health checks and graceful shutdown — handle SIGTERM to drain in-flight requests before exit