What is Docker?

Docker is an open-source platform that enables developers to automate the deployment of applications inside lightweight, portable containers. Released in 2013, Docker has revolutionized how we build, ship, and run applications by solving the age-old problem of “it works on my machine.”

A Docker container packages an application with all its dependencies, libraries, and configuration files, ensuring it runs consistently across any environment – from a developer’s laptop to production servers in the cloud.

Why Docker Matters for Web Developers

1. Consistency Across Environments

Docker eliminates the “works on my machine” problem:

– Development environment matches production exactly
– No more dependency conflicts
– Same behavior on any operating system
– Predictable deployments

2. Simplified Dependency Management

# Instead of installing Node.js, MongoDB, Redis, PostgreSQL locally
# Just run:
docker-compose up

3. Faster Onboarding

New team members can start developing immediately:

git clone project
docker-compose up
# Start coding - everything is configured

4. Isolation and Security

– Each application runs in its own container
– No conflicts between projects
– Easy to clean up (just remove container)
– Sandboxed execution environment

5. Microservices Architecture

– Each service in its own container
– Scale services independently
– Update without affecting others
– Mix technologies easily

Docker Core Concepts

Images

A Docker image is a read-only template containing application code, runtime, libraries, and dependencies.

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

# List images
docker images

# Remove image
docker rmi node:18

Containers

A container is a running instance of an image.

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

# List running containers
docker ps

# List all containers
docker ps -a

# Stop container
docker stop myapp

# Remove container
docker rm myapp

Dockerfile

A Dockerfile defines how to build an image:

# Dockerfile for Node.js application
FROM node:18

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy application code
COPY . .

# Expose port
EXPOSE 3000

# Start application
CMD ["npm", "start"]

Docker Compose

Docker Compose manages multi-container applications:

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - DATABASE_URL=postgres://db:5432/myapp
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7

volumes:
  postgres_data:

Installing Docker

Windows

Download Docker Desktop from docker.com:
– Requires Windows 10/11 Pro or Enterprise
– WSL 2 backend recommended
– Includes Docker Compose

Mac

Download Docker Desktop for Mac:
– Works on Intel and Apple Silicon
– Includes Docker Compose
– Native performance

Linux

# Ubuntu/Debian
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Add user to docker group
sudo usermod -aG docker krithin

# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Verify Installation

docker --version
docker-compose --version
docker run hello-world

Essential Docker Commands

Working with Containers

# Run container interactively
docker run -it ubuntu bash

# Run container in detached mode
docker run -d nginx

# Execute command in running container
docker exec -it container_name bash

# View container logs
docker logs container_name
docker logs -f container_name  # Follow logs

# Inspect container
docker inspect container_name

# View container resource usage
docker stats

Working with Images

# Build image from Dockerfile
docker build -t myapp:1.0 .

# Tag image
docker tag myapp:1.0 myapp:latest

# Push image to registry
docker push username/myapp:1.0

# Save image to file
docker save myapp:1.0 > myapp.tar

# Load image from file
docker load < myapp.tar

Networking

# Create network
docker network create mynetwork

# List networks
docker network ls

# Connect container to network
docker network connect mynetwork container_name

# Inspect network
docker network inspect mynetwork

Volumes

# Create volume
docker volume create mydata

# List volumes
docker volume ls

# Remove volume
docker volume rm mydata

# Run container with volume
docker run -v mydata:/app/data myapp

Practical Examples for Web Development

Node.js Application

Dockerfile:

FROM node:18-alpine

WORKDIR /app

# Copy dependency files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy application code
COPY . .

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

EXPOSE 3000

CMD ["node", "server.js"]

docker-compose.yml:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=mongodb://mongo:27017/myapp
    depends_on:
      - mongo

  mongo:
    image: mongo:7
    volumes:
      - mongo_data:/data/db

volumes:
  mongo_data:

React Development Environment

Dockerfile.dev:

FROM node:18

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

docker-compose.yml:

version: '3.8'

services:
  react-app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - CHOKIDAR_USEPOLLING=true

Full-Stack Application (MERN)

version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    volumes:
      - ./backend:/app
      - /app/node_modules
    environment:
      - MONGO_URL=mongodb://mongo:27017/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - mongo
      - redis

  mongo:
    image: mongo:7
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db

  redis:
    image: redis:7
    ports:
      - "6379:6379"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - frontend
      - backend

volumes:
  mongo_data:

WordPress with MySQL

version: '3.8'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8000:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: secret
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html

  db:
    image: mysql:8
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: rootsecret
    volumes:
      - db_data:/var/lib/mysql

volumes:
  wordpress_data:
  db_data:

Docker Best Practices

1. Use Official Base Images

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

# Avoid - unknown source
FROM someuser/node

2. Multi-Stage Builds

Reduce final image size:

# Build stage
FROM node:18 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/node_modules ./node_modules
CMD ["node", "dist/server.js"]

3. Optimize Layer Caching

# Good - dependencies cached separately
COPY package*.json ./
RUN npm install
COPY . .

# Bad - full rebuild on any file change
COPY . .
RUN npm install

4. Use .dockerignore

# .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
.vscode

5. Run as Non-Root User

FROM node:18-alpine

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

USER nodejs

# Rest of Dockerfile

6. Minimize Layers

# Good - single layer
RUN apt-get update && apt-get install -y \
    package1 \
    package2 \
    && rm -rf /var/lib/apt/lists/*

# Bad - multiple layers
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2

7. Use Specific Image Tags

# Good - specific version
FROM node:18.17.0-alpine

# Bad - unpredictable
FROM node:latest

Docker Compose Commands

# Start services
docker-compose up
docker-compose up -d  # Detached mode

# Build images
docker-compose build
docker-compose build --no-cache

# Stop services
docker-compose stop
docker-compose down  # Stop and remove

# View logs
docker-compose logs
docker-compose logs -f service_name

# Execute command
docker-compose exec service_name bash

# List services
docker-compose ps

# Restart service
docker-compose restart service_name

Development vs Production

Development Configuration

docker-compose.dev.yml:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app  # Live code reloading
      - /app/node_modules
    environment:
      - NODE_ENV=development
    ports:
      - "3000:3000"

Production Configuration

docker-compose.prod.yml:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - NODE_ENV=production
    restart: always
    ports:
      - "80:3000"

Usage:

# Development
docker-compose -f docker-compose.dev.yml up

# Production
docker-compose -f docker-compose.prod.yml up -d

Docker Registry and Deployment

Docker Hub

# Login
docker login

# Tag image
docker tag myapp:latest username/myapp:1.0

# Push to Docker Hub
docker push username/myapp:1.0

# Pull from Docker Hub
docker pull username/myapp:1.0

Private Registry

# Run private registry
docker run -d -p 5000:5000 --name registry registry:2

# Tag for private registry
docker tag myapp:latest localhost:5000/myapp:1.0

# Push to private registry
docker push localhost:5000/myapp:1.0

Debugging Docker Containers

View Container Details

# Container logs
docker logs container_name
docker logs --tail 100 container_name

# Container processes
docker top container_name

# Container resource usage
docker stats container_name

# Container details
docker inspect container_name

Troubleshooting Common Issues

Container exits immediately:

docker logs container_name
docker run -it image_name /bin/sh

Port already in use:

# Find process using port
lsof -i :3000
# Or change port mapping
docker run -p 3001:3000 myapp

Cannot connect to Docker daemon:

# Linux - add user to docker group
sudo usermod -aG docker krithin

# Restart Docker service
sudo systemctl restart docker

Docker Security

Security Best Practices

1. Don't run as root
2. Scan images for vulnerabilities
3. Use minimal base images
4. Keep images updated
5. Don't store secrets in images
6. Limit container resources

Docker Secrets (Swarm)

# Create secret
echo "db_password" | docker secret create db_pass -

# Use in service
docker service create \
  --name myapp \
  --secret db_pass \
  myapp:latest

Docker Alternatives and Related Tools

- Podman: Daemonless container engine
- Kubernetes: Container orchestration
- Docker Swarm: Docker's native orchestration
- LXC: Linux containers
- containerd: Container runtime

Performance Optimization

Reduce Image Size

# Use alpine variants
FROM node:18-alpine  # ~170MB vs ~900MB

# Remove development dependencies
RUN npm ci --only=production

# Clean up in same layer
RUN apt-get update && apt-get install -y package \
    && rm -rf /var/lib/apt/lists/*

Resource Limits

docker run -m 512m --cpus=2 myapp

# docker-compose.yml
services:
  app:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 512M

Real-World Deployment Example

Complete production setup:

version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app

  app:
    build: .
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://db:5432/myapp
    depends_on:
      - db
      - redis
    restart: always

  db:
    image: postgres:15
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: always

  redis:
    image: redis:7-alpine
    restart: always

volumes:
  postgres_data:

Conclusion

Docker has become an essential tool for modern web development, offering:

- Consistent development environments
- Simplified dependency management
- Easy deployment and scaling
- Isolation and security
- Microservices architecture support

Whether you're building a simple application or a complex microservices system, Docker provides the tools and flexibility needed for efficient development and deployment.

Start with simple containers, gradually adopt Docker Compose for multi-container applications, and explore orchestration tools like Kubernetes as your needs grow.

For deploying Docker containers in production, consider reliable hosting providers like Hostinger or Cloudways that offer excellent Docker support and container hosting with managed services.