STEP 01Confirm the entry point
This recipe is for a plain JavaScript Node app with server.js and package-lock.json. A TypeScript or framework project needs its own build stage. Verify the project supports Node 24 LTS and that required production dependencies are not mistakenly listed only as devDependencies.
STEP 02Listen on the right interface
Inside a container, listen on 0.0.0.0 and read the port from the environment. On a host with a local reverse proxy, bind the app to 127.0.0.1 instead. Avoid exposing the raw application port publicly just to make a proxy error disappear.
const port = Number(process.env.PORT || 3000);
app.listen(port, "0.0.0.0");STEP 03Use a non-root runtime
Create this Dockerfile and keep secrets out of the build context with .dockerignore. The node user runs the app with fewer privileges. The sample assumes the application does not need to write into its code directory.
FROM node:24-bookworm-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --chown=node:node . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]STEP 04Add restart handling and HTTPS
Use the Docker Compose recipe with restart: unless-stopped and Caddy. Handle SIGTERM in your app so connections close cleanly during deployment. Persist uploads separately and log to stdout/stderr. A container restart policy cannot fix a missing environment variable; inspect the logs.
docker compose logs --tail=100 app
docker compose psCOPY → YOUR AI
Take the next step to your AI.
A safe starting prompt for this guide. No secrets. Works with ChatGPT, Claude and other assistants.
Sources and technical documentation
Node.js releases ↗Docker documentation ↗This recipe is a pattern for the stated prerequisites. Verify project compatibility and your actual server configuration before applying it to a live service.