Node.js services are what Dokploy was built around: a repository with a package.json, a start command and a port. The two ways it goes wrong are both about the network: the process listens on 127.0.0.1 instead of 0.0.0.0, or the port in Dokploy's domain settings does not match the one the app binds. This guide deploys an Express API with a PostgreSQL database and a Redis cache, first with Nixpacks for a quick start and then with a Dockerfile for production, on a MassiveGRID server from the Dokploy hosting page.

The two rules that avoid a Bad Gateway

Traefik reaches your container over the Docker network, so the process must bind to every interface and the port must be known:

const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0', () => console.log('listening on ' + port));

In Dokploy, add the domain with that same port (3000 here) and leave the Advanced, Ports setting off; publishing a host port is for reaching the app at ip:port and the Dokploy docs warn it can interfere with the domain. If you ever see 502 Bad Gateway with a running container, it is one of these two, as our domain troubleshooting post explains.

Quick start with Nixpacks or Railpack

Create a project, then Create Service, Application, connect GitHub, GitLab, Bitbucket or Gitea and choose the repository and branch. Leave the build type on Nixpacks (or pick Railpack, its successor, which also supports Node.js): it detects package.json, runs npm ci and uses the start script. Override the command with NIXPACKS_START_CMD or RAILPACK_START_CMD if your script is named differently, and pin the Node version with NIXPACKS_NODE_VERSION or an engines.node field. Add the domain on port 3000 with HTTPS and letsencrypt, deploy, and the API is live. This is fine for staging; production deserves the Dockerfile below.

A production Dockerfile

FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build        # omit if the app is plain JavaScript

FROM node:22-alpine
ENV NODE_ENV=production PORT=3000
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist   # or COPY . . for plain JavaScript
COPY package.json ./
USER app
EXPOSE 3000
HEALTHCHECK --interval=15s --timeout=3s --retries=3 CMD wget -qO- http://127.0.0.1:3000/healthz || exit 1
CMD ["node", "dist/server.js"]

Dependencies are installed in their own layer, so a code-only push reuses the cache and rebuilds in seconds; dev dependencies stay out of the runtime image; the process runs as a non-root user, which Dokploy's hardening guide asks for; and the health check is what makes rolling restarts safe. Select Dockerfile as the build type with path Dockerfile and context .. Add a .dockerignore with node_modules, .git and dist.

Environment, database and cache

Create PostgreSQL and Redis as Dokploy database services in the same project; Dokploy shows their internal hostnames. In the application's Environment tab:

NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://app:<password>@<postgres host>:5432/app
REDIS_URL=redis://:<password>@<redis host>:6379/0
LOG_LEVEL=info

Nothing is exposed publicly: the services talk over the project's Docker network by name. Schedule a daily PostgreSQL backup to an S3 destination on the database service (our backup guide covers the destination). Log to stdout and Dokploy's log viewer shows everything; ship logs off the server if you need retention.

Zero-downtime restarts, workers and scheduled jobs

Dokploy runs applications as Docker Swarm services with rolling updates: with the HEALTHCHECK above (or Dokploy's health check settings), the new container must report healthy before the old one is stopped, so a deploy never serves a 502. Do not run a process manager such as pm2 inside the container; one process per container, and Docker's restart policy handles crashes. Background workers are a second application from the same repository with a different start command and no domain; cron-style tasks are Dokploy Schedule Jobs that run a command in the container; several replicas of a stateless API are a Swarm setting once you have more than one node.

Sizing and cost

An Express API idles at 80 to 200 MB; PostgreSQL wants 512 MB to 1 GB in production and Redis 50 to 200 MB; a build spikes to 500 MB or more.

An API with its database and cache fits the MassiveGRID Starter preset (2 vCPU, 4 GB RAM, 64 GB NVMe) at $9.58 a month on shared cores, or $19.80 on dedicated cores when build times and response latency must stay steady; the Growth preset (4 vCPU, 8 GB) at $19.16 or $39.60 adds room for previews, a worker and a second project. Dokploy is free, MassiveGRID installs it on request after you order from the Dokploy hosting page, and the server runs on a Proxmox HA cluster with Ceph storage and a 15-day money-back guarantee.

Frequently Asked Questions

Which Node.js version does Dokploy use?

With Nixpacks or Railpack it follows the engines.node field in package.json or the NIXPACKS_NODE_VERSION variable; with a Dockerfile you choose the base image, node:22-alpine in this guide. Pin a version in either case so a rebuild months later does not pick up a new major release.

Why does my Node app return 502 Bad Gateway on Dokploy?

Almost always because the process listens on 127.0.0.1 or on a different port than the one in the domain settings. Bind to 0.0.0.0 and the PORT variable, set the domain port to match, keep the Advanced Ports setting off, and redeploy. A failing health check produces the same symptom.

Do I need pm2 or another process manager on Dokploy?

No. Each container runs one Node process, Docker restarts it if it exits, and Swarm rolling updates replace it on deploy. pm2 inside a container hides crashes from the orchestrator and complicates logs; use Swarm replicas if you want several processes.

Dokploy hosting from $9.58 a month

A MassiveGRID VPS preset for Dokploy: 2 vCPU, 4 GB RAM and 64 GB NVMe for $9.58 a month on shared cores or $19.80 on dedicated cores, in New York, London, Frankfurt or Singapore, on a Proxmox HA cluster with Ceph storage, a 100% uptime SLA and a 15-day money-back guarantee. Run the one-line installer yourself or open a ticket and we install Dokploy for you.

Configure a Dokploy server

Further Reading