How to Install Docker on a Raspberry Pi: Complete Step-by-Step Setup Guide for ARM Devices

how to Install Docker on a Raspberry Pi

The first time I installed Docker on a Raspberry Pi, I expected some kind of special “ARM edition” — instead I found that Docker’s official installer script handles architecture detection automatically, and the real differences show up later, in image availability and performance characteristics rather than the installation process itself. This guide covers getting Docker running cleanly on a Raspberry Pi, plus the ARM-specific quirks that trip people up.

Prerequisites

  • A Raspberry Pi (3, 4, or 5 recommended; Docker works on a Pi 2 but performance will be rough)
  • Raspberry Pi OS (64-bit recommended — Docker support for 32-bit ARM exists but many modern images and tools assume arm64)
  • At least a 16GB SD card, ideally a USB SSD for anything beyond light use
  • SSH access or a keyboard/monitor attached directly

Check your OS architecture first:

uname -m
aarch64

aarch64 confirms a 64-bit ARM OS, which I strongly recommend for Docker. If this returns armv7l, you’re on 32-bit, which still works but with a smaller pool of compatible pre-built images.

Step 1: Update the System

sudo apt-get update && sudo apt-get upgrade -y

Step 2: Install Docker Using the Official Convenience Script

Docker publishes a script that auto-detects the OS and architecture, including Raspberry Pi OS:

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

Expected output (abbreviated):

# Executing docker install script, commit 26ff363...
+ sh -c apt-get update -qq
+ sh -c apt-get install -y -qq apt-transport-https ca-certificates curl
+ sh -c curl -fsSL "https://download.docker.com/linux/raspbian/gpg"
+ sh -c apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
================================================================================
To run Docker as a non-privileged user, consider setting up the
Docker daemon in rootless mode for your user:

    dockerd-rootless-setuptool.sh install

Visit https://docs.docker.com/go/rootless/ to learn about rootless mode.

To run the Docker daemon as a fully privileged service, but granting non-root
users access, refer to https://docs.docker.com/go/daemon-access/

WARNING: Access to the remote API on a privileged Docker daemon is equivalent
         to root access on the host.
================================================================================

Notice it correctly identified the raspbian repository — Docker’s script detects Raspberry Pi OS specifically and points at the right package repo automatically.

Step 3: Add Your User to the Docker Group

sudo usermod -aG docker $USER

Log out and back in (or reboot) for the group membership to take effect:

sudo reboot

Step 4: Verify the Installation

docker --version
Docker version 27.3.1, build ce12230
docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
Digest: sha256:1408fec78703e60de6b7c8f8e9c8b0e9a3fe5c9f5e8b0e9a3fe5c9f5e8b0e9a
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

Note that Docker automatically pulled the arm64 (or arm/v7) variant of the hello-world manifest — I didn’t have to specify an architecture manually, since Docker images can be multi-arch manifests that resolve to the correct platform automatically.

Step 5: Enable Docker to Start on Boot

sudo systemctl enable docker
sudo systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2026-07-29 09:12:03 BST; 2min ago

Step 6: Install Docker Compose (Included as a Plugin)

Modern Docker installs the Compose plugin automatically via the convenience script. Verify:

docker compose version
Docker Compose version v2.29.2

Step 7: Run a Real Workload — Pi-hole as an Example

Raspberry Pis are frequently used for lightweight always-on services. Pi-hole is a common one and ships proper multi-arch images:

docker run -d \
  --name pihole \
  -p 53:53/tcp -p 53:53/udp -p 80:80 \
  -e TZ="Europe/London" \
  -v pihole_etc:/etc/pihole \
  -v pihole_dnsmasq:/etc/dnsmasq.d \
  --restart unless-stopped \
  pihole/pihole:latest
docker logs pihole | tail -5
[i] Pi-hole blocking is enabled
[i] Added ENV to FTL's config file
[✓] FTL is listening on port 53
[✓] Pi-hole enabled

Architecture-Specific Considerations

Some Docker images on Docker Hub only publish amd64 builds and will either fail outright or run painfully slowly under QEMU-based emulation on ARM. Before pulling an unfamiliar image, I check its supported platforms:

docker manifest inspect nginx:latest | grep architecture
"architecture": "amd64",
"architecture": "arm64",
"architecture": "arm",

If an image lacks arm64/arm, Docker Desktop-style emulation (binfmt_misc + QEMU) can sometimes still run it, but I avoid this for anything beyond quick tests since performance is significantly degraded and some syscalls behave differently under emulation.

Internal Working: Multi-Arch Manifests

Modern Docker images on registries are often published as “manifest lists” — a single tag (like nginx:latest) that actually points to several architecture-specific image manifests (amd64, arm64, arm/v7, and so on). When I run docker pull nginx on a Raspberry Pi, the Docker client reads the manifest list, detects the host’s CPU architecture via the daemon’s platform info, and pulls only the matching manifest. This is entirely transparent and is why the same docker run command works unmodified across an x86 laptop and an ARM Raspberry Pi, as long as the image publisher built and pushed an ARM variant.

Networking Considerations

Docker’s default bridge networking, iptables-based NAT, and DNS resolution between containers all work identically on Raspberry Pi OS as on any other Linux Docker host, since they rely on standard Linux kernel features (netfilter, network namespaces) available on the Pi’s kernel. The one thing worth checking is that iptables (not nftables-only mode) is properly configured, since some minimal ARM OS images ship without full iptables support by default — the Docker installer handles this dependency automatically in most cases.

Storage Considerations

SD cards have limited write endurance and relatively slow random I/O, which matters a lot for Docker workloads that do heavy layer writes (image builds, databases). For anything beyond light use, I move Docker’s data root to a USB-attached SSD:

sudo systemctl stop docker
sudo mkdir -p /mnt/ssd/docker

/etc/docker/daemon.json:

{
  "data-root": "/mnt/ssd/docker"
}
sudo systemctl start docker
docker info | grep "Docker Root Dir"
Docker Root Dir: /mnt/ssd/docker

Security Considerations

  • Keep Raspberry Pi OS and Docker packages updated regularly — ARM SBCs are frequently deployed as always-on home-network appliances and often go unpatched for long stretches.
  • Avoid running the Docker daemon’s API over an unauthenticated TCP socket, especially since many Pi setups sit on home networks with other, less-trusted devices.
  • Change any service’s default credentials (a common Pi use case is self-hosted apps like Pi-hole, Home Assistant, or Portainer) immediately after first login.

Troubleshooting

“exec format error” when running a container This means the pulled image doesn’t support your Pi’s architecture. Check with docker manifest inspect as shown above, and look for an explicitly arm64/arm compatible image or tag.

Docker service fails to start after installing

sudo journalctl -u docker --no-pager | tail -30

Common cause on Raspberry Pi OS Lite images: missing cgroup kernel parameters. Check /boot/cmdline.txt includes cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory.

Extremely slow builds or pulls Usually SD card I/O bottlenecks — moving Docker’s data root to a USB SSD (Step above) typically resolves this dramatically.

Monitoring

docker stats
CONTAINER ID   NAME     CPU %     MEM USAGE / LIMIT     NET I/O
f7a8b9c0d1e2   pihole   1.2%      45.3MiB / 3.7GiB      120kB / 45kB

For temperature and throttling awareness specific to the Pi hardware itself (relevant since sustained Docker workloads can heat up a Pi enough to throttle):

vcgencmd measure_temp
temp=52.1'C

Best Practices

  • Use 64-bit Raspberry Pi OS for the widest image compatibility.
  • Move Docker’s data root to external SSD storage for anything beyond casual use.
  • Always check multi-arch support before pulling unfamiliar images.
  • Enable docker to start on boot for always-on appliance-style deployments.
  • Monitor Pi temperature under sustained container workloads, since thermal throttling silently degrades performance.

Summary

Installing Docker on a Raspberry Pi is nearly identical to installing it on any other Linux host, thanks to Docker’s official install script detecting Raspberry Pi OS and ARM architecture automatically, and thanks to multi-arch image manifests resolving transparently at pull time. The real ARM-specific work is downstream of installation: verifying image architecture support, managing SD card I/O limitations, and keeping an eye on thermal behavior under load.

References

  • Docker Engine installation overview: https://docs.docker.com/engine/install/
  • Docker convenience install script source: https://github.com/docker/docker-install
  • Raspberry Pi OS documentation: https://www.raspberrypi.com/documentation/computers/os.html
  • Docker multi-platform images: https://docs.docker.com/build/building/multi-platform/
Total
0
Shares

Leave a Reply

Previous Post
Socket and network libraries in Ruby

Socket and Network Libraries in Ruby: Complete Guide to Network Programming with TCPSocket and UDPSocket

Next Post
How T Set Up a Local Docker Host by Using Vagrant

How to Set Up a Local Docker Host by Using Vagrant: Development Environment Configuration Guide

Related Posts