AdGuard Home quadlet
A network-wide DNS ad blocker as a rootful systemd service that starts at boot.
- Standalone container
- Rootful: sudo systemctl
- Tested with podman 5.0 and newer
Last updated
See where this fits on the homelab connections map, with every port it uses.
In short: AdGuard Home must listen on port 53, which normal users cannot bind, so this quadlet is rootful: save it in /etc/containers/systemd/ and start it with sudo systemctl start adguardhome.service. It keeps two volumes (work for the query log and statistics, conf for AdGuardHome.yaml) and pins an exact image tag, because a broken DNS update takes the whole network down. Finish the setup wizard on port 3000, then point your router's DHCP DNS setting at the server.
The quadlet files
Every quadlet file below passes podman's quadlet generator and
systemd-analyze verify on podman 5.0 and the current release. Personal values
are placeholders: replace example.com, /srv/... paths and the
time zone with your own.
adguardhome.container
Save as /etc/containers/systemd/adguardhome.container
[Unit]
Description=AdGuard Home DNS ad blocker
[Container]
# Exact tag: DNS is critical, so updates are a deliberate edit, not automatic.
Image=docker.io/adguard/adguardhome:v0.107.79
ContainerName=adguardhome
PublishPort=53:53/tcp
PublishPort=53:53/udp
# First-run setup wizard
PublishPort=3000:3000/tcp
# Dashboard after setup (the wizard's default admin port 80)
PublishPort=8081:80/tcp
Volume=adguardhome-work.volume:/opt/adguardhome/work
Volume=adguardhome-conf.volume:/opt/adguardhome/conf
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=multi-user.target
adguardhome-work.volume
Save as /etc/containers/systemd/adguardhome-work.volume
[Volume]
VolumeName=adguardhome-work
adguardhome-conf.volume
Save as /etc/containers/systemd/adguardhome-conf.volume
[Volume]
VolumeName=adguardhome-conf
Why is it built this way?
Why rootful. DNS listens on port 53, and Linux reserves ports below 1024 for
root. A rootful quadlet in /etc/containers/systemd/ can publish port 53, runs under
the system manager with sudo systemctl, and starts at boot through
WantedBy=multi-user.target, before anyone logs in. That matters for the one service
every other device depends on.
Two volumes. AdGuard Home keeps its settings in /opt/adguardhome/conf
and its query log, statistics and filter lists in /opt/adguardhome/work. Each gets
its own .volume. Mounting one volume on both paths mixes the two folders, and a
reset of the statistics then risks the configuration too.
An exact tag, no auto-update. adguardhome:v0.107.79 is an exact
release, and the file has no AutoUpdate= line. When a DNS update goes wrong, every
device on the network loses name resolution at once, so updates here are a deliberate edit
followed by a quick test.
Ports. Port 53 over TCP and UDP answers DNS queries, 3000 serves the first-run
setup wizard, and host port 8081 maps to the dashboard on container port 80 after setup. Add
PublishPort=853:853/tcp if you enable DNS-over-TLS. DHCP is left out on purpose: it
needs Network=host and extra capabilities, and most routers already do it well.
The keys are documented in podman-systemd.unit(5), the container settings in the AdGuard Home Docker wiki.
How do I start it?
Run these with sudo. Quadlet reads /etc/containers/systemd/ for system services.
sudo mkdir -p /etc/containers/systemd
# save the three files there, then:
sudo systemctl daemon-reload
sudo systemctl start adguardhome.service
# firewalld: allow DNS, the setup wizard and the dashboard
sudo firewall-cmd --permanent --add-service=dns --add-port=3000/tcp --add-port=8081/tcp
sudo firewall-cmd --reload
# then open http://<server>:3000 and keep the admin port at 80 in the wizard
Check the result with sudo systemctl status and
the logs with sudo journalctl -u followed by the
service name. To turn a docker-compose file of your own into files like these, use the
compose to quadlet converter.
Common mistakes
Error: address already in use on port 53
Another DNS service already listens on port 53. Find it with sudo ss -lntup 'sport = :53'. On systems with systemd-resolved, AdGuard's own documentation recommends DNSStubListener=no in a drop-in under /etc/systemd/resolved.conf.d/, then restarting systemd-resolved.
The dashboard is unreachable after the setup wizard
After the wizard AdGuard Home serves its dashboard on the admin port you picked inside the container. This quadlet publishes container port 80 on host port 8081, so keep the wizard's default of 80. If you chose another port, change PublishPort= to match.
Devices on the network still see ads
They still use the old DNS server. Set the server's LAN address as the DNS server in your router's DHCP settings and renew the leases, or set it by hand on one device to test with nslookup example.com <server>.
Frequently asked questions
Why does AdGuard Home need a rootful podman container?
DNS uses port 53, and ports below 1024 are privileged, so rootless podman cannot publish them by default. A rootful quadlet in /etc/containers/systemd/ also starts at boot under the system manager (WantedBy=multi-user.target) without a user session or lingering.
Can AdGuard Home run as a rootless container?
Yes, if you lower the privileged port limit with net.ipv4.ip_unprivileged_port_start=53 in a file under /etc/sysctl.d/. That setting applies to every user on the host, so weigh it against running one rootful container.
How do I update AdGuard Home in a quadlet?
Change the tag in Image= to the new release, then run sudo systemctl daemon-reload and sudo systemctl restart adguardhome.service. The volumes keep your settings. To roll back, put the old tag back and restart.
Can the AdGuard Home container also be the DHCP server?
Only with Network=host and the NET_ADMIN and NET_RAW capabilities, because DHCP works with broadcasts on the physical network that a bridged container never sees. Most setups keep DHCP on the router and only change the DNS server it hands out.