5 min read By Michael Rodriguez

Docker for Developers: Complete Guide

Master Docker containerization for modern development. Learn best practices for creating, managing, and deploying containerized applications.

Docker DevOps Containers Development
Docker for Developers: Complete Guide

What is Docker?

Docker is a platform that enables developers to package applications into containers—standardized executable components that combine application source code with all the dependencies needed to run that code in any environment.

Why Use Docker?

Consistency Across Environments

The age-old problem: “It works on my machine!”

# Define your environment once
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]

Benefits

  • Portability - Run anywhere Docker is installed
  • Isolation - Each container is isolated from others
  • Efficiency - Lightweight compared to VMs
  • Version Control - Dockerfile is version controlled
  • Scalability - Easy to scale horizontally

Core Concepts

Images

A Docker image is a read-only template with instructions for creating a container:

# Pull an image from Docker Hub
docker pull node:18-alpine

# List images
docker images

# Remove an image
docker rmi node:18-alpine

Containers

Containers are runnable instances of images:

# Run a container
docker run -d -p 3000:3000 --name myapp node:18-alpine

# List running containers
docker ps

# Stop a container
docker stop myapp

# Remove a container
docker rm myapp

Creating Your First Dockerfile

Node.js Application

# Use official Node.js image
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy application code
COPY . .

# Expose port
EXPOSE 3000

# Set environment to production
ENV NODE_ENV=production

# Start application
CMD ["node", "server.js"]

Multi-stage Builds

Optimize image size with multi-stage builds:

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]

Docker Compose

Manage multi-container applications:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db
    volumes:
      - ./src:/app/src
    
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  postgres_data:

Run with:

# Start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Stop all services
docker-compose down

Best Practices

1. Use Official Images

# Good
FROM node:18-alpine

# Avoid
FROM random-user/node-custom

2. Minimize Layers

# Bad - Creates multiple layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git

# Good - Single layer
RUN apt-get update && \
    apt-get install -y curl git && \
    rm -rf /var/lib/apt/lists/*

3. Use .dockerignore

node_modules
npm-debug.log
.git
.env
*.md
.vscode
dist
coverage

4. Don’t Run as Root

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# Switch to user
USER nodejs

5. Health Checks

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js || exit 1

Common Commands

# Build image
docker build -t myapp:latest .

# Run container with volume mount
docker run -v $(pwd):/app myapp

# Execute command in running container
docker exec -it myapp sh

# View container logs
docker logs -f myapp

# Inspect container
docker inspect myapp

# Clean up
docker system prune -a

Development Workflow

Hot Reload Setup

services:
  app:
    build: .
    volumes:
      - ./src:/app/src
      - /app/node_modules
    environment:
      - NODE_ENV=development
    command: npm run dev

Debug Mode

services:
  app:
    build: .
    ports:
      - "9229:9229"
    command: node --inspect=0.0.0.0:9229 server.js

Networking

Bridge Network

# Create network
docker network create mynetwork

# Run containers on network
docker run --network mynetwork --name db postgres
docker run --network mynetwork --name app myapp

Docker Compose Networks

services:
  frontend:
    networks:
      - frontend
  
  backend:
    networks:
      - frontend
      - backend
  
  database:
    networks:
      - backend

networks:
  frontend:
  backend:

Volumes

Named Volumes

# Create volume
docker volume create mydata

# Use volume
docker run -v mydata:/app/data myapp

# List volumes
docker volume ls

# Remove volume
docker volume rm mydata

Security

Scan Images

# Docker Scout
docker scout quickview

# Trivy
trivy image myapp:latest

Environment Variables

# Use .env file
docker run --env-file .env myapp

# Docker secrets (Swarm mode)
echo "my_secret" | docker secret create db_password -

Deployment

Build for Production

# Build optimized image
docker build --target production -t myapp:1.0.0 .

# Push to registry
docker tag myapp:1.0.0 registry.com/myapp:1.0.0
docker push registry.com/myapp:1.0.0

Troubleshooting

# Container won't start
docker logs myapp
docker inspect myapp

# Check resource usage
docker stats

# Enter container
docker exec -it myapp sh

# Clean everything
docker system prune -a --volumes

Conclusion

Docker revolutionizes how we develop, ship, and run applications. By containerizing your applications, you ensure consistency across environments, simplify deployment, and improve scalability. Start small, experiment with Docker Compose, and gradually adopt best practices as you become more comfortable with containers.

M

Michael Rodriguez

Published on March 12, 2024

Share: