What Is Docker
Docker is a platform for building, shipping, and running applications inside lightweight containers. This lesson explains why it matters and how to start.
Focus: what is docker and why use it
You deploy your application to production. It works locally. Your teammate pulls the latest code, installs dependencies, and — boom — something breaks. The familiar chorus of "it works on my machine" echoes across the room. This friction — environment inconsistency — wastes hours, delays releases, and erodes confidence. Docker solves this by packaging your application with everything it needs to run: code, runtime, system tools, libraries, and settings. Once packaged, that container runs identically on any machine that has Docker installed. This lesson unpacks what Docker is and why it matters for modern development.
The problem this lesson solves
Before Docker, every deployment required careful environment setup: install Python 3.10, pin exact library versions, configure OS-level dependencies, and pray the production server had the same glibc version. Teams relied on long setup scripts, manual checklists, and often discovered mismatches only after deployment.
The core pain points: - Environment drift: Your laptop and the production server diverge over time, causing bugs that are hard to replicate. - Dependency hell: Library A needs patched OpenSSL; Library B needs unpatched OpenSSL. Stuck. - Onboarding friction: New developers spend days installing tools and fighting obscure OS-specific errors. - CI/CD variability: Tests pass on your machine but fail on the CI server because of slight environment differences.
Docker eliminates "works on my machine" by bundling the application with its full execution environment into a container image — a lightweight, standalone, executable package.
Core concept / mental model
Think of Docker containers as standardized shipping containers for software. A shipping container can carry any cargo — electronics, grain, cars — and it fits onto any ship, truck, or train around the globe because of a uniform interface (size, corner castings). Similarly, a Docker container can carry any application and its dependencies, and it runs on any system that has a Docker engine.
Key terms
| Term | Definition |
|---|---|
| Container image | A read-only template with instructions for creating a container. Like a class definition. |
| Container | A runnable instance of an image. Like an object from a class. You can start, stop, move, delete containers. |
| Dockerfile | A text file with commands to build an image. Think of it as a recipe. |
| Docker Engine | The runtime that builds and runs containers. The kernel of Docker. |
| Registry | A storage and distribution system for images. Docker Hub is the public default. |
Mental model: A container image is the "blueprint," a container is the "running house," and a Dockerfile is the "construction plan."
What makes containers different from virtual machines?
Traditional virtual machines (VMs) virtualize hardware — each VM includes a full guest operating system, kernel, and virtual devices. This is heavy and slow to start. Containers virtualize the operating system — they share the host kernel but isolate the application process and its filesystem.
| Feature | Virtual Machine | Container |
|---|---|---|
| Size | GBs (full OS) | MBs (app + libs) |
| Boot time | Minutes | Seconds |
| Resource overhead | High (per-VM OS) | Low (shared kernel) |
| Isolation | Strong (separate kernel) | Good (namespace + cgroups) |
| Portability | Depends on hypervisor | Runs on any Docker host |
This means you can run dozens of containers on a single laptop with negligible overhead.
How it works step by step
Docker uses client-server architecture. You interact with the Docker daemon via a command-line client.
- Write a Dockerfile — define the application environment (base OS, dependencies, code copy, commands).
- Build the image —
docker build .reads the Dockerfile, executes each instruction, and produces an image. Each instruction creates a cached layer, enabling fast rebuilds. - Run a container —
docker run <image>creates a writable container layer on top of the immutable image layers and starts the process inside. - Share or distribute — push the image to a registry (
docker push), and anyone can pull and run it (docker pull) with zero setup beyond having Docker installed.
Pro tip: Image layers are cached. If you change your code but not your dependencies, only the layer with code changes is rebuilt. This makes development iteration fast.
Lifecycle of a container:
- Created from a base image or scratch.
- Starts in its own isolated filesystem, network, and process namespace.
- Runs the defined command (e.g., python app.py).
- When the main process exits, the container stops — unless you restart it.
Hands-on walkthrough
Let's Dockerize a minimal Python app and see the magic. Ensure you have Docker Desktop installed (download from docker.com). Run these commands in your terminal.
Step 1: Create a simple Python app
# app.py
from http.server import HTTPServer, BaseHTTPRequestHandler
class HelloHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b"Hello from Docker!\n")
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8000), HelloHandler)
print("Server running on port 8000...")
server.serve_forever()
Step 2: Write a Dockerfile
Create a file named Dockerfile (no extension) in the same directory:
# Use an official Python 3.10 image as the base
FROM python:3.10-slim
# Set working directory inside the container
WORKDIR /app
# Copy the app file into the container
COPY app.py .
# Command to run when the container starts
CMD ["python", "app.py"]
Step 3: Build the image
docker build -t hello-docker .
Expected output (last lines):
Successfully built abc123
Successfully tagged hello-docker:latest
Step 4: Run the container
docker run -p 4000:8000 hello-docker
-p 4000:8000maps your machine's port 4000 to the container's port 8000.- You should see:
Server running on port 8000...
Now open a browser to http://localhost:4000. You'll see Hello from Docker!.
Step 5: Stop the container
Press Ctrl+C in the terminal running the container.
Pro tip: Use
docker psto list running containers anddocker stop <container_id>to stop them gracefully.
Compare options / when to choose what
Docker is not the only container runtime, but it's the most popular and user-friendly.
| Alternative | Use case | Pros | Cons |
|---|---|---|---|
| Docker | General-purpose, dev-to-prod, local development | Rich ecosystem, great tooling, OCI compliant | Slightly heavier than some lighter runtimes |
| Podman | Rootless containers, Red Hat ecosystems | No daemon, fully OCI compliant | Smaller community, less tooling |
| containerd | Kubernetes node runtime | Lightweight, industry standard for K8s | No CLI — used as an API |
| LXC/LXD | System containers (lightweight VMs) | Run full OS containers | Not app-centric, different paradigm |
For most developers starting with containers, Docker is the right choice. It has the largest ecosystem, best documentation, and integrates with nearly every CI/CD tool.
Troubleshooting & edge cases
Common errors
| Error | Cause | Fix |
|---|---|---|
Cannot connect to the Docker daemon |
Docker not running | Start Docker Desktop / Docker service |
port is already allocated |
Port conflict | Stop other container using same host port, or change -p mapping |
docker: 'build' is not a docker command |
Docker not installed | Install Docker from docker.com |
The command returned a non-zero code |
Dockerfile instruction failed | Check error line number in build output (often missing file or wrong permission) |
Edge case: Container exits immediately
If your container runs and exits right away, check the command. A web server stays running until you stop it. A script that finishes its work will exit. Use docker logs <container_id> to see the output.
docker run --name test hello-docker
# If it exits, check logs
docker logs test
Edge case: Permission issues on Linux
Add your user to the docker group to run Docker without sudo:
sudo usermod -aG docker $USER
newgrp docker # or log out and back in
What you learned & what's next
This lesson introduced you to the central pain point Docker solves — environment inconsistency — and gave you a mental model of containers as standardized shipping containers. You built a real Docker image from a Dockerfile, ran a container, and mapped ports to access the app. You also compared Docker with alternatives and saw how to fix common issues.
Key takeaways:
- Docker packages an application with its dependencies into a portable container image.
- Containers share the host OS kernel, making them lightweight vs. VMs.
- A Dockerfile defines how to build an image layer by layer.
- docker build creates the image; docker run starts a container.
- Port mapping (-p host:container) connects container services to your machine.
What's next: In the next lesson, you'll learn how to manage multiple containers with Docker Compose, connecting a web app to a database — all defined in a single YAML file. This is the next step toward replicating a microservices environment locally.
Practice recap
Try modifying the example: change the response text in app.py, rebuild the image with docker build -t hello-docker ., and run a new container. Observe that only the changed layer is rebuilt. Then create a second container from the same image with a different command (e.g., docker run hello-docker python app.py). This exercise reinforces the layer cache and the flexibility of containers.
Common mistakes
- Running
docker buildwithout a Dockerfile in the current directory — Docker picks up . by default, but if you specify a file without proper path, it fails. - Forgetting to map ports — container runs fine but you can't access the service because no -p flag was provided.
- Using
latestbase images without specifying a version — can lead to unexpected changes when base images update. - Trying to run Docker in a container without understanding privileged mode — containers by default cannot run Docker daemon inside.
Variations
- Use Podman as a drop-in replacement for Docker if you need rootless containers in a production environment.
- Consider Docker Desktop vs. Docker Engine on Linux — Desktop includes a GUI and Kubernetes integration; Engine is CLI-only but lighter.
- Leverage multi-stage builds in Dockerfile to produce smaller final images by separating build-time and runtime dependencies.
Real-world use cases
- Microservices development: run 5+ services (API, DB, cache, queue) in separate containers on a single dev machine, each with isolated dependencies.
- CI/CD pipeline: use Docker to run tests in an environment identical to production — same OS, same libraries, same Python version.
- Onboarding: new team members clone a repo, run
docker-compose up, and have a fully functional development environment in minutes.
Key takeaways
- Docker eliminates 'works on my machine' by bundling app + dependencies into a portable container image.
- Containers are lightweight (MBs), fast (seconds to start), and portable across any Docker host.
- A Dockerfile is the blueprint for building an image; each instruction creates a cached layer.
- Use
docker build -t <name> .to build anddocker run -p host:container <name>to run. - Docker's ecosystem (Docker Hub, Compose, Swarm) is the most mature toolset for container development.
- Start with Docker for general use; consider Podman or containerd when specific security or K8s constraints arise.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.