Deploy a Node / Express API

A JSON API on Express, live on an HTTPS URL.

An Express API is the simplest thing to run on Dockhold: it's already a long-running server. You just need it to listen on the assigned port. This recipe is the whole path.

In a hurry? Start from the ready-madeexpress-api-starter template: click Use this template on GitHub, thendeploy it. Or follow the steps below to set up a project you already have.

1. Listen on the assigned port

Read the port from the environment and bind all interfaces:

const express = require("express");
const app = express();

app.get("/health", (_req, res) => res.send("ok"));

const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0", () => {
  console.log(`listening on ${port}`);
});

And give it a start script in package.json:

{
  "scripts": {
    "start": "node server.js"
  }
}

Your app must listen on 0.0.0.0 and read its port from thePORT environment variable. Never localhost, never a hardcoded port. Dockhold assigns PORT at runtime; an app that ignores it can't receive traffic.

2. Read config and secrets from the environment

Set plain config (feature flags, public URLs) in the dashboard. Put secrets (API keys, tokens, passwords) in the Vault; they're encrypted and injected at runtime, so they never live in your repo. Read everything fromprocess.env; don't hardcode values.

3. Add a database (optional)

Enable the managed Postgres add-on and Dockhold injectsDATABASE_URL. Read it directly:

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

Apps are stateless. The filesystem is wiped on every restart and deploy. Persist anything you need to keep in the database, not on local disk.

4. Connect the repo and deploy

  1. Push the project to a GitHub repository.
  2. Open the dashboard, connect GitHub, and pick the repo.
  3. On a paid plan (any account with compute added), Dockhold detects the Node project, runs npm install, and starts it with npm run start. Nothing else to do.
  4. On a free account, add the Dockerfile below at the root of your repo first. Auto-detection is a paid feature, so without one the deploy stops right away and tells you.
# Dockerfile
FROM node:22-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]

Your API goes live at https://<your-app>.dockhold.appwith HTTPS handled for you. Every later push to your main branch redeploys.

Troubleshooting

  • URL times out: you must pass "0.0.0.0" toapp.listen and use process.env.PORT. Bindinglocalhost or a fixed port means no traffic reaches you.
  • CORS errors from a browser frontend: set the allowed origin to your deployed frontend URL via an env var; don't hardcodehttp://localhost.
  • Database connection refused: readprocess.env.DATABASE_URL. Don't point at localhost.

Next