Skip to content
← All writing
February 11, 2026·3 min read

A Practical Intro to Docker for App Developers

If you've ever deployed a backend and watched it break on the server despite working perfectly on your laptop, Docker was built for you. It packages your application together with everything it needs to run, so it behaves the same everywhere. This is a plain-language introduction to what Docker is and how to actually use it.

The problem it solves

Software depends on its environment: a specific language version, particular libraries, system packages, environment variables. Your machine has one setup; the server has another. That mismatch is the source of the eternal "but it works on my machine."

Docker fixes this by bundling your app and its entire environment into one portable unit. That unit runs identically on your laptop, a teammate's machine, and production — because it carries its environment with it instead of borrowing the host's.

Two words you need: image and container

  • An image is a blueprint — a snapshot of your app plus everything it needs (runtime, dependencies, code, config). It's built once and doesn't change.
  • A container is a running instance of an image. You can start many containers from the same image, and each is isolated from the others and from the host.

The mental model: an image is like a class, a container is like an object created from it. You build an image, then run containers from it.

The Dockerfile

You describe how to build your image in a Dockerfile — a recipe of steps. A simple one for a Python backend:

FROM python:3.12-slim          # start from a base image with Python
WORKDIR /app                   # set the working directory
COPY requirements.txt .        # copy dependency list first (for caching)
RUN pip install -r requirements.txt
COPY . .                       # copy the rest of the app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Build it into an image, then run a container from it:

docker build -t myapp .
docker run -p 8000:8000 myapp

That's the whole core loop: write a Dockerfile, build an image, run a container.

A few practices that matter

  • Order layers for caching. Docker caches each step. Copying your dependency file and installing before copying the rest of the code means dependencies aren't reinstalled every time you change a line of app code — a big speed-up.
  • Use small base images. Slim base images produce smaller, faster, more secure containers than full ones. Go apps can even compile to a tiny static binary in a minimal image.
  • Don't bake in secrets. Never put passwords or API keys in the image. Pass them in as environment variables at run time.
  • Use .dockerignore. Exclude junk (local caches, .git, build artifacts) so images stay lean and builds stay fast.

Running more than one service

Real apps often need several pieces — an API, a database, a cache — running together. Docker Compose lets you define them all in a single file and start them with one command, wired up to talk to each other. It's the natural next step once your app is more than a single container, and it makes local development environments reproducible for the whole team.

Where it pays off

  • Consistent environments across every developer and every stage of deployment.
  • Easy onboarding — a new teammate runs one command instead of following a page of setup instructions.
  • Portable deployment — most modern hosting platforms take a container and run it, so you're not locked to one provider's quirks.
  • Isolation — each app carries its own dependencies, so two services with conflicting requirements coexist peacefully.

Summary

Docker packages your app with its whole environment into an image, which you run as isolated containers that behave the same everywhere — ending "works on my machine." Write a Dockerfile, order its steps for caching, keep base images small, pass secrets in at run time, and use Compose when you have multiple services. It's a modest amount to learn for a large gain in deployment confidence, and it's become a standard part of shipping backends.