Docker
Introduction to Docker
Welcome to the world of Docker - a revolutionary platform transforming the way software is developed, shipped, and run across diverse computing environments. In this introductory guide, we'll embark on a journey to explore the fundamental concepts, capabilities, and benefits of Docker.
What is Docker?
Docker is an open-source platform that enables developers to package applications and their dependencies into lightweight, portable containers. These containers encapsulate everything an application needs to run, including code, runtime, libraries, and system tools. Unlike traditional virtualization methods, Docker containers virtualize the operating system layer, making them highly efficient and portable across different environments.
That difference is the whole story:
VIRTUAL MACHINES CONTAINERS
┌───────┬───────┬───────┐ ┌───────┬───────┬───────┐
│ App │ App │ App │ │ App │ App │ App │
├───────┼───────┼───────┤ ├───────┼───────┼───────┤
│ libs │ libs │ libs │ │ libs │ libs │ libs │
├───────┼───────┼───────┤ ├───────┴───────┴───────┤
│ Guest │ Guest │ Guest │ │ Docker Engine │
│ OS │ OS │ OS │ ├───────────────────────┤
├───────┴───────┴───────┤ │ Host OS │
│ Hypervisor │ ├───────────────────────┤
├───────────────────────┤ │ Hardware │
│ Host OS │ └───────────────────────┘
├───────────────────────┤
│ Hardware │
└───────────────────────┘
gigabytes each, megabytes each,
boots in minutes starts in milliseconds
own kernel shares the host kernel
A virtual machine ships an entire guest operating system per application. A container ships only the application and its libraries, and borrows the kernel it is running on. That is why a container starts in the time it takes a process to start - because that is all it is.
Key Components
At the core of Docker is the Docker Engine, a powerful runtime and set of tools that facilitate the creation, deployment, and management of containers. Docker Engine runs on top of the host operating system and orchestrates containerized applications seamlessly.
Docker Images and Containers
Central to Docker's workflow are Docker images and containers. Docker images are read-only templates that serve as the blueprint for containers. They are created using Dockerfiles, which contain instructions for building the image layer by layer. Once an image is built, it can be instantiated into a container - a runnable instance of the image that isolates the application and its dependencies.
┌────────────────────────┐
│ Dockerfile │
│ │
│ FROM python:3.12-slim │
│ COPY requirements.txt │
│ RUN pip install ... │
│ COPY app.py │
│ CMD ["python", ...] │
└────────────┬───────────┘
│ docker build
▼
┌────────────────────────┐
│ IMAGE - read-only │ each instruction here
│ ┌──────────────────┐ │ becomes one layer of
│ │ CMD │ │ the image, and is
│ ├──────────────────┤ │ rebuilt only when it
│ │ COPY app.py │ │ or something below
│ ├──────────────────┤ │ it changes
│ │ RUN pip install │ │
│ ├──────────────────┤ │
│ │ python:3.12-slim │ │
│ └──────────────────┘ │
└──┬──────────────────┬──┘
│ │
│ docker push │ docker run
▼ ▼
┌──────────────┐ ┌─────────────────────┐
│ Docker Hub │ │ CONTAINER │
│ (registry) │ │ the image + a thin │
└──────────────┘ │ writable layer │
└─────────────────────┘
The layering matters more than it first appears. Each instruction in a Dockerfile produces a layer, and Docker caches them. Put the instructions that rarely change (installing dependencies) above the ones that change constantly (copying your source), and a rebuild after a one-line code edit takes a second instead of a minute. Get the order backwards and you reinstall every dependency on every build.
Docker Hub
Docker Hub is a cloud-based registry service that hosts a vast repository of Docker images. It provides a centralized platform for developers to discover, share, and collaborate on containerized applications. Docker Hub simplifies the process of finding and distributing Docker images, accelerating the development and deployment of software.
Benefits of Docker
Docker offers numerous advantages for developers, operations teams, and businesses alike. By standardizing the application packaging process, Docker streamlines development workflows, improves resource utilization, and enhances application portability. With Docker, organizations can build, ship, and run applications consistently across diverse environments, from developer laptops to production servers and cloud platforms.
Try it on a Raspberry Pi: where "runs anywhere" meets its limit
Docker's portability promise is real, but it has exactly one important asterisk, and a Raspberry Pi is the cheapest way to run into it. Your laptop is almost certainly x86_64 (or arm64 if it is an Apple Silicon Mac). A Pi is arm64. An image is only portable across machines that share its architecture - so this is the ideal place to learn what that actually means.
Install Docker on the Pi
On a 64-bit Raspberry Pi OS:
curl -fsSL https://get.docker.com | sh
# so you can drop the sudo - log out and back in afterwards
sudo usermod -aG docker $USER
docker run --rm hello-world
Meet the architecture trap
Before building anything, provoke the error deliberately - it is much less confusing when you have seen it on purpose:
# arch of the Pi itself
uname -m # aarch64
# a normal, multi-arch image: works fine
docker run --rm alpine uname -m
# force the x86 build of the same image on an ARM machine
docker run --rm --platform linux/amd64 alpine uname -m
# exec /bin/uname: exec format error
That exec format error is the single most common surprise when moving containers between a laptop and a Pi. The image pulled perfectly - it just contains binaries the CPU cannot run.
Build something Pi-specific
Here is a small app that reports the Pi's own CPU temperature. It is worth building precisely because of how it works: the container has no special privileges, yet it can read the host's thermal sensor - because a container shares the host kernel rather than virtualising one.
# app.py
from flask import Flask
app = Flask(__name__)
def cpu_temp():
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return int(f.read()) / 1000
@app.route("/")
def index():
return {"cpu_temp_c": round(cpu_temp(), 1)}
app.run(host="0.0.0.0", port=8000)
# requirements.txt
flask
The Dockerfile, ordered so the dependency layer stays cached:
FROM python:3.12-slim
WORKDIR /app
# Dependencies first. This layer is only rebuilt when
# requirements.txt changes - not on every code edit.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Source last, because it changes constantly
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]
Build and run it directly on the Pi:
docker build -t pitemp .
docker run -d --name pitemp -p 8000:8000 pitemp
curl http://localhost:8000
# {"cpu_temp_c":47.8}
Now run docker history pitemp and you will see the layers from the diagram above, with their individual sizes.
Building for the Pi from your laptop
Building on the Pi works but is slow. buildx cross-builds for arm64 on your laptop and pushes a multi-arch image - one tag that serves the right binaries to whichever machine pulls it:
docker buildx create --use # once
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t youruser/pitemp:latest \
--push .
Then on the Pi, docker pull youruser/pitemp fetches the arm64 variant automatically. This is what "multi-arch" means on Docker Hub, and why alpine worked earlier while the forced --platform did not.
Keep it running with Compose
A single docker run does not survive a reboot. This is the version you actually want on a Pi doing a job:
# compose.yml
services:
pitemp:
image: youruser/pitemp:latest
ports:
- "8000:8000"
restart: unless-stopped
deploy:
resources:
limits:
memory: 128M
docker compose up -d
docker compose logs -f
Pi-specific gotchas
- Not every image has an
arm64build. Check the tags on Docker Hub before you plan around an image. Older or niche images are frequentlyamd64-only. - Docker writes a lot. Images, layers and logs all land on the SD card. Boot from an SSD if you can, and cap log growth in
/etc/docker/daemon.jsonwith"log-opts": {"max-size": "10m"}. - Set memory limits. A container with no limit can take the whole Pi down. The
limitsblock above costs nothing to add. - GPIO needs to be passed in. Containers do not see hardware by default. Reading
/sysworks as shown, but toggling pins needs--device /dev/gpiomem- grant the narrowest access that works rather than reaching for--privileged. docker system pruneis your friend. Old layers accumulate quietly and a Pi has nowhere to hide them.
Getting Started
Whether you're a seasoned developer or new to containerization, Docker provides a user-friendly experience for building and deploying applications. In the upcoming sections, we'll delve deeper into Docker's features, best practices, and real-world use cases to empower you on the Docker journey.

