Back to Blog
Guide8 min read2026-05-01

Node.js Production Deployment: Best Practices Guide

Learn how to deploy Node.js applications to production with Docker, proper process management, environment configuration, and health checks on a cloud PaaS.

Node.js Production Deployment: Best Practices Guide

Node.js powers millions of production APIs, real-time applications, and backend services worldwide. But moving from node index.js in development to a resilient production deployment requires attention to containerization, process management, health checks, and environment configuration.

This guide covers the best practices for deploying Node.js applications to production using Docker and [PandaStack](https://pandastack.io).

Production vs Development: Key Differences

In development, you likely run your Node.js app directly, restart manually on crashes, and store configuration in a local .env file. In production:

  • The app runs inside a Docker container
  • The process must restart automatically on crash
  • All secrets come from environment variables injected at runtime
  • A health check endpoint tells the platform when the app is ready

Writing a Production-Ready Dockerfile

A multi-stage Docker build keeps your production image lean by excluding dev dependencies:

FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000

COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser

EXPOSE 3000
CMD ["node", "src/index.js"]

Key practices in this Dockerfile:

  • npm ci --only=production installs only production dependencies
  • A non-root user (appuser) runs the process for security
  • NODE_ENV=production enables Express performance optimizations and disables dev tooling

Application Entry Point

Your application should start cleanly, bind to the port from the environment, and export a health check endpoint:

// src/index.js
const express = require('express')
const app = express()

const PORT = process.env.PORT || 3000

app.use(express.json())

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok', uptime: process.uptime() })
})

// your routes
app.use('/api', require('./routes'))

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

module.exports = app

Always read the port from process.env.PORT — cloud platforms, including PandaStack, inject the port dynamically.

Configuring pandastack.json

PandaStack uses a pandastack.json at your project root to understand your application:

{
  "type": "container",
  "healthCheckPath": "/health"
}

The healthCheckPath tells PandaStack which endpoint to poll after startup. Traffic is only routed to your container once this endpoint returns HTTP 200, giving your app time to fully initialize — including database connections and cache warming.

Environment Variables

Never hardcode secrets. Set all environment variables from the PandaStack dashboard at [dashboard.pandastack.io](https://dashboard.pandastack.io):

NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:pass@host:5432/mydb
REDIS_URL=redis://redis.internal:6379
JWT_SECRET=your-jwt-signing-secret
API_KEY=third-party-api-key

In your code, read these with process.env:

const { DATABASE_URL, JWT_SECRET, REDIS_URL } = process.env

if (!DATABASE_URL || !JWT_SECRET) {
  console.error('Missing required environment variables')
  process.exit(1)
}

Failing fast on missing configuration prevents silent runtime errors in production.

Graceful Shutdown

Production Node.js apps should handle SIGTERM gracefully, finishing in-flight requests before exiting:

const server = app.listen(PORT)

process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully')
  server.close(() => {
    console.log('HTTP server closed')
    process.exit(0)
  })
})

PandaStack sends SIGTERM before stopping a container during re-deployments, giving your app time to drain requests.

Connecting a Database

Provision a managed PostgreSQL, MySQL, MongoDB, or Redis instance from the Databases section of [dashboard.pandastack.io](https://dashboard.pandastack.io). Copy the connection string and set it as DATABASE_URL. Avoid creating database connections at module load time — use a connection pool that initializes asynchronously:

const { Pool } = require('pg')
const pool = new Pool({ connectionString: process.env.DATABASE_URL })

module.exports = { query: (text, params) => pool.query(text, params) }

GitHub Integration and Auto-Deploy

Connect your GitHub repository from the PandaStack dashboard. Every merge to main triggers an automatic build and zero-downtime deployment. No manual steps, no SSH access required.

For manual or scripted deployments, use the CLI:

npm install -g @pandastack/cli
panda deploy

Production Checklist

  • NODE_ENV=production is set
  • Port is read from process.env.PORT
  • All secrets are environment variables
  • /health returns HTTP 200 with a valid response
  • Container runs as a non-root user
  • SIGTERM handler closes the server gracefully
  • Database uses a connection pool, not individual connections

Logging Best Practices

Node.js logs to stdout and stderr by default, which PandaStack captures and displays in your deployment dashboard. Avoid writing log files inside the container — they consume disk space and are lost when the container restarts.

For structured logging, consider a lightweight library like pino:

npm install pino
const pino = require('pino')
const logger = pino({ level: process.env.LOG_LEVEL || 'info' })

logger.info({ port: PORT }, 'Server started')
logger.error({ err: error }, 'Unhandled error')

Structured JSON logs make it easy to search for specific request IDs, error codes, or user sessions across your deployment history.

Visit [docs.pandastack.io](https://docs.pandastack.io) for the full deployment reference.

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →

See also