Skip to content
Laviyx Learn

Try

Node.js and Express course menu

How to Deploy a Node.js and Express App

Take a Node.js API to production: preparing the app (PORT, start script, engines), environment variables on the host, platform options (PaaS, containers, VPS), a Dockerfile, process managers and restarts, reverse proxies and HTTPS, databases and migrations, and a deployment checklist.

Advanced6 min readUpdated
On this page

Deploying means running your app on a server that is always on and reachable from the internet. The details depend on the platform, but every Node.js deployment needs the same preparation.

Prepare the app#

1. Listen on the port the platform gives you. Hosting platforms set a PORT environment variable:

JavaScript
const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => console.log(`Listening on ${port}`));

2. Define how to start it, and which Node.js version it needs:

package.json (JSON)
{
  "type": "module",
  "scripts": {
    "start": "node src/server.js",
    "test": "node --test"
  },
  "engines": {
    "node": ">=22"
  }
}

3. Install only production dependencies on the server: npm ci --omit=dev installs exactly what the lock file lists, without dev tools.

4. Configuration through environment variables set in the platform's dashboard or secrets manager (database URL, JWT secret, NODE_ENV=production), never in the repository. NODE_ENV=production also makes Express and many libraries skip development-only work, such as stack traces in error pages.

5. Health check and graceful shutdown, as in Configuration, Logging and Shutdown.

Where to run it#

OptionWhat you manageExamples
Platform as a service (PaaS)your code and settings; the platform builds, runs, scales and adds HTTPSRender, Railway, Fly.io, Heroku, Google Cloud Run, AWS App Runner
Containersa Docker image; an orchestrator runs itCloud Run, AWS ECS, Kubernetes
Virtual private server (VPS)the whole machine: OS updates, Node, proxy, firewall, process managerAWS EC2, DigitalOcean, Linode
Serverless functionsindividual functions, run per requestAWS Lambda, Vercel and Netlify functions

For a first deployment, a PaaS is the simplest: connect the Git repository, set the build command (npm ci), the start command (npm start) and environment variables, and every push deploys. Check each provider's current pricing and limits.

A Dockerfile#

Containers package the app with its exact Node.js version and dependencies, so it runs the same everywhere:

Dockerfile (Text)
FROM node:24-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
USER node
EXPOSE 3000
CMD ["node", "src/server.js"]
.dockerignore (Text)
node_modules
.env
.git
test

Copying package*.json and running npm ci before copying the source lets Docker reuse the cached dependency layer when only your code changes. USER node runs the app without root privileges. Build and run with docker build -t shop-api . and docker run -p 3000:3000 --env-file .env shop-api. (Docker must be installed to run these commands; they are shown here as a reference.)

Keeping it running#

A crash must not take the API down for good:

  • PaaS and container platforms restart crashed processes automatically.
  • On a VPS, use a process manager: systemd (built into Linux) or PM2 (pm2 start src/server.js --name shop-api), which restarts on crashes and on reboot, and collects logs.
  • Node runs your JavaScript on one CPU core. To use several cores, run several instances behind a load balancer (PaaS "instances", PM2 cluster mode, or containers).

Reverse proxy and HTTPS#

On a VPS, a reverse proxy such as nginx or Caddy sits in front of Node: it terminates HTTPS (with free certificates from Let's Encrypt), serves static files, compresses responses and forwards API requests to Node on a local port. PaaS platforms do this for you. Behind a proxy, set app.set("trust proxy", 1) so req.ip and req.protocol reflect the real client.

Databases in production#

  • Use a managed database (managed PostgreSQL or MySQL from your cloud or PaaS provider) with automatic backups.
  • Change the schema with migrations: versioned scripts applied in order, so every environment has the same tables (tools: Prisma Migrate, Drizzle Kit, Knex, node-pg-migrate).
  • Keep the database in the same region as the app for low latency, and never expose it publicly.

Deployment checklist#

  • Tests pass in CI before deploying.
  • PORT, NODE_ENV=production and all secrets set on the host; .env not committed.
  • Health endpoint, structured logs and error monitoring (such as Sentry) in place.
  • HTTPS, Helmet, CORS limited to your front-end origins, rate limiting on auth routes.
  • Graceful shutdown on SIGTERM.
  • Database backups and migrations.
  • A way to roll back to the previous version.

Common mistakes#

Key takeaway#

Next, build complete projects: Project: Notes API with a Database.

Finished this lesson?