“It works on my machine.”

If you’ve been in software development for more than five minutes, you’ve heard this phrase. It’s the battle cry of developers everywhere, usually uttered right before an ops person’s eye starts twitching.

Docker fixes this. Here’s how.

What Docker Actually Is

Docker is a way to package your application along with everything it needs to run — the code, runtime, libraries, environment variables, config files — into a single portable unit called a container.

Think of it like shipping containers in the real world. Before standardized shipping containers, moving goods was chaos. Different trucks, different ships, different handling at every port. Standardized containers changed everything: pack once, ship anywhere.

Docker does the same for software.

Containers vs. Virtual Machines

You might be thinking: “Isn’t this just a VM?”

No, and the difference matters.

Virtual Machines:

  • Run a complete operating system
  • Take minutes to boot
  • Use gigabytes of memory
  • Heavy resource overhead

Containers:

  • Share the host OS kernel
  • Start in milliseconds
  • Use megabytes of memory
  • Minimal overhead
GuAeApspptpAOASGDVuoiAeHHAcHrpsyoCpkotptpsopesuetnrtaBOrtBlSvOaEOiSinSMsngaoeicrrnhseinGeAuAspeppsptCCOS

Containers are lightweight because they share the host’s kernel. This means you can run dozens of containers on hardware that might struggle with a handful of VMs.

Your First Dockerfile

Let’s create a simple Python web application in Docker.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Use an official Python runtime as base
FROM python:3.11-slim

# Set the working directory
WORKDIR /app

# Copy requirements first (for layer caching)
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose the port
EXPOSE 8000

# Run the application
CMD ["python", "app.py"]

Build and run it:

1
2
3
4
5
# Build the image
docker build -t my-python-app .

# Run a container
docker run -p 8000:8000 my-python-app

That’s it. Your app is now running in an isolated container, and it will run exactly the same way on any machine with Docker installed.

Real-World Use Cases

1. Development Environments

No more “install Python 3.8, PostgreSQL 12, Redis 6.2, and pray they don’t conflict.”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - db
      - redis
      
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: devpassword
      
  redis:
    image: redis:7-alpine

Run docker-compose up and your entire stack is running. New developer joins? They run the same command. Done.

2. CI/CD Pipelines

Test in the same environment you deploy to:

1
2
3
4
5
6
7
8
9
# GitHub Actions example
jobs:
  test:
    runs-on: ubuntu-latest
    container: python:3.11
    steps:
      - uses: actions/checkout@v4
      - run: pip install -r requirements.txt
      - run: pytest

3. Microservices

Each service gets its own container, its own dependencies, its own scaling:

1
2
3
docker run -d --name auth-service auth:latest
docker run -d --name user-service user:latest
docker run -d --name order-service order:latest

Scale what needs scaling, update what needs updating, without touching the rest.

Common Gotchas

1. Don’t run as root

1
2
3
# Create a non-root user
RUN useradd -m appuser
USER appuser

2. Use .dockerignore

#.n.go_.e.idppndteyyvo_cccmakocedhrueil_ge_nsore

3. Layer caching matters

Put things that change frequently (your code) after things that don’t (dependencies). This makes rebuilds much faster.

4. Use specific tags, not latest

1
2
3
4
5
# Bad
FROM python:latest

# Good  
FROM python:3.11.7-slim-bookworm

Beyond Basics: Orchestration

Once you have multiple containers, you need orchestration. That’s where tools like:

  • Docker Compose — Multi-container applications on a single host
  • Docker Swarm — Simple clustering built into Docker
  • Kubernetes — Industry standard for serious container orchestration

Start with Compose, graduate to Kubernetes when you need it.

The Bottom Line

Docker isn’t just a tool — it’s a mindset shift. Instead of configuring servers, you describe your environment in code. Instead of hoping things work in production, you know they will because it’s the same container.

The “it works on my machine” era is over. Welcome to “it works in a container.”


Just getting started with Docker? Have questions? Find me on Twitter — happy to help.