Loading content…
Loading content…
Master multi-stage Docker builds, production-grade docker-compose architectures, database backup schedules, network isolation, and non-root security standards
# Stage 1: Install dependencies
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Stage 2: Build the application
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# Stage 3: Production Runner
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
# Create a non-root group and user for security
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy pre-compiled standalone app server and assets
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Run as non-root user
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
docker-compose.yml configuration:version: "3.8"
services:
# Node/Express Backend API
api:
build:
context: ./backend
dockerfile: Dockerfile
restart: always
environment:
- DATABASE_URL=postgres://app_user:db_secure_pass@db:5432/app_prod
- NODE_ENV=production
depends_on:
db:
condition: service_healthy # Wait for Postgres to be fully ready
networks:
- web-network
- db-network
# PostgreSQL Database
db:
image: postgres:16-alpine
restart: always
shm_size: 256mb # Adjust shared memory for Postgres query speeds
environment:
- POSTGRES_USER=app_user
- POSTGRES_PASSWORD=db_secure_pass
- POSTGRES_DB=app_prod
volumes:
- pg-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app_user -d app_prod"]
interval: 10s
timeout: 5s
retries: 5
networks:
- db-network
# Network isolation rules
networks:
web-network: # Public facing network
db-network: # Internal backend-to-database network
# Persistent volume config
volumes:
pg-data:
driver: local
db service in our Compose file only belongs to the db-network, whereas the api service belongs to both web-network and db-network.db-network, only the api container can communicate with the db port.depends_on: [db]. This tells Docker to start the api container once the db container starts.api container attempts to connect immediately, it will throw a connection exception and crash.pg_isready) alongside depends_on: { db: { condition: service_healthy } } ensures the API only starts once PostgreSQL is fully prepared to execute database queries.volumes:
- pg-data:/var/lib/postgresql/data
pg_dump inside the running container to output a SQL backup without downtime.#!/bin/bash
# backup-db.sh
BACKUP_DIR="/backups/postgres"
DATE=$(date +%Y-%m-%d_%H%M%S)
CONTAINER_NAME="app-db-1"
DB_USER="app_user"
DB_NAME="app_prod"
# Create backup directory
mkdir -p $BACKUP_DIR
# Run pg_dump inside running database container
docker exec -t $CONTAINER_NAME pg_dump -U $DB_USER $DB_NAME > $BACKUP_DIR/backup_$DATE.sql
# Retain only the last 7 days of backups
find $BACKUP_DIR -type f -name "*.sql" -mtime +7 -delete
root. If an attacker breaks out of your app container, they gain root access to the host machine. Always define a custom user:
USER nextjs
# docker-compose.yml
services:
api:
read_only: true
-alpine or -slim Node images to reduce security vulnerabilities.Production Docker Checklist
Marking it complete updates your roadmap progress percentage.