Featured image of post Setting Up a Local Dev Environment with DockerFeatured image of post Setting Up a Local Dev Environment with Docker

Setting Up a Local Dev Environment with Docker

Set up a reliable local development environment with Docker. Reduce environment discrepancies and streamline your workflow.

“It works on my machine” is a phrase every developer knows all too well. Docker solves this by packaging your application and its dependencies into portable containers. This article walks through setting up a local development environment with Docker.

The Three Core Concepts

  • Dockerfile: A blueprint for your container — which base image to use and what to install.
  • docker-compose.yml: A configuration file for managing multiple containers (web server, database, cache, etc.) together.
  • Volume mount: Lets the container reference files on your host machine. Essential for hot reload.

Multi-Service 设置 with docker-compose

A typical frontend + backend + database setup can be defined in a single docker-compose.yml:

version: "3.9"
services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    command: npm run dev

  backend:
    build: ./backend
    ports:
      - "8000:8000"
    volumes:
      - ./backend:/app
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/app

  db:
    image: postgres:16
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Key Points

  • Volume mounts like ./frontend:/app sync host source code into the container. Changes on the host reflect instantly inside the container, keeping hot reload working.
  • Anonymous volume for node_modules: The /app/node_modules entry prevents syncing with the host, avoiding native module compatibility issues between Windows and Linux.
  • Environment variables: Pass configuration like database connection strings through the environment block.

Dev-Friendly Dockerfile

It’s common practice to maintain a separate Dockerfile for development:

FROM node:20-alpine

WORKDIR /app

# Install dependencies first (leverage layer caching)
COPY package*.json ./
RUN npm install

# Source code is volume-mounted, so no COPY needed
CMD ["npm", "run", "dev"]

The trick is not to COPY source code into the image. Instead, mount the host directory via volumes in docker-compose.yml. This way, code changes take effect immediately without rebuilding the container.

Team Consistency

The real value of Docker in local development is that everyone runs the same environment. A few team rules go a long way:

  • Commit docker-compose.yml and Dockerfile to Git.
  • Add .env (secrets) to .gitignore and include .env.example in the repo.
  • Provide wrapper commands in a Makefile or npm scripts so team members unfamiliar with Docker can get started easily.
up:
	docker compose up -d
down:
	docker compose down
build:
	docker compose build
logs:
	docker compose logs -f

Common Pitfalls

  • Performance: File mounts can be slow on Windows, especially without WSL2. Run Docker Desktop on WSL2 or consider sync tools like Mutagen.
  • Port conflicts: If multiple projects use the same port (e.g., 3000), change the host-side port in the ports config (e.g., "3001:3000").
  • Volume cleanup: Stale volumes accumulate over time. Run docker compose down -v to clean them up.

摘要

Docker’s biggest win for local development is reproducibility and team consistency. Using docker-compose to manage multiple services together while maintaining hot reload through volume mounts gives you the best of both worlds. Try it on a small project first, then expand to your team’s full workflow.