NodeJSDeploying Node.js Apps

Deploying Node.js Apps

Deployment is everything between "it works on my laptop" and "users can reach it reliably on the internet." A Node app in production is a different beast: it must survive crashes, use all the CPU cores, sit behind a proxy that terminates TLS, read its config from the environment, shut down gracefully, and be observable when something goes wrong. This page is the map for the section — the gap between dev and prod, the main hosting models (PaaS, containers, VMs, serverless), the anatomy of a typical production stack, and the deployment workflow — before later pages dive into PM2, Nginx, Docker, the cloud, and CI/CD.

What changes from dev to production

Concern

Development

Production

Process

One node/nodemon, restart by hand

Supervised, auto-restart, multi-core (PM2/cluster)

TLS / HTTPS

Usually plain HTTP

TLS terminated at a proxy or load balancer

Config

.env file

Injected env vars / secrets manager

NODE_ENV

development

production (faster, no leaked stack traces)

Errors

Full stack traces to screen

Logged & monitored; generic message to users

Crashes

You notice and restart

Supervisor restarts; alerting fires

Static assets

Served by Node

Served by CDN / proxy

Production demands process supervision, TLS, injected config, observability, and crash recovery — none of which dev needs
Running in production means meeting a set of concerns that simply don't exist on your laptop. A dev process you start and stop by hand becomes a **supervised** process that restarts on crash and runs across **all CPU cores**. Plain HTTP becomes **TLS**, usually terminated by a [reverse proxy](/nodejs/reverse-proxy-nginx) or load balancer in front of Node. A `.env` file becomes **injected environment variables** from a secrets store, with [`NODE_ENV=production`](/nodejs/node-env) for speed and safety. Errors stop going to your screen and start going to **logs and monitoring**, with only generic messages shown to users. And you need **observability** — [health checks](/nodejs/health-checks), [logging](/nodejs/logging-intro), metrics — because when something breaks you can't just look at your terminal. The rest of this section equips you for each of these.
Hosting models

Model

You manage

Examples

Best for

PaaS

Just your code

Render, Railway, Fly.io, Heroku

Fastest path to live; small/medium apps

Containers

Image + orchestration

Docker + Kubernetes/ECS

Portability, scale, complex systems

VMs / bare metal

OS, runtime, everything

EC2, DigitalOcean droplet

Full control; legacy/specific needs

Serverless

Just a function

Lambda, Cloud Functions

Spiky/low traffic, event-driven, no ops

The four models trade control for convenience — PaaS is easiest, VMs give the most control, serverless removes servers entirely
Where you run Node falls on a spectrum from "manage everything" to "manage nothing." **PaaS** (Render, Railway, Fly.io, Heroku) takes your code or container and handles the servers, TLS, and scaling — the fastest route to a live app and the right default for most small-to-medium projects. **[Containers](/nodejs/docker)** (Docker images run on Kubernetes, ECS, etc.) give portability and fine-grained scaling at the cost of managing the orchestration. **VMs / bare metal** (EC2, droplets) hand you full control and full responsibility — OS patches, the runtime, the proxy, everything. **[Serverless](/nodejs/serverless)** runs your code as functions with no server to manage at all, ideal for spiky or event-driven workloads but with cold-start and execution-model trade-offs. Pick by how much operational work you want to own versus how much control and scale you need.
Anatomy of a typical production stack

Text
Internet
   │  HTTPS
   ▼
[ Load balancer / CDN ]         ← TLS termination, static assets, DDoS edge
   │  HTTP (internal)
   ▼
[ Reverse proxy: Nginx ]        ← routing, gzip, rate limiting, buffering
   │
   ▼
[ Node processes × N cores ]    ← PM2 / cluster / container replicas (stateless)
   │           │
   ▼           ▼
[ Database ] [ Redis cache / queue ]   ← shared state lives HERE, not in Node
Node sits behind a proxy/load balancer, runs as multiple stateless instances, and keeps shared state in a database/cache
A production Node deployment is rarely just `node app.js` exposed to the world. At the edge, a **load balancer / CDN** terminates TLS, serves static assets, and absorbs attack traffic. Behind it, a **[reverse proxy](/nodejs/reverse-proxy-nginx)** (commonly Nginx) handles routing, compression, rate limiting, and request buffering. Node itself runs as **multiple instances** — one per CPU core via [PM2](/nodejs/pm2)/[cluster](/nodejs/cluster-module) or as several container replicas — and those instances are **stateless**: any shared state (sessions, cache, jobs) lives in a **database** and **[Redis](/nodejs/redis)**, not in process memory, so any instance can serve any request and you can add or replace instances freely. Internalizing this shape explains *why* the production concerns in this section exist — statelessness enables [scaling](/nodejs/scaling-strategies), the proxy handles cross-cutting edge work, and supervision keeps the Node tier alive.
The deployment workflow
  • Build — install production dependencies, compile TypeScript, produce the runnable artifact (a dist/ or a Docker image).

  • Test & check — run tests, lint, and type-check in CI; a red build never deploys.

  • Configure — supply environment variables / secrets for the target environment; set NODE_ENV=production.

  • Release — push the artifact to the host (PaaS, registry, server) and start it under a process manager.

  • Migrate — run database migrations as a controlled step, ordered safely relative to the code rollout.

  • Verify — confirm health checks pass and key paths work before sending real traffic (and be ready to roll back).

  • Observe — watch logs, metrics, and error rates after release; alert on regressions.

Deployment is a repeatable pipeline — build, test, configure, release, migrate, verify, observe — ideally automated end to end
Reliable deployment is a **repeatable pipeline**, not a manual ritual. The shape is consistent everywhere: **build** the artifact (compiled code or a container image), **test and check** it in [CI](/nodejs/ci-cd) so nothing broken ships, **configure** it with the target environment's variables and secrets, **release** it to the host under a [process manager](/nodejs/pm2), run **database migrations** as a deliberate ordered step, **verify** via [health checks](/nodejs/health-checks) and smoke tests before it takes full traffic (with rollback ready), and then **observe** logs and metrics for regressions. Automating this end to end — so a merge to main flows through build → test → deploy without human steps — is the goal that the [CI/CD page](/nodejs/ci-cd) builds toward. The more automated and repeatable, the less risky each release becomes.
Next
Before any of that, get your app itself production-ready: [Preparing for Production](/nodejs/preparing-for-production).