Docker Raspberry Pi 5 gives you a container runtime on the Pi’s ARM64 architecture, letting you run self-hosted applications as isolated containers that start, stop, and update independently. Portainer provides a web-based management interface so you can deploy and monitor containers without typing every command at a terminal. This guide covers OS preparation, Docker CE installation, Portainer setup, your first Docker Compose stack, storage best practices, and maintenance.
Last tested: Raspberry Pi OS Bookworm Lite 64-bit | May 3, 2026 | Raspberry Pi 5 (8GB) | Docker 27.3 | Portainer CE 2.21 | Compose v2
Key Takeaways
- Not all Docker images support ARM64. Before pulling an image, check Docker Hub for an
arm64orlinux/arm64tag. Images built for x86 only will fail with “exec format error” on the Pi 5. Most major projects publish multi-arch images that work without any special flags. - Run workloads from an SSD, not a microSD card. Docker writes layer caches, volumes, and logs continuously. MicroSD cards degrade quickly under this write pattern and produce slow container start times and visible I/O latency. A USB 3.0 SSD or NVMe via PCIe HAT is the minimum for a stable long-running stack.
- Portainer CE is optional. Everything Portainer does is also achievable from the command line. Portainer adds a visual interface for browsing containers, reading logs, and deploying stacks from a web form. It is useful while learning and less necessary once you are comfortable with
docker compose.
Docker Raspberry Pi 5: Why It Works Well
The Pi 5 runs a 64-bit ARM processor (ARM Cortex-A76) with enough performance to run multiple lightweight containers simultaneously. Most self-hosted applications publish ARM64-compatible images: Nextcloud, Home Assistant, Grafana, Node-RED, Pi-hole, and hundreds of others. The Pi 5 handles these well, particularly for read-heavy workloads like media serving, dashboards, and home automation. Compute-intensive workloads like video transcoding or machine learning inference need additional hardware acceleration.
Docker containers share the host OS kernel, which means they are more lightweight than virtual machines. A Pi 5 with 8GB RAM can run 10 or more modest containers simultaneously without memory pressure. Each container is isolated from others, so a misconfigured container does not affect the host or other containers.
Hardware and OS Preparation
Pi 5 with 4GB or 8GB RAM both work. The 4GB model handles a small stack (5-7 containers) without issue. The 8GB model gives comfortable headroom for larger stacks or running Docker alongside Home Assistant or other memory-intensive services. Active cooling is recommended. Docker build operations and container startup bursts generate short CPU spikes that push temperatures higher than idle. See Raspberry Pi 5 Cooling Guide for tested options.
Mount an SSD as the primary storage. See Booting Raspberry Pi from USB SSD for the setup. Flash Raspberry Pi OS Bookworm Lite 64-bit using Raspberry Pi Imager. In the advanced settings, set hostname, enable SSH, and configure credentials.
After first boot, update the system and set a static IP so services are always reachable at the same address:
sudo apt update && sudo apt full-upgrade -y
sudo nmcli connection modify "Wired connection 1" ipv4.method manual ipv4.addresses 192.168.1.100/24 ipv4.gateway 192.168.1.1 ipv4.dns 192.168.1.1
sudo nmcli connection up "Wired connection 1"
Installing Docker CE and Compose
Install Docker using the official install script. This sets up the Docker CE APT repository with the current keyring method and installs the latest stable release:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
sudo apt install docker-compose-plugin -y
Log out and back in so the Docker group membership takes effect. Verify the installation:
docker --version
docker compose version
docker run hello-world
Expected result: docker --version returns the installed version. docker run hello-world pulls the hello-world image and prints a confirmation message. If you see “permission denied,” log out and back in to pick up the docker group membership.
Configure Docker to use the SSD
By default Docker stores images, containers, and volumes under /var/lib/docker on the OS drive. If that is an SSD, this is fine. If the OS is on microSD and you want Docker data on a separate SSD, create a Docker daemon configuration file:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<EOF
{
"data-root": "/mnt/ssd/docker"
}
EOF
sudo mkdir -p /mnt/ssd/docker
sudo systemctl restart docker
Installing Portainer CE
Portainer CE is itself a Docker container. Create a persistent volume for its data and run the container:
docker volume create portainer_data
docker run -d --name portainer --restart unless-stopped -p 9443:9443 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
Navigate to https://<pi-ip>:9443 in a browser. Accept the self-signed certificate warning and create an admin account. Select the local Docker environment when prompted.
Expected result: The Portainer dashboard loads showing the local Docker environment with the running containers visible. The Portainer container itself appears in the container list.

Running Your First Docker Compose Stack
Docker Compose manages multi-container applications defined in a single compose.yaml file. Create a project directory and an .env file for credentials:
mkdir -p ~/stacks/pihole && cd ~/stacks/pihole
cat > .env <<EOF
PIHOLE_PASSWORD=change_this_password
TZ=America/Chicago
EOF
chmod 600 .env
Create compose.yaml:
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
env_file: .env
environment:
WEBPASSWORD: ${PIHOLE_PASSWORD}
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
ports:
- "53:53/tcp"
- "53:53/udp"
- "127.0.0.1:8080:80"
restart: unless-stopped
docker compose up -d
docker compose logs -f pihole
Expected result: Pi-hole starts and the log shows it listening. Navigate to http://<pi-ip>:8080/admin and the Pi-hole dashboard loads. Log in with the password set in .env.
Useful containers to add next
Each of the following has a dedicated article on this site with a full production-grade compose setup:
- Node-RED: visual flow automation for IoT and home automation. See Node-RED Raspberry Pi.
- Grafana + InfluxDB: monitoring stack for Pi system metrics and sensor data. See Grafana InfluxDB Raspberry Pi.
- Home Assistant: smart home automation platform. See Home Assistant Raspberry Pi 5.
- Nextcloud AIO: self-hosted file sync and collaboration. See Nextcloud AIO Raspberry Pi 5.
Storage, Networking, and Security Basics
Volumes and bind mounts
Docker data persists in named volumes (managed by Docker) or bind mounts (a specific path on the host). Named volumes are easier to back up and migrate. Bind mounts give direct access to files from the host. For configuration files you want to edit directly, use bind mounts. For databases and other opaque data stores, use named volumes.
# List named volumes
docker volume ls
# Inspect a volume to see its mount point
docker volume inspect portainer_data
# Remove unused volumes
docker volume prune
Port binding security
Bind service ports to 127.0.0.1 for services that should only be accessible locally, or use a reverse proxy. Binding to 0.0.0.0 exposes the port to every interface including the LAN. For services behind Caddy, bind to 127.0.0.1:port:containerport and let Caddy handle external access. See Caddy Reverse Proxy Raspberry Pi.
UFW firewall
Docker bypasses UFW rules by default by writing directly to iptables. If you enable UFW expecting it to restrict container traffic, it will not work as expected. The simplest approach is to use 127.0.0.1 port bindings for internal services and only expose ports 80 and 443 through a reverse proxy.
Maintaining Your Docker Stack
Updating containers
# Update all containers in a stack
cd ~/stacks/pihole
docker compose pull
docker compose up -d
docker image prune -f
Pull new images before restarting. docker compose up -d recreates containers only if their image changed. Run docker image prune -f after updates to remove stale layers and reclaim disk space.
Updating Portainer
docker pull portainer/portainer-ce:latest
docker stop portainer
docker rm portainer
docker run -d --name portainer --restart unless-stopped -p 9443:9443 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
Disk usage and cleanup
# Check Docker disk usage
docker system df
# Check available disk space
df -h /var/lib/docker
# Remove stopped containers, unused networks, dangling images
docker system prune
# Also remove unused volumes (warning: removes all unused named volumes)
docker system prune --volumes
Monitoring resources
# Per-container CPU and RAM usage
docker stats
# Pi CPU temperature
vcgencmd measure_temp
# System-wide resource view
htop
Troubleshooting
exec format error
This error means the container image was built for x86 and does not run on ARM64. Check Docker Hub for an ARM64-compatible image tag. Most major projects publish multi-arch images tagged latest that include ARM64. If only an x86 image exists, there is no workaround other than finding an alternative image or building your own from source.
Permission denied on volumes
# Check container user
docker exec <container-name> id
# Fix ownership to match container UID (replace 1000 with actual UID)
sudo chown -R 1000:1000 /path/to/bind/mount
Always check the actual UID the container runs as before changing ownership. Do not blindly chown 1000. The container may use a different UID.
Container keeps restarting
# Check container exit code and recent logs
docker logs --tail 50 <container-name>
# Check restart history
docker inspect <container-name> | grep -i restart
The exit code in the log tells you the failure type. Exit code 1 is an application error: check the application config. Exit code 137 is OOM kill: the container ran out of memory. Exit code 127 is a missing binary: the image may be corrupt or the wrong architecture.
YAML errors in compose files
# Validate compose file syntax before running
docker compose config
docker compose config parses the compose file and reports syntax errors with line numbers. YAML is whitespace-sensitive. Use two-space indentation consistently and never use tabs. The most common mistakes are misaligned keys, missing colons, and unquoted special characters in values.
FAQ
Can I run x86 Docker images on the Pi 5?
Not natively. The Pi 5 runs ARM64 and x86 images are incompatible without emulation. Docker Desktop on x86 machines can emulate ARM for development purposes, but the reverse (running x86 on ARM) requires QEMU emulation which is slow and unreliable for production use. For any service you want to run on Pi, check that an ARM64 image is available before committing to it.
Should I use Portainer or manage Docker from the command line?
Start with Portainer if you are new to Docker. The visual log viewer, container inspector, and stack deployment form reduce the learning curve significantly. Once you are comfortable with docker compose up, docker logs, and docker stats, you will find the command line faster for most operations. The two are not mutually exclusive. Portainer is useful for at-a-glance monitoring even when you manage deployments from the CLI.
How many containers can a Pi 5 run?
There is no fixed limit. The practical ceiling depends on the memory footprint of each container and the available RAM. A Pi 5 with 8GB RAM comfortably runs 15 to 20 modest containers (DNS resolver, ad blocker, monitoring stack, a few self-hosted apps) without memory pressure. Containers with large in-memory databases or active transcoding are more constrained. Use docker stats to monitor actual memory usage rather than guessing.
What is the difference between Docker CE and Docker Desktop?
Docker CE (Community Edition) is the open-source Docker engine that runs on Linux servers and is what this guide installs. Docker Desktop is a GUI application for Windows and macOS that bundles Docker CE with a Linux VM and a graphical interface. On the Pi you use Docker CE directly. Docker Desktop is not available for ARM Linux.
Is it safe to expose Portainer to the internet?
Not without additional protection. Portainer has access to the Docker socket, which means anyone who can authenticate to Portainer can run arbitrary containers with root privileges on the host. If you need remote access to Portainer, put it behind a reverse proxy with strong authentication, or use Tailscale to access it over an encrypted tunnel without any public exposure. See Tailscale Raspberry Pi.
References
- https://docs.docker.com/engine/install/debian/
- https://docs.portainer.io/start/install/server/docker/linux
- https://docs.docker.com/compose/compose-file/
- https://hub.docker.com/r/portainer/portainer-ce
About the Author
Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.
Last tested hardware: Raspberry Pi 5 (8GB), USB 3.0 SSD. Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit. Docker 27.3, Portainer CE 2.21, Compose v2.

