Container Deployment with Docker
Build, optimize, and deploy multi-stage Docker images for web applications with security best practices.
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
Example
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.