← Back to articles

How to Deploy a Node.js App to Production (2026)

Your Node.js app works locally. Now what? Here's how to deploy it to production with HTTPS, environment variables, monitoring, and zero-downtime updates. Four approaches from simplest to most control.

Option 1: Railway (Simplest)

Railway deploys from GitHub with zero config. Push code → it's live.

Step 1: Connect GitHub

  1. Go to railway.app → New Project → Deploy from GitHub
  2. Select your repo
  3. Railway detects Node.js automatically

Step 2: Add Environment Variables

In the Railway dashboard: Variables tab → add your env vars (DATABASE_URL, API_KEY, etc.)

Step 3: Configure Start Command

Railway reads package.json:

{
  "scripts": {
    "start": "node dist/index.js",
    "build": "tsc"
  }
}

Step 4: Add a Domain

Settings → Networking → Generate Domain (or add custom domain).

That's it. Railway handles:

  • HTTPS (automatic)
  • Build and deploy (on every push)
  • Scaling (horizontal and vertical)
  • Logs and metrics

Pricing: $5/month + usage (~$5-20/month for small apps). Free trial available.

Best for: Startups, side projects, APIs that need to be live fast.

Option 2: Fly.io (Edge Deployment)

Fly.io deploys your app to servers worldwide. Great for low-latency APIs.

Step 1: Install CLI

curl -L https://fly.io/install.sh | sh
fly auth login

Step 2: Launch

cd your-node-app
fly launch

Fly detects Node.js, creates a fly.toml config:

[build]
  [build.args]
    NODE_VERSION = "22"

[[services]]
  internal_port = 3000
  protocol = "tcp"
  [services.concurrency]
    hard_limit = 250
    soft_limit = 200
  [[services.ports]]
    port = 443
    handlers = ["tls", "http"]

Step 3: Set Secrets

fly secrets set DATABASE_URL="postgres://..." API_KEY="sk-..."

Step 4: Deploy

fly deploy

Pricing: Free for small apps (3 shared VMs). Pay-as-you-go after.

Best for: Global APIs needing low latency. Websocket apps.

Option 3: VPS with Docker (Most Control)

Deploy to a VPS (Hetzner, DigitalOcean, Linode) with Docker.

Step 1: Create Dockerfile

FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./

ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/index.js"]

Step 2: Set Up VPS

# On your VPS (Ubuntu)
apt update && apt install docker.io docker-compose nginx certbot -y

Step 3: Docker Compose

# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    env_file: .env
    restart: unless-stopped

Step 4: Nginx Reverse Proxy

server {
    listen 80;
    server_name api.yourdomain.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Step 5: HTTPS with Certbot

certbot --nginx -d api.yourdomain.com

Step 6: Deploy

docker compose up -d --build

Pricing: VPS from $4/month (Hetzner) to $12/month (DigitalOcean). You manage everything.

Best for: Maximum control, lowest cost at scale, compliance requirements.

Option 4: Coolify (Self-Hosted PaaS)

Coolify gives you Railway-like experience on your own VPS.

Step 1: Install Coolify

curl -fsSL https://cdn.coolify.io/install.sh | bash

Step 2: Connect GitHub

In Coolify dashboard: New Resource → GitHub → Select repo

Step 3: Configure and Deploy

Coolify auto-detects Node.js, sets up HTTPS, and deploys on push. Same experience as Railway but on your $4/month VPS.

Pricing: Free (open source) + VPS cost ($4-20/month)

Best for: Teams who want Railway DX at VPS prices.

Production Checklist

Before going live, ensure:

Security

  • Environment variables (not hardcoded secrets)
  • HTTPS enabled
  • CORS configured properly
  • Rate limiting on API endpoints
  • Helmet.js for security headers
import helmet from 'helmet'
import cors from 'cors'
app.use(helmet())
app.use(cors({ origin: 'https://yourfrontend.com' }))

Performance

  • NODE_ENV=production
  • Gzip/Brotli compression
  • Database connection pooling
  • Response caching where appropriate

Monitoring

  • Error tracking (Sentry)
  • Uptime monitoring (BetterStack)
  • Log aggregation (Axiom or BetterStack)
  • Health check endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() })
})

Reliability

  • Graceful shutdown handling
  • Auto-restart on crash (Docker restart policy or PM2)
  • Database migrations run before deploy
  • Rollback plan
process.on('SIGTERM', () => {
  server.close(() => {
    db.end()
    process.exit(0)
  })
})

FAQ

Which deployment method is best?

Railway for speed. VPS + Docker for control and cost. Fly.io for global distribution. Coolify for Railway experience on your own hardware.

How much does hosting cost?

Small API: $5-20/month (any platform). Medium SaaS: $20-100/month. Scale beyond that: VPS is cheapest.

Do I need Docker?

Not for Railway or Fly.io (they handle containerization). Yes for VPS deployment. Docker ensures your app runs the same everywhere.

How do I handle zero-downtime deployments?

Railway and Fly.io handle this automatically. For VPS: use Docker rolling updates or blue-green deployment with nginx.

Bottom Line

For most Node.js apps in 2026: Railway for the fastest path to production. Fly.io for global deployment. VPS + Docker for maximum control and lowest cost. Coolify for the best of both worlds.

Start with Railway. Move to VPS when you need to optimize costs or need more control.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.