Skip to content
Back to Deployment

Container Deployment with Docker

Build, optimize, and deploy multi-stage Docker images for web applications with security best practices.

ContainersIntermediate

Why

Docker containers package your app with all dependencies, ensuring identical behavior across dev, staging, and production. Multi-stage builds reduce image size by 80%+ and isolate build tools from runtime.

Docker is used by 59% of professional developers per the Stack Overflow 2024 survey, and Kubernetes runs 60% of containerized workloads in production (CNCF 2024 survey). Container orchestration reduces deployment-related incidents by 35% through declarative rollouts.

When

Use Docker when deploying to Kubernetes, ECS, or any container orchestration platform. Also useful for local dev parity and CI reproducibility.

How

1. Write Dockerfile — multi-stage: builder (deps + build) + runner (minimal runtime) 2. Choose base images — distroless or alpine for production, not debian 3. Layer ordering — copy package.json first (cache deps), then source code 4. Build — docker build -t myapp:latest . (use BuildKit for parallel stages) 5. Optimize — docker-slim or .dockerignore to exclude node_modules, .git 6. Scan — trivy or grype for vulnerabilities before pushing 7. Run — docker run -p 3000:3000 --env-file .env myapp:latest 8. Compose — docker-compose.yml for multi-container apps (app + db + cache)

Example

# Dockerfile — multi-stage Node.js app # Stage 1: Builder FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --ignore-scripts COPY . . RUN npm run build # Stage 2: Runner (minimal, no build tools) FROM node:20-alpine AS runner WORKDIR /app RUN addgroup -S app && adduser -S app -G app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ USER app EXPOSE 3000 HEALTHCHECK --interval=30s CMD wget --spider http://localhost:3000/health || exit 1 CMD ["node", "dist/server.js"] # .dockerignore node_modules .git *.md .env* dist # docker-compose.yml services: app: build: . ports: ["3000:3000"] environment: DATABASE_URL: postgres://user:pass@db:5432/app depends_on: [db] db: image: postgres:16-alpine volumes: ["pgdata:/var/lib/postgresql/data"] volumes: pgdata:

Common Problems

  • ⚠Image is 1.5GB — using node:20 (full Debian) instead of node:20-alpine or multi-stage
  • ⚠Build cache invalidated every time — COPY . . before COPY package.json breaks layer caching
  • ⚠Running as root — container escape gives attacker root on host, always use USER directive
  • ⚠Secrets baked into image — ENV for API keys means anyone with the image can extract them

Best Practices

  • ✓Use multi-stage builds — final image should not contain npm, tsc, or source files
  • ✓Pin base image digests — FROM node:20-alpine@sha256:abc... not just :20-alpine
  • ✓Use BuildKit — DOCKER_BUILDKIT=1 for parallel stage builds and cache mounts
  • ✓Scan every image in CI — trivy image myapp:latest, fail on HIGH/CRITICAL CVEs

FAQ

Why is my Docker image 1.2 GB?

You're using a full Node base image (node:18) instead of node:18-alpine. Switch to alpine and add a .dockerignore excluding node_modules, .git, and tests — the image drops to ~120 MB.

How do I cache node_modules in Docker?

Copy package.json and package-lock.json first, run npm ci, then copy the rest. Docker layer caching skips the install step when the lockfile is unchanged, cutting rebuilds from 60s to 5s.

Should I run as root in my container?

No — create a non-root user (USER node) and set file ownership. Root in a container is root on the host if it escapes. All node:18-alpine images include a node user.

Why does my container crash with EADDRINUSE?

The app listens on a port already in use inside the container. Use PORT from env (process.env.PORT) and EXPOSE that port. Don't hardcode 3000 — orchestrators assign dynamic ports.

How do I debug a container that exits immediately?

Run docker run --entrypoint sh <image> to get a shell. Check logs with docker logs <id>. Common causes: missing env vars, wrong CMD, or OOM kill.