apiVersion: v1
kind: Pod
metadata:
name: pihole
labels:
app: pihole
spec:
restartPolicy: Always
containers:
- name: pihole
image: docker.io/pihole/pihole:latest
ports:
- hostPort: 53
containerPort: 53
- hostPort: 8080
containerPort: 80
env:
- name: TZ
value: Europe/Vienna
- name: WEBPASSWORD
value: changeme
- name: PIHOLE_DNS_
value: 8.8.8.8;8.8.4.4
volumeMounts:
- name: vol-0
mountPath: /etc/pihole
- name: vol-1
mountPath: /etc/dnsmasq.d
volumes:
- name: vol-0
persistentVolumeClaim:
claimName: pihole-etc
- name: vol-1
persistentVolumeClaim:
claimName: pihole-dnsmasq
Prerequisite as root
Install Podman
apt update && apt install -y podman
Save the YAML file
sudo mkdir -p /etc/containers/ # Copy the YAML above to: sudo nano /etc/containers/pihole.yaml
Test the pod (without systemd)
sudo podman play kube /etc/containers/pihole.yaml # Status: sudo podman pod ps && sudo podman ps # Stop: sudo podman play kube --down /etc/containers/pihole.yaml
Create Quadlet .kube file (systemd)
Place it at /etc/containers/systemd/pihole.kube
sudo mkdir -p /etc/containers/systemd/ cat << 'EOF' | sudo tee /etc/containers/systemd/pihole.kube [Unit] Description=pihole Pod [Kube] Yaml=/etc/containers/pihole.yaml [Install] WantedBy=multi-user.target EOF
Enable systemd service (rootful)
sudo systemctl daemon-reload sudo systemctl enable --now pihole-pod.service
Status & Logs
sudo systemctl status pihole-pod.service sudo journalctl -u pihole-pod.service -f sudo podman pod ps sudo podman ps
loginctl enable-linger needed — systemd manages the service/etc/containers/systemd/ (not ~/.config/)Ports < 1024 (e.g. 80, 443)
Rootless cannot open privileged ports. Solution:
sysctl net.ipv4.ip_unprivileged_port_start=80
Make persistent in /etc/sysctl.d/99-podman.conf.
Containers communicate via localhost
All containers in the pod share the same network namespace. Always use localhost, not container names.
# Correct (e.g. app → db): localhost:5432 # Wrong (doesn't work in a pod): db-container:5432
List open ports
Which ports is the running pod listening on?
podman port pihole-pod
Custom DNS for the pod
Set a custom DNS server (e.g. local Pi-hole):
# In YAML under spec.dnsConfig:
spec:
dnsConfig:
nameservers:
- 192.168.1.x
Set volume ownership
Fix permission errors by adjusting UID/GID in the user namespace:
podman unshare chown 1000:1000 /path/to/volume
SELinux volume labels
On SELinux systems (RHEL, Fedora) set the volume suffix:
/host/path:/container/path:Z # private /host/path:/container/path:z # shared
List all volumes
podman volume ls podman volume inspect <volume-name>
Volume backup
Back up data from a named volume:
podman run --rm \ -v <volume-name>:/data:ro \ -v $(pwd):/backup \ busybox tar czf /backup/backup.tar.gz /data
Cleanup
Remove unused images, containers and volumes:
podman system prune -f # containers + images podman image prune -f # untagged images only podman volume prune -f # unused volumes
Automatic image updates (podman-auto-update)
Podman can automatically update images and restart the pod. Enable once:
systemctl --user start podman.socket systemctl --user daemon-reload systemctl --user enable --now podman-auto-update.timer systemctl --user status podman-auto-update.timer
Test without actually updating:
podman auto-update --dry-run
Manual update
Pull a new image version and restart the pod:
podman pull <image>:<tag> podman play kube --replace \ ~/.config/containers/pihole.yaml
Find outdated images
Check local images against the registry:
podman images --filter dangling=false podman pull --all-tags <image>
Shell into a running container
podman exec -it pihole-<container> /bin/sh # or bash: podman exec -it pihole-<container> /bin/bash
Follow live logs
# All containers in the pod: podman pod logs -f pihole-pod # Single container: podman logs -f pihole-<container>
Pod info & resource usage
podman pod inspect pihole-pod podman stats pihole-pod
Restart pod without data loss
podman pod restart pihole-pod # or via systemd: systemctl --user restart pihole-pod.service