Skip to content
beginner Phase 7 · Docker in Development

Development Environments

Create reproducible dev environments with Docker and Docker Compose.

1h
0 problems
Topic Progress 0%

Setting Up Dev Environments

Setting Up Dev Environments

The Problem

"Works on my machine" — developers have different OS, versions, dependencies, and configurations. Docker eliminates this by packaging everything into containers.

Basic Dev Dockerfile

# Dockerfile.dev
FROM node:20

# Install global tools
RUN npm install -g nodemon

WORKDIR /app

# Copy package files first (cache layer)
COPY package*.json ./
RUN npm install

# Copy source code
COPY . .

EXPOSE 3000

# Use nodemon for auto-restart
CMD ["nodemon", "server.js"]

docker-compose.dev.yml

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app                    # Bind mount source code
      - /app/node_modules         # Anonymous volume (don't mount host node_modules)
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DEBUG=app:*
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: devpass
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  pgdata:
  redis_data:

Start Development

# Start dev environment
docker compose -f docker-compose.dev.yml up -d

# View logs
docker compose -f docker-compose.dev.yml logs -f api

# Execute into container
docker compose -f docker-compose.dev.yml exec api bash

# Run tests
docker compose -f docker-compose.dev.yml exec api npm test

# Stop environment
docker compose -f docker-compose.dev.yml down

# Stop and reset database
docker compose -f docker-compose.dev.yml down -v

VS Code Dev Containers

VS Code Dev Containers

What are Dev Containers?

Dev Containers use Docker to create fully configured development environments with VS Code. The IDE runs on the host, but the terminal, extensions, and toolchain run inside the container.

.devcontainer/devcontainer.json

{
  "name": "My Project",
  "dockerComposeFile": "docker-compose.dev.yml",
  "service": "api",
  "workspaceFolder": "/app",
  
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-vscode.vscode-typescript-next",
        "humao.rest-client"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      }
    }
  },
  
  "postCreateCommand": "npm install",
  "postStartCommand": "npm run dev",
  "forwardPorts": [3000, 5432, 6379],
  "remoteUser": "node"
}

.devcontainer/docker-compose.dev.yml

services:
  api:
    build:
      context: ..
      dockerfile: Dockerfile.dev
    volumes:
      - ..:/app:cached
      - /app/node_modules
    command: sleep infinity  # Keep container running for VS Code

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: devpass

  redis:
    image: redis:7-alpine

Setup Steps

  1. Install VS Code Extension: "Dev Containers"
  2. Open project in VS Code
  3. Press Ctrl+Shift+P → "Dev Containers: Reopen in Container"
  4. VS Code connects to the container and installs extensions
  5. Terminal runs inside the container with all tools

Benefits

  • Every team member gets the same environment
  • New developers are productive in minutes
  • No local toolchain setup required
  • Consistent CI/CD (same container used for dev and CI)

Dev Container Features

{
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "20" },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  }
}

Multi-Language Dev Environments

Multi-Language Dev Environments

Node.js + Python

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    volumes:
      - ./frontend:/app
      - /app/node_modules
    ports:
      - "3000:3000"

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile.dev
    volumes:
      - ./backend:/app
      - /app/.venv
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  pgdata:

Python Backend Dockerfile.dev

FROM python:3.12-slim
WORKDIR /app
RUN pip install poetry
COPY pyproject.toml poetry.lock ./
RUN poetry install --no-root
COPY . .
CMD ["poetry", "run", "uvicorn", "main:app", "--reload", "--host", "0.0.0.0"]

Go Backend Dockerfile.dev

FROM golang:1.21
WORKDIR /go/src/app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
CMD ["go", "run", "main.go"]

Java Spring Boot Dockerfile.dev

FROM eclipse-temurin:21-jdk-jammy
WORKDIR /app
COPY gradlew build.gradle settings.gradle ./
COPY gradle gradle
RUN ./gradlew dependencies --no-daemon
COPY src src
CMD ["./gradlew", "bootRun", "--continuous", "--no-daemon"]

Development Best Practices

services:
  api:
    build:
      target: development  # Multi-stage: use dev stage
    volumes:
      # Named volume for deps (avoids permission issues)
      - node_modules:/app/node_modules
      # Bind mount source (live reload)
      - ./src:/app/src
      # Bind mount config (read-only)
      - ./config:/app/config:ro
    environment:
      - NODE_ENV=development
      - DEBUG=app:*
    # Don't use restart: always in dev (annoying for debugging)

volumes:
  node_modules: