Docker Raspberry Pi 5: Complete Beginner Stack with Portainer Guide

Beginner docker stack on raspberry pi 5 with portainer

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 arm64 or linux/arm64 tag. 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.

Docker Raspberry Pi 5 stack diagram showing Portainer container management Docker Engine and application containers

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:

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


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.

Was this helpful?

Yes
No
Thanks for your feedback!