Environment Management

Docker for Developers

Containerize your applications so they run identically everywhere — from your laptop to production.

What Docker Is

Docker is a tool that packages your application together with everything it needs to run — the runtime, dependencies, configuration, and system libraries — into a single portable unit called a container.

The promise: if it runs in a container on your laptop, it runs identically on any other machine with Docker installed. No more "it works on my machine."

Images vs Containers

Two concepts are essential to understand:

Image — A read-only blueprint. Think of it as a recipe. It defines what the container will contain: the base OS, the Node.js version, your application code, installed dependencies, configuration.

Container — A running instance of an image. Think of it as a meal made from the recipe. You can run multiple containers from the same image simultaneously.

text
Image → Container (running)
       → Container (running)
       → Container (stopped)

Writing a Dockerfile

A Dockerfile is a text file that contains instructions to build an image:

dockerfile
# Start from the official Node.js 20 image
FROM node:20-alpine

# Set the working directory inside the container
WORKDIR /app

# Copy package files first (for better caching)
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy the rest of the application code
COPY . .

# Build the application
RUN npm run build

# Expose the port the app runs on
EXPOSE 3000

# The command to run when the container starts
CMD ["node", "server.js"]

Multi-Stage Builds

For Next.js and other compiled applications, use multi-stage builds to keep the final image small:

dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

# Only copy what's needed to run (not dev dependencies)
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]

The builder stage compiles the app; the runner stage only contains what's needed to run it. Result: a much smaller production image.

Essential Docker Commands

bash
# Build an image from the Dockerfile in the current directory
docker build -t myapp:latest .

# Run a container, mapping port 3000 on host to port 3000 in container
docker run -p 3000:3000 myapp:latest

# Run with environment variables
docker run -p 3000:3000 -e DATABASE_URL=... myapp:latest

# Run in the background (detached mode)
docker run -d -p 3000:3000 myapp:latest

# List running containers
docker ps

# View container logs
docker logs container-id

# Execute a command inside a running container
docker exec -it container-id sh

# Stop a container
docker stop container-id

When to Use Docker

Docker shines when:

  • Your team has inconsistent local environments (different OS, Node versions)
  • Your app has complex dependencies (databases, caches, background workers)
  • You need production-like behavior during development
  • Your deployment target is a container registry (AWS ECS, Google Cloud Run, Kubernetes)

When Docker may be overkill:

  • Simple static sites deployed to Vercel or Netlify
  • Early prototyping where speed of iteration matters more than environment parity
  • Solo projects where you control the entire stack

Key Takeaways

  • Docker packages your app and its dependencies into a portable container that runs identically everywhere
  • Images are blueprints; containers are running instances
  • Multi-stage Dockerfiles keep production images small by separating the build environment from the runtime environment
  • Use volume mounts (-v) for development to get hot-reloading without rebuilding the image
  • Docker is not always necessary — Vercel/Netlify handle deployment for most frontend projects

Example

dockerfile
# Multi-stage Dockerfile for Next.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
Try it yourself — DOCKERFILE

Docker, AWS, Vercel, Netlify, GitHub, GitHub Actions are trademarks of Docker, Inc., Amazon.com, Inc., Vercel, Inc., Netlify, Inc., Microsoft Corporation. DevForge Academy is not affiliated with, endorsed by, or sponsored by these companies. Referenced for educational purposes only. See full disclaimers