“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:/appsync 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_modulesentry prevents syncing with the host, avoiding native module compatibility issues between Windows and Linux. - Environment variables: Pass configuration like database connection strings through the
environmentblock.
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.ymlandDockerfileto Git. - Add
.env(secrets) to.gitignoreand include.env.examplein the repo. - Provide wrapper commands in a
Makefileornpm scriptsso 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
portsconfig (e.g.,"3001:3000"). - Volume cleanup: Stale volumes accumulate over time. Run
docker compose down -vto 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.

