1
Create a multi-stage image
Build dependencies separately and run the compiled application as a non-root user.
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
USER node
CMD ["node", "dist/main.js"]2
Define the service
Bind the API to loopback when a reverse proxy is on the same host and include a health check.
services:
api:
build: .
restart: unless-stopped
env_file: .env.production
ports:
- "127.0.0.1:<port>:<port>"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:<port>/health"]
interval: 30s
timeout: 5s
retries: 3Important: Never commit .env.production or bake secrets into the image.
3
Build and start
Validate the resolved Compose model, then build and launch in the background.
docker compose config
docker compose up -d --build4
Inspect health and logs
Confirm the container is healthy and watch startup output for configuration errors.
docker compose ps
docker compose logs --tail=100 -f api5
Deploy an update
Pull code or the pinned image, rebuild only what changed, and remove obsolete containers.
Commandwarning
docker compose pull
docker compose up -d --build --remove-orphansFinal verification
- ✓ docker compose config reports no errors
- ✓ docker compose ps reports a healthy API
- ✓ The health endpoint responds through the reverse proxy
- ✓ No secret file is tracked by Git