FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 CMD ["node", "src/index.js"]
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 CMD ["node", "src/index.js"]
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 CMD ["node", "src/index.js"]
node_modules
.git
.gitignore
*.log
.env
.env.local
.env.*.local
dist
build
.next
Dockerfile
docker-compose*.yml
README.md
.DS_Store
coverage
node_modules
.git
.gitignore
*.log
.env
.env.local
.env.*.local
dist
build
.next
Dockerfile
docker-compose*.yml
README.md
.DS_Store
coverage
node_modules
.git
.gitignore
*.log
.env
.env.local
.env.*.local
dist
build
.next
Dockerfile
docker-compose*.yml
README.md
.DS_Store
coverage
version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/myapp - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started volumes: - .:/app - /app/node_modules db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: password POSTGRES_DB: myapp volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine volumes: - redis_data:/data volumes: postgres_data: redis_data:
version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/myapp - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started volumes: - .:/app - /app/node_modules db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: password POSTGRES_DB: myapp volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine volumes: - redis_data:/data volumes: postgres_data: redis_data:
version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/myapp - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started volumes: - .:/app - /app/node_modules db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: password POSTGRES_DB: myapp volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine volumes: - redis_data:/data volumes: postgres_data: redis_data:
volumes: - .:/app - /app/node_modules
volumes: - .:/app - /app/node_modules
volumes: - .:/app - /app/node_modules
# Stage 1: Builder
FROM node:20-alpine AS builder WORKDIR /app COPY package*.json tsconfig.json ./
RUN npm ci COPY src ./src
RUN npm run build # Stage 2: Production
FROM node:20-alpine AS production WORKDIR /app RUN addgroup -g 1001 -S nodejs && \ adduser -S nodeuser -u 1001 COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force COPY --from=builder /app/dist ./dist USER nodeuser EXPOSE 3000 CMD ["node", "dist/index.js"]
# Stage 1: Builder
FROM node:20-alpine AS builder WORKDIR /app COPY package*.json tsconfig.json ./
RUN npm ci COPY src ./src
RUN npm run build # Stage 2: Production
FROM node:20-alpine AS production WORKDIR /app RUN addgroup -g 1001 -S nodejs && \ adduser -S nodeuser -u 1001 COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force COPY --from=builder /app/dist ./dist USER nodeuser EXPOSE 3000 CMD ["node", "dist/index.js"]
# Stage 1: Builder
FROM node:20-alpine AS builder WORKDIR /app COPY package*.json tsconfig.json ./
RUN npm ci COPY src ./src
RUN npm run build # Stage 2: Production
FROM node:20-alpine AS production WORKDIR /app RUN addgroup -g 1001 -S nodejs && \ adduser -S nodeuser -u 1001 COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force COPY --from=builder /app/dist ./dist USER nodeuser EXPOSE 3000 CMD ["node", "dist/index.js"]
services: app: env_file: - .env
services: app: env_file: - .env
services: app: env_file: - .env
# Development with hot reload
docker compose up # Force rebuild when dependencies change
docker compose up --build # Run in the background
docker compose up -d # Watch logs in real time
docker compose logs -f app # Get inside the container to debug
docker compose exec app sh # Wipe everything and start fresh
docker compose down -v
# Development with hot reload
docker compose up # Force rebuild when dependencies change
docker compose up --build # Run in the background
docker compose up -d # Watch logs in real time
docker compose logs -f app # Get inside the container to debug
docker compose exec app sh # Wipe everything and start fresh
docker compose down -v
# Development with hot reload
docker compose up # Force rebuild when dependencies change
docker compose up --build # Run in the background
docker compose up -d # Watch logs in real time
docker compose logs -f app # Get inside the container to debug
docker compose exec app sh # Wipe everything and start fresh
docker compose down -v
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })" - The final image only has the compiled code and production dependencies. No TypeScript, no ts-node, no devDependencies whatsoever. Smaller images, more secure, faster to deploy.
- USER nodeuser: Don't run your app as root inside the container. It's a basic security principle that a lot of people ignore until something goes wrong.