Modern Development Environments with Docker
One of the biggest challenges in modern software development is solving the "it works on my machine" problem. Docker eliminates this issue by enabling us to create consistent and portable development environments.
Benefits of Docker for Development
Docker offers numerous advantages in the development process:
- Consistency - The entire team works in the same environment
- Isolation - Projects don't interfere with each other
- Quick Setup - New developers can start in minutes
- Production-like - Minimize differences between development and production
- Clean System - Host system remains clean
- Easy Version Management - Different technology versions are easily managed
Core Docker Concepts
Dockerfile - Image Definition
A Dockerfile defines how your application will run:
# Example Dockerfile class="keyword">class="keyword">for Node.js application
class="keyword">FROM node:20-alpine class="keyword">AS base
# Create working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy application code
COPY . .
# Port information
EXPOSE 3000
# Startup command
CMD [class="keyword">class="string">"npm", class="keyword">class="string">"run", class="keyword">class="string">"dev"]
Multi-Stage Builds
Create optimized images for production:
# Build stage
class="keyword">FROM node:20-alpine class="keyword">AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
class="keyword">FROM node:20-alpine class="keyword">AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --class="keyword">from=builder /app/dist ./dist
EXPOSE 3000
CMD [class="keyword">class="string">"node", class="keyword">class="string">"dist/server.js"]
Multi-Service Management with Docker Compose
Docker Compose is ideal for managing multiple services.
Next.js + PostgreSQL + Redis Example
version: class="keyword">class="string">"3.8"
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- class="keyword">class="string">"3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- DATABASE_URL=postgresql:class="keyword">class="comment">//postgres:password@db:5432/myapp
- REDIS_URL=redis:class="keyword">class="comment">//redis:6379
depends_on:
- db
- redis
command: npm run dev
db:
image: postgres:16-alpine
ports:
- class="keyword">class="string">"5432:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- postgres_data:/class="keyword">class="keyword">var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- class="keyword">class="string">"6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Dockerfile for Development
# Dockerfile.dev
class="keyword">FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies(including dev dependencies)
RUN npm install
# Install nodemon class="keyword">class="keyword">for hot reload
RUN npm install -g nodemon
EXPOSE 3000
CMD [class="keyword">class="string">"npm", class="keyword">class="string">"run", class="keyword">class="string">"dev"]
Best Practices
1. Use .dockerignore
Exclude unnecessary files from the image:
node_modules
npm-debug.log
.env
.env.local
.git
.gitignore
README.md
.next
.DS_Store
dist
build
coverage
2. Leverage Layer Caching
Copy files that change less frequently first:
# First dependencies(changes less often)
COPY package*.json ./
RUN npm ci
# Then code(changes frequently)
COPY . .
3. Add Health Checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD node healthcheck.js || exit 1
4. Use Non-Root User
# Use non-root user class="keyword">class="keyword">for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
Common Use Cases
Working with Database
# Start services
docker-compose up -d
# Run database migrations
docker-compose exec app npx prisma migrate dev
# Connect to database
docker-compose exec db psql -U postgres -d myapp
Viewing Logs
# View all logs
docker-compose logs
# Only app logs
docker-compose logs app -f
# Last 100 lines
docker-compose logs --tail=100
Cleanup and Restart
# Stop and clean services
docker-compose down
# Also remove volumes
docker-compose down -v
# Rebuild and start
docker-compose up --build
Docker Integration with VS Code
Develop directly inside containers with VS Code Dev Containers extension:
class="keyword">class="comment">// .devcontainer/devcontainer.json
{
class="keyword">class="string">"name": class="keyword">class="string">"Next.js App",
class="keyword">class="string">"dockerComposeFile": class="keyword">class="string">"../docker-compose.yml",
class="keyword">class="string">"service": class="keyword">class="string">"app",
class="keyword">class="string">"workspaceFolder": class="keyword">class="string">"/app",
class="keyword">class="string">"customizations": {
class="keyword">class="string">"vscode": {
class="keyword">class="string">"extensions": [
class="keyword">class="string">"dbaeumer.vscode-eslint",
class="keyword">class="string">"esbenp.prettier-vscode",
class="keyword">class="string">"bradlc.vscode-tailwindcss"
]
}
}
}
Performance Tips
1. Use Build Cache
# Fast build with BuildKit
DOCKER_BUILDKIT=1 docker build -t myapp .
2. Prefer Named Volumes
volumes:
# Instead of anonymous volume
- node_modules:/app/node_modules
3. Optimize Bind Mounts
volumes:
- .:/app:delegated # Performance class="keyword">class="keyword">for macOS
- /app/node_modules # Isolate node_modules class="keyword">from host
Conclusion
Docker is essential for modern development environments. When used correctly, it:
- Increases team productivity
- Reduces onboarding time
- Minimizes production issues
- Provides a clean and consistent development experience
By integrating Docker into your projects, you can find a permanent solution to the "it works on my machine" problem and significantly improve your development process.
Useful Commands
# List running containers
docker ps
# List all containers
docker ps -a
# List images
docker images
# Connect to container with shell
docker-compose exec app sh
# Restart container
docker-compose restart app
# View container resources
docker stats
# Clean up everything unused
docker system prune -a
Happy coding! 🚀