$linuxjunkies
>

Run Docker on a Raspberry Pi

Install Docker and Docker Compose on a Raspberry Pi, handle arm64 vs armv7 images, protect your SD card, and configure log rotation with journald.

BeginnerUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • Raspberry Pi 3, 4, or 5 with a 64-bit OS flashed and SSH or desktop access
  • A USB SSD or external drive for persistent data (strongly recommended)
  • Internet connection for downloading packages and images
  • A user account with sudo privileges

The Raspberry Pi is a capable little server once you pair it with Docker. Whether you're running a Pi 4 with 4 GB of RAM or a Pi 5, containerized workloads let you deploy self-hosted services without fighting dependency conflicts. The main gotcha is architecture: your Pi runs ARM, not x86, so you must pick images built for arm64 (AArch64, Pi 3/4/5 running 64-bit OS) or armv7 (32-bit Raspberry Pi OS). Most popular images on Docker Hub now ship multi-arch manifests, but niche images may only exist for one architecture. This guide also covers Docker Compose, log rotation to protect your SD card, and moving container data off the SD card entirely.

Prerequisites and Architecture Check

Before installing anything, confirm your OS bitness. A 64-bit kernel unlocks arm64 images, which have broader support and better performance.

uname -m

Output will be aarch64 (64-bit) or armv7l (32-bit). Raspberry Pi OS Lite 64-bit or Ubuntu Server 22.04/24.04 for Raspberry Pi are both solid choices. If you're on 32-bit OS, consider reflashing — most modern images drop armv7 support eventually.

Install Docker Engine

Use Docker's official install script or the apt repository. The convenience script is fine for personal Pi servers; production systems should pin a specific version via the repository.

Option A — Convenience script (fastest)

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

Option B — apt repository (Debian/Ubuntu/Raspberry Pi OS)

sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

For Ubuntu on Pi, replace debian with ubuntu in the repo URL.

Add your user to the docker group

sudo usermod -aG docker $USER
newgrp docker

Log out and back in (or use newgrp docker for the current session) so the group membership takes effect.

Enable and start Docker

sudo systemctl enable --now docker

Verify arm64 / armv7 Image Compatibility

Before pulling any image, check whether it supports your architecture. Docker Hub's web UI shows supported architectures under the Tags tab. From the command line:

docker buildx imagetools inspect nginx:alpine

Look for linux/arm64 or linux/arm/v7 in the output. If neither appears, the image won't run on your Pi. For armv7, search for the arm32v7 organization on Docker Hub as a fallback (e.g., arm32v7/nginx).

Run a quick sanity check to confirm Docker itself is working:

docker run --rm hello-world

Install Docker Compose

If you installed Docker via the apt repository above, the Compose v2 plugin (docker compose) is already present. Verify:

docker compose version

Expected output is something like Docker Compose version v2.27.x. The old standalone docker-compose binary (v1) is deprecated — use docker compose (with a space) going forward.

Example Compose stack

Create a directory for your project and drop in a compose.yaml:

mkdir ~/pihole && cd ~/pihole
nano compose.yaml
services:
  pihole:
    image: pihole/pihole:latest   # ships arm64 + armv7
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80"
    environment:
      TZ: America/New_York
      WEBPASSWORD: changeme
    volumes:
      - /mnt/data/pihole/etc:/etc/pihole
      - /mnt/data/pihole/dnsmasq:/etc/dnsmasq.d
docker compose up -d

Notice the volumes point to /mnt/data — not the SD card. That's intentional; see the next section.

Protect the SD Card: Move Data Off It

SD cards have limited write endurance. Docker's default storage root (/var/lib/docker) lives on the SD card and will accumulate gigabytes of layer data, logs, and container writes. Move it to a USB SSD or external drive.

Mount an external drive

sudo mkdir -p /mnt/data
sudo blkid /dev/sda1   # note the UUID
echo "UUID=your-uuid-here  /mnt/data  ext4  defaults,noatime  0  2" \
  | sudo tee -a /etc/fstab
sudo mount -a

The noatime flag prevents access-time writes on every file read — a meaningful SD card saving even for the ext4 journal partition if you only partially offload.

Relocate Docker's data root

sudo systemctl stop docker
sudo mkdir -p /mnt/data/docker
sudo nano /etc/docker/daemon.json
{
  "data-root": "/mnt/data/docker"
}
sudo rsync -aP /var/lib/docker/ /mnt/data/docker/
sudo systemctl start docker
docker info | grep "Docker Root Dir"

Confirm the root dir is /mnt/data/docker before deleting the old path. Once confirmed:

sudo rm -rf /var/lib/docker

Log Rotation with journald

Docker's default logging driver is json-file, which writes per-container log files under the data root. On a Pi, unbounded logs can fill storage fast. Switch to journald — systemd already handles rotation — or cap json-file explicitly.

sudo nano /etc/docker/daemon.json
{
  "data-root": "/mnt/data/docker",
  "log-driver": "journald",
  "log-opts": {
    "tag": "{{.Name}}"
  }
}
sudo systemctl restart docker

Read container logs via journalctl:

journalctl CONTAINER_NAME=pihole -f

Cap journald itself in /etc/systemd/journald.conf:

sudo nano /etc/systemd/journald.conf
[Journal]
SystemMaxUse=200M
SystemKeepFree=100M
sudo systemctl restart systemd-journald

Option B — cap json-file per container

{
  "data-root": "/mnt/data/docker",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Verification

sudo systemctl status docker
docker info
docker compose ls

Check that Docker Root Dir points to your external drive, the logging driver matches what you configured, and any Compose stacks show as running.

Troubleshooting

  • "exec format error" — You're trying to run an x86-only image. Verify with docker buildx imagetools inspect <image>. Find an arm64/armv7 tag or build your own.
  • Container exits immediately on armv7 — Some images ship a multi-arch manifest but their armv7 layer is broken. Try an explicit arm32v7/<image> variant or upgrade to a 64-bit OS.
  • Docker fails to start after moving data-root — Check that the external drive is mounted before Docker starts: sudo systemctl edit docker and add RequiresMountsFor=/mnt/data under [Unit]. Run sudo systemctl daemon-reload && sudo systemctl restart docker.
  • Slow pulls or timeouts — Pi 3 uses 100 Mbps Ethernet; Pi 4/5 has gigabit. If on Wi-Fi, consider a wired connection for initial image pulls.
  • Permission denied on /var/run/docker.sock — You haven't logged out after adding yourself to the docker group. Run newgrp docker or start a fresh session.
tested on:Ubuntu 24.04Ubuntu 22.04Debian 12

Frequently asked questions

Can I run x86 Docker images on a Raspberry Pi?
Not natively. You can use QEMU emulation via `docker run --platform linux/amd64`, but it's slow and unsupported for production use. Always prefer native arm64 or armv7 images.
Do I need a 64-bit OS, or is 32-bit Raspberry Pi OS fine?
32-bit (armv7) works but image availability is shrinking. Most projects now only publish arm64 builds. Flashing a 64-bit OS is strongly recommended for new setups.
Is a USB flash drive good enough, or do I need an SSD?
A USB 3 flash drive is better than the SD card but still has limited write endurance. A cheap USB 3 SATA SSD is the right answer for anything running 24/7.
Why use journald over json-file for logging?
journald integrates with systemd's built-in rotation, compression, and size caps. It also lets you query all container logs with `journalctl` alongside your system logs in one place.
Does Docker Compose v2 work the same as v1?
Almost identically. The main differences are the `docker compose` command (space, not hyphen) and that the default filename is now `compose.yaml` rather than `docker-compose.yml`. Both filenames are still recognized.

Related guides