Docker Development Environment Setup: Complete Guide 2026
AI & Development

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.

Jan 27, 2026
10 min read
Docker Development Environment Setup: Complete Guide 2026

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 from
  • WORKDIR: Sets the working directory inside container
  • COPY: Copies files from host to container
  • RUN: Executes commands during build
  • CMD: 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:

  1. Install Docker Desktop
  2. Create a basic Dockerfile
  3. Add docker-compose.yml for services
  4. Enable hot reload for your framework
  5. 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: