Docker Development Environment Setup: Complete Guide 2026
Step-by-step guide to creating a production-ready Docker development environment with hot reload, debugging, and Docker Compose.
Setting up Docker for development can transform your workflow, but getting it right takes some know-how. After years of helping teams containerize their applications, I've learned what actually works in real-world development.
Related reading: Check out our guides on TypeScript migration and remote work setup for more development insights.
Why Docker for Development?#
The Real Benefits#
Consistency across environments:
- "Works on my machine" becomes "works everywhere"
- Same environment for all team members
- Identical dev, staging, and production setups
Faster onboarding:
- New developers productive in minutes, not days
- No complex installation instructions
- One command to start everything
Isolation and cleanup:
- Multiple projects without conflicts
- Easy to tear down and rebuild
- No leftover dependencies cluttering your system
Production parity:
- Catch environment-specific bugs early
- Test with production-like services
- Smooth deployment process
When Docker Makes Sense#
Perfect for:
- Full-stack applications with multiple services
- Projects with complex dependencies
- Teams with different operating systems
- Microservices architectures
Skip Docker if:
- Building simple static sites
- Working solo on small scripts
- Learning a new language (native install is simpler)
- Your team has zero Docker experience and tight deadlines
Prerequisites#
Before diving in, make sure you have:
Required:
- Docker Desktop installed (download here)
- Basic command line knowledge
- A code editor (VS Code recommended)
Helpful:
- Understanding of your application's architecture
- Familiarity with environment variables
- Basic networking concepts
System requirements:
- 8GB RAM minimum (16GB recommended)
- 20GB free disk space
- Windows 10/11 Pro, macOS 10.15+, or Linux
How to Set Up Docker for Development#
Step 1: Install Docker Desktop#
macOS:
# Using Homebrew
brew install --cask docker
# Or download from docker.com
# Start Docker Desktop from Applications
Windows:
# Download Docker Desktop from docker.com
# Enable WSL 2 backend during installation
# Restart your computer
Linux (Ubuntu/Debian):
# Install Docker Engine
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Add your user to docker group
sudo usermod -aG docker $USER
# Install Docker Compose
sudo apt-get install docker-compose-plugin
Verify installation:
docker --version
docker compose version
Step 2: Create Your First Dockerfile#
Let's start with a Node.js application:
# Dockerfile
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Start application
CMD ["npm", "run", "dev"]
Key concepts:
FROM: Base image to build fromWORKDIR: Sets the working directory inside containerCOPY: Copies files from host to containerRUN: Executes commands during buildCMD: Command to run when container starts
Step 3: Add Docker Compose#
Create docker-compose.yml for multi-service setup:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:password@db:5432/myapp
depends_on:
- db
- redis
command: npm run dev
db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Start your environment:
docker compose up
Step 4: Enable Hot Reload#
For Node.js with nodemon:
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
# Install nodemon globally
RUN npm install -g nodemon
COPY . .
EXPOSE 3000
CMD ["nodemon", "src/index.js"]
Update docker-compose.yml:
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true
For React/Vite:
services:
frontend:
build: ./frontend
ports:
- "5173:5173"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- VITE_API_URL=http://localhost:3000
command: npm run dev -- --host
Step 5: Configure Environment Variables#
Create .env file:
# .env
NODE_ENV=development
DATABASE_URL=postgresql://user:password@db:5432/myapp
REDIS_URL=redis://redis:6379
API_KEY=your-api-key-here
Update docker-compose.yml:
services:
app:
env_file:
- .env
environment:
- NODE_ENV=${NODE_ENV}
- DATABASE_URL=${DATABASE_URL}
Important: Add .env to .gitignore!
Step 6: Set Up Debugging#
VS Code launch configuration (.vscode/launch.json):
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Docker: Attach to Node",
"port": 9229,
"address": "localhost",
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app",
"protocol": "inspector"
}
]
}
Update Dockerfile for debugging:
CMD ["node", "--inspect=0.0.0.0:9229", "src/index.js"]
Update docker-compose.yml:
services:
app:
ports:
- "3000:3000"
- "9229:9229"
Step 7: Optimize Build Performance#
Use .dockerignore:
node_modules
npm-debug.log
.git
.env
.DS_Store
dist
build
coverage
Multi-stage builds for production:
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]
Layer caching:
# Copy package files first (changes less frequently)
COPY package*.json ./
RUN npm ci
# Copy source code last (changes frequently)
COPY . .
Common Development Patterns#
Full-Stack Application#
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- REACT_APP_API_URL=http://localhost:4000
backend:
build: ./backend
ports:
- "4000:4000"
volumes:
- ./backend:/app
- /app/node_modules
environment:
- DATABASE_URL=postgresql://user:password@db:5432/myapp
depends_on:
- db
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Microservices Setup#
version: '3.8'
services:
api-gateway:
build: ./services/gateway
ports:
- "8080:8080"
depends_on:
- auth-service
- user-service
auth-service:
build: ./services/auth
environment:
- JWT_SECRET=${JWT_SECRET}
- DATABASE_URL=${AUTH_DB_URL}
user-service:
build: ./services/users
environment:
- DATABASE_URL=${USER_DB_URL}
message-queue:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
Useful Docker Commands#
Daily Development#
# Start services
docker compose up
# Start in background
docker compose up -d
# Stop services
docker compose down
# Rebuild and start
docker compose up --build
# View logs
docker compose logs -f app
# Execute command in running container
docker compose exec app npm install package-name
# Open shell in container
docker compose exec app sh
Debugging and Maintenance#
# List running containers
docker ps
# List all containers
docker ps -a
# View container logs
docker logs container-name
# Inspect container
docker inspect container-name
# Remove stopped containers
docker container prune
# Remove unused images
docker image prune
# Remove everything (careful!)
docker system prune -a
Database Operations#
# Access PostgreSQL
docker compose exec db psql -U user -d myapp
# Run migrations
docker compose exec app npm run migrate
# Seed database
docker compose exec app npm run seed
# Backup database
docker compose exec db pg_dump -U user myapp > backup.sql
# Restore database
docker compose exec -T db psql -U user myapp < backup.sql
Troubleshooting Common Issues#
Port Already in Use#
Error: Bind for 0.0.0.0:3000 failed: port is already allocated
Solution:
# Find process using port
lsof -i :3000 # macOS/Linux
netstat -ano | findstr :3000 # Windows
# Kill the process or change port in docker-compose.yml
ports:
- "3001:3000"
Volume Permission Issues#
Error: EACCES: permission denied
Solution:
# Add user in Dockerfile
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
USER appuser
Slow Performance on macOS/Windows#
Problem: File sync is slow with volumes
Solution:
# Use delegated or cached mode
volumes:
- .:/app:delegated
- /app/node_modules
Or use Docker volumes instead:
volumes:
- app_data:/app
volumes:
app_data:
Container Keeps Restarting#
Check logs:
docker compose logs app
Common causes:
- Application crashes on startup
- Missing environment variables
- Database not ready (add healthcheck)
Solution with healthcheck:
services:
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5
app:
depends_on:
db:
condition: service_healthy
Best Practices#
Security#
Don't run as root:
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
USER appuser
Use specific image versions:
# Bad
FROM node:latest
# Good
FROM node:20.11-alpine
Scan for vulnerabilities:
docker scan your-image:tag
Performance#
Minimize layers:
# Bad
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
# Good
RUN apt-get update && \
apt-get install -y package1 package2 && \
rm -rf /var/lib/apt/lists/*
Use alpine images:
FROM node:20-alpine # ~40MB
# vs
FROM node:20 # ~900MB
Maintainability#
Document your setup:
# README.md
## Development Setup
1. Install Docker Desktop
2. Copy `.env.example` to `.env`
3. Run `docker compose up`
4. Access app at http://localhost:3000
## Common Commands
- `docker compose up` - Start services
- `docker compose down` - Stop services
- `docker compose logs -f` - View logs
Use make for common tasks:
# Makefile
.PHONY: up down logs shell test
up:
docker compose up -d
down:
docker compose down
logs:
docker compose logs -f
shell:
docker compose exec app sh
test:
docker compose exec app npm test
Frequently Asked Questions#
Q: Should I use Docker for development or just production? A: Use Docker for both. Development with Docker ensures your local environment matches production, catching environment-specific bugs early. The initial setup time pays off quickly with consistent environments across your team.
Q: How do I handle database migrations in Docker?
A: Run migrations as part of your startup script or as a separate service. Add a migration command to your docker-compose.yml: docker compose exec app npm run migrate. For production, run migrations before deploying new code.
Q: Why is my Docker container so slow on macOS?
A: File system performance with volumes can be slow on macOS. Use :delegated or :cached flags on volumes, or use named volumes instead of bind mounts for node_modules and other frequently accessed directories.
Q: How do I debug inside a Docker container? A: Expose the debug port (9229 for Node.js) in docker-compose.yml and use your IDE's remote debugging feature. VS Code has excellent Docker debugging support with the Docker extension.
Q: Should I commit my docker-compose.yml to git? A: Yes, commit docker-compose.yml but not .env files. Create a .env.example with dummy values for documentation. Each developer creates their own .env from the example.
Q: How do I update dependencies in a Docker container?
A: Run docker compose exec app npm install package-name to install in the running container, or rebuild with docker compose up --build after updating package.json locally.
Q: What's the difference between CMD and ENTRYPOINT? A: CMD provides default arguments that can be overridden. ENTRYPOINT defines the main command that always runs. Use CMD for development (easy to override) and ENTRYPOINT for production (consistent behavior).
Q: How do I share my Docker setup with the team? A: Commit Dockerfile, docker-compose.yml, and .dockerignore to git. Create a .env.example file. Document setup steps in README.md. Consider using a Makefile for common commands.
Conclusion#
Docker transforms development from "works on my machine" to "works everywhere." The key is starting simple and adding complexity only when needed.
Your action plan:
- Install Docker Desktop
- Create a basic Dockerfile
- Add docker-compose.yml for services
- Enable hot reload for your framework
- Document the setup for your team
The initial setup takes time, but you'll save hours every week with consistent environments and faster onboarding.
Further Reading:
- Docker Official Documentation - Comprehensive Docker guide
- Docker Compose Documentation - Multi-container applications
- Learn more about our editorial team and how we research our articles.
Continue Reading
Kubernetes & Docker: Container Orchestration Mastery 2026
Learn Kubernetes and Docker from basics to production deployment. Includes real-world examples, scaling strategies, and DevOps best practices for 2026.
Progressive Web Apps (PWA): The Complete 2026 Guide
Learn how to build Progressive Web Apps that work offline, load instantly, and feel like native apps. Includes service workers, caching strategies, and push notifications.
Serverless Edge Computing: The 2026 Revolution in Web Performance
Edge computing is revolutionizing web performance. Learn how to leverage Cloudflare Workers, Vercel Edge Functions, and Deno Deploy for lightning-fast applications.
Browse by Topic
Find stories that matter to you.