docker
Docker Compose for Node.js Application
Docker Compose config for a Node.js app with hot-reload, debugging, and development-friendly defaults.
Overview
A Docker Compose setup optimized for Node.js application development. Features hot-reloading with volume mounts, Node.js debugging support, environment variable management, and proper signal handling for graceful shutdowns.
Configuration
# docker-compose.yml
services:
app:
build:
context: . # Build from current directory
dockerfile: Dockerfile # Path to Dockerfile
target: development # Use dev stage of multi-stage build
args:
NODE_VERSION: 20 # Pass build arguments
container_name: node-app
restart: unless-stopped
ports:
- "3000:3000" # Application port
- "9229:9229" # Node.js debugger port
environment:
NODE_ENV: development
PORT: 3000
LOG_LEVEL: debug
env_file:
- .env # Load additional env vars from file
volumes:
- .:/app # Mount source code for hot-reload
- /app/node_modules # Anonymous volume — keep container's node_modules
- app_logs:/app/logs # Persist log files
# Use nodemon or tsx for auto-restart on file changes
command: npx nodemon --inspect=0.0.0.0:9229 src/index.ts
# Proper signal handling for graceful shutdown
init: true # Use tini as PID 1
stop_grace_period: 10s # Wait 10s before SIGKILL
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://localhost:3000/health').then(r => { if (!r.ok) throw new Error() })"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
# Resource constraints
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 256M
# Run tests in a separate service
test:
build:
context: .
dockerfile: Dockerfile
target: development
profiles:
- test # Only runs with --profile test
environment:
NODE_ENV: test
CI: "true"
volumes:
- .:/app
- /app/node_modules
command: npm test
volumes:
app_logs:
driver: local
# Dockerfile (referenced above)
FROM node:20-alpine AS development
WORKDIR /app
# Install dependencies first (layer caching)
COPY package*.json ./
RUN npm ci
# Copy source code
COPY . .
# Expose ports
EXPOSE 3000 9229
# Default command (overridden by docker-compose)
CMD ["npm", "run", "dev"]
Key Options Explained
/app/node_modulesanonymous volume — Prevents the host’snode_modulesfrom overriding the container’s. The container keeps its own copy installed during build.--inspect=0.0.0.0:9229— Enables Node.js debugger on all interfaces so VS Code or Chrome DevTools can attach from the host.init: true— Runstinias PID 1 to properly forward signals. Without this, Node.js won’t receive SIGTERM for graceful shutdown.target: development— Uses only the development stage from a multi-stage Dockerfile, skipping production optimization steps.env_file— Loads environment variables from a.envfile. Docker Compose reads this automatically, keeping secrets out ofdocker-compose.yml.
Common Modifications
- Use
watchmode: Replace thevolumesmount with Docker Composedevelop.watchfor selective syncing and rebuild triggers. - Add database dependency: Include
depends_on: postgres: condition: service_healthyto ensure the database starts first. - TypeScript compilation: Change command to
npx tsx watch src/index.tsfor faster TypeScript execution without a separate compilation step. - Production override: Create
docker-compose.prod.ymlthat removes debug ports, volume mounts, and setsNODE_ENV=production. - Monorepo support: Adjust context and volumes to mount specific workspace packages.