Docker for Developers: Complete Development Workflow Guide
Docker has revolutionized how we develop and deploy applications. Let’s explore how to leverage Docker for a seamless development workflow that works consistently across all environments.
Docker Fundamentals for Development
Essential Dockerfile Patterns
# Multi-stage build for Node.js
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Optimized Python Dockerfile
FROM python:3.11-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Docker Compose for Multi-Service Development
Full-Stack Application Setup
# docker-compose.yml
version: '3.8'
services:
# Frontend React App
frontend:
build:
context: ./frontend
target: development
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- REACT_APP_API_URL=http://localhost:8000
depends_on:
- backend
# Backend API
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
volumes:
- ./backend:/app
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
# PostgreSQL Database
db:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
# Redis Cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
# Nginx Reverse Proxy
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- frontend
- backend
volumes:
postgres_data:
redis_data:
Development Override File
# docker-compose.override.yml
version: '3.8'
services:
backend:
build:
target: development
volumes:
- ./backend:/app
- /app/__pycache__
environment:
- DEBUG=1
- LOG_LEVEL=debug
command: ["python", "manage.py", "runserver", "0.0.0.0:8000"]
frontend:
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- FAST_REFRESH=true
- CHOKIDAR_USEPOLLING=true
Development Environment Optimization
Hot Reloading Configuration
# Dockerfile.dev for Node.js with hot reload
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm install
# Install nodemon globally for hot reloading
RUN npm install -g nodemon
# Copy source code
COPY . .
EXPOSE 3000
# Use nodemon for development
CMD ["nodemon", "--legacy-watch", "server.js"]
Volume Mounting Strategies
services:
app:
build: .
volumes:
# Bind mount source code for hot reloading
- ./src:/app/src
# Anonymous volume for node_modules to avoid conflicts
- /app/node_modules
# Named volume for persistent data
- app_data:/app/data
# Bind mount config files
- ./config:/app/config:ro # Read-only
Database Development Patterns
Database Initialization
-- db/init.sql
CREATE DATABASE IF NOT EXISTS myapp_dev;
CREATE DATABASE IF NOT EXISTS myapp_test;
CREATE USER IF NOT EXISTS 'dev_user'@'%' IDENTIFIED BY 'dev_password';
GRANT ALL PRIVILEGES ON myapp_dev.* TO 'dev_user'@'%';
GRANT ALL PRIVILEGES ON myapp_test.* TO 'dev_user'@'%';
-- Create initial tables
USE myapp_dev;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Database Seeding
services:
db-seed:
build:
context: ./database
dockerfile: Dockerfile.seed
depends_on:
- db
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
volumes:
- ./database/seeds:/seeds
command: ["python", "seed.py"]
Testing with Docker
Test Environment Setup
# docker-compose.test.yml
version: '3.8'
services:
test-db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=test_db
- POSTGRES_USER=test_user
- POSTGRES_PASSWORD=test_password
tmpfs:
- /var/lib/postgresql/data # In-memory for faster tests
app-test:
build:
context: .
target: test
environment:
- NODE_ENV=test
- DATABASE_URL=postgresql://test_user:test_password@test-db:5432/test_db
depends_on:
- test-db
command: ["npm", "test"]
Running Tests
# Run tests in isolated environment
docker-compose -f docker-compose.test.yml up --build --abort-on-container-exit
# Run specific test suite
docker-compose -f docker-compose.test.yml run --rm app-test npm run test:unit
# Interactive testing
docker-compose -f docker-compose.test.yml run --rm app-test bash
Production-Ready Configurations
Health Checks
# Add health check to Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Health checks in docker-compose
services:
app:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Resource Limits
services:
app:
build: .
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
restart: unless-stopped
Useful Docker Commands
Development Workflow Commands
# Build and start all services
docker-compose up --build
# Start services in background
docker-compose up -d
# View logs
docker-compose logs -f app
# Execute commands in running container
docker-compose exec app bash
docker-compose exec db psql -U postgres
# Clean up
docker-compose down -v # Remove volumes
docker system prune -a # Clean up everything
# Database operations
docker-compose exec db pg_dump -U postgres myapp > backup.sql
docker-compose exec -T db psql -U postgres myapp < backup.sql
Debugging Commands
# Inspect container
docker inspect container_name
# Check resource usage
docker stats
# View container processes
docker-compose top
# Debug networking
docker network ls
docker network inspect network_name
Docker Development Best Practices
.dockerignore Optimization
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output
.vscode
.idea
*.log
dist
build
Environment-Specific Configurations
# .env.development
NODE_ENV=development
DEBUG=1
DATABASE_URL=postgresql://postgres:password@localhost:5432/myapp_dev
REDIS_URL=redis://localhost:6379
# .env.production
NODE_ENV=production
DATABASE_URL=${DATABASE_URL}
REDIS_URL=${REDIS_URL}
Docker transforms development from “works on my machine” to “works everywhere.” Master these patterns, and you’ll have consistent, reproducible development environments that scale from local development to production deployment.