chalkline

Public teaching whiteboard

Docker on Raspberry Pi: Portainer & Pi-hole

Run Portainer and Pi-hole with Docker on a Raspberry Pi — public tutorial whiteboard. · by zlu

Topics: Raspberry Pi tutorials

Docker on Raspberry Pi: Portainer & Pi-hole

Session 05 · Docker Compose: real services

Install Docker, run Portainer, then deploy Pi‑hole with a complete compose file. Includes update/backup commands you will actually reuse.

Prereq

Session 04 style host ready (Pi 4/5, 64‑bit OS, preferably Ethernet + SSD at /srv/data). You can follow on a Pi that only has an SD card — just know SD cards wear faster.

Step 1 — Install Docker Engine + Compose plugin

curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # CRITICAL: log out of SSH and back in (or reboot) so the group applies exit ssh piuser@homeserver.local docker version docker compose version docker run --rm hello-world

Step 2 — Portainer (Docker web UI)

mkdir -p /srv/data/docker/portainer 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 sudo ufw allow 9443/tcp comment 'portainer' # Browser: https://homeserver.local:9443 # First visit: create the admin password

Step 3 — Pi‑hole compose file

mkdir -p /srv/data/docker/pihole && cd /srv/data/docker/pihole nano docker-compose.yml services: pihole: image: pihole/pihole:latest container_name: pihole hostname: pihole environment: TZ: 'America/Los_Angeles' # change to your timezone WEBPASSWORD: 'change-me-now' FTLCONF_LOCAL_IPV4: '192.168.1.50' # your Pi LAN IP ports: - '53:53/tcp' - '53:53/udp' - '8080:80/tcp' volumes: - './etc-pihole:/etc/pihole' - './etc-dnsmasq.d:/etc/dnsmasq.d' cap_add: - NET_ADMIN restart: unless-stopped

Step 4 — Launch & verify Pi‑hole

cd /srv/data/docker/pihole # if something else uses port 53 on the Pi (systemd-resolved), stop conflict first: sudo ss -ulpn | grep ':53' || true docker compose pull docker compose up -d docker compose ps docker compose logs -f --tail=100 # Ctrl+C leaves the container running sudo ufw allow 53 comment 'dns' sudo ufw allow 8080/tcp comment 'pihole-web' Admin UI: http://homeserver.local:8080/admin On your router (or phone Wi‑Fi DNS setting), set DNS server = Pi IP. Test: dig +short google.com @192.168.1.50

Step 5 — Day‑2 operations

Update one stack: cd /srv/data/docker/pihole docker compose pull docker compose up -d Stop / start: docker compose stop docker compose start Nuke container but keep config volumes: docker compose down Backup config: tar czf ~/pihole-backup.tgz -C /srv/data/docker/pihole .

Optional next containers

Create a folder per app under /srv/data/docker/: • jellyfin — media (map /srv/data/media) • homeassistant — smart home • nextcloud — files (needs solid SSD) • vaultwarden — passwords • wireguard — remote VPN into LAN Pattern is always the same: compose.yml → up -d → ufw allow port → bookmark UI.

Common failures

• Permission denied talking to docker → you didn’t re‑login after usermod • Port 53 in use → disable other DNS on the Pi, or use network_mode tricks carefully • Image won’t pull → wrong arch; ensure 64‑bit OS; try docker pull <image> alone • UI not loading → check ufw + docker compose ps + docker compose logs • DNS breaks whole house → set a secondary DNS (1.1.1.1) on router while debugging

Checklist

☐ hello-world ran ☐ Portainer opens on :9443 ☐ Pi‑hole admin opens on :8080/admin ☐ dig against the Pi returns answers ☐ You know how to pull + up -d to update Next: Session 06 (camera) or 07 (ML) — both can later run inside Compose too.

Drag to pan · scroll to zoom · read-only

Board contents

Text extracted from this public whiteboard for search and accessibility.

Docker on Raspberry Pi: Portainer & Pi-hole

Session 05 · Docker Compose: real services

Install Docker, run Portainer, then deploy Pi‑hole with a complete compose file. Includes update/backup commands you will actually reuse.

Prereq

Session 04 style host ready (Pi 4/5, 64‑bit OS, preferably Ethernet + SSD at /srv/data). You can follow on a Pi that only has an SD card — just know SD cards wear faster.

Step 1 — Install Docker Engine + Compose plugin

curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # CRITICAL: log out of SSH and back in (or reboot) so the group applies exit ssh piuser@homeserver.local docker version docker compose version docker run --rm hello-world

Step 2 — Portainer (Docker web UI)

mkdir -p /srv/data/docker/portainer 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 sudo ufw allow 9443/tcp comment 'portainer' # Browser: https://homeserver.local:9443 # First visit: create the admin password

Step 3 — Pi‑hole compose file

mkdir -p /srv/data/docker/pihole && cd /srv/data/docker/pihole nano docker-compose.yml services: pihole: image: pihole/pihole:latest container_name: pihole hostname: pihole environment: TZ: 'America/Los_Angeles' # change to your timezone WEBPASSWORD: 'change-me-now' FTLCONF_LOCAL_IPV4: '192.168.1.50' # your Pi LAN IP ports: - '53:53/tcp' - '53:53/udp' - '8080:80/tcp' volumes: - './etc-pihole:/etc/pihole' - './etc-dnsmasq.d:/etc/dnsmasq.d' cap_add: - NET_ADMIN restart: unless-stopped

Step 4 — Launch & verify Pi‑hole

cd /srv/data/docker/pihole # if something else uses port 53 on the Pi (systemd-resolved), stop conflict first: sudo ss -ulpn | grep ':53' || true docker compose pull docker compose up -d docker compose ps docker compose logs -f --tail=100 # Ctrl+C leaves the container running sudo ufw allow 53 comment 'dns' sudo ufw allow 8080/tcp comment 'pihole-web' Admin UI: http://homeserver.local:8080/admin On your router (or phone Wi‑Fi DNS setting), set DNS server = Pi IP. Test: dig +short google.com @192.168.1.50

Step 5 — Day‑2 operations

Update one stack: cd /srv/data/docker/pihole docker compose pull docker compose up -d Stop / start: docker compose stop docker compose start Nuke container but keep config volumes: docker compose down Backup config: tar czf ~/pihole-backup.tgz -C /srv/data/docker/pihole .

Optional next containers

Create a folder per app under /srv/data/docker/: • jellyfin — media (map /srv/data/media) • homeassistant — smart home • nextcloud — files (needs solid SSD) • vaultwarden — passwords • wireguard — remote VPN into LAN Pattern is always the same: compose.yml → up -d → ufw allow port → bookmark UI.

Common failures

• Permission denied talking to docker → you didn’t re‑login after usermod • Port 53 in use → disable other DNS on the Pi, or use network_mode tricks carefully • Image won’t pull → wrong arch; ensure 64‑bit OS; try docker pull <image> alone • UI not loading → check ufw + docker compose ps + docker compose logs • DNS breaks whole house → set a secondary DNS (1.1.1.1) on router while debugging

Checklist

☐ hello-world ran ☐ Portainer opens on :9443 ☐ Pi‑hole admin opens on :8080/admin ☐ dig against the Pi returns answers ☐ You know how to pull + up -d to update Next: Session 06 (camera) or 07 (ML) — both can later run inside Compose too.