Here’s how to schedule your AdGuard Home container to run automatically on boot using a Quadlet file—no manual enabling required. These steps assume you’re on Fedora CoreOS as of December 2025, with a recent Podman version.
If CoreOS is not installed yet, use Beginner’s Guide to Fedora CoreOS or the 2026 Bitwarden + Ignition + AdGuard guide (that one bakes a Quadlet into Butane). This post is the after-boot version: nano a .container file, nftables, auto-update timer. Pi-hole instead of AdGuard: Pi-hole Quadlet. Do not run both resolvers on the same IP and port 53.
Why AdGuard Home on CoreOS?
Same appliance story as Pi-hole: one job, atomic host, containers as systemd. AdGuard Home’s first-run wizard lives on port 3000, then the UI typically moves to 80. It wants host networking in this unit so DNS on port 53 is not DNAT-spaghetti. That also means the container sees the host’s network stack — which is why the nftables section matters.
Prerequisites
- Fedora CoreOS, SSH as
core. - Static IP (examples use a LAN
192.168.1.0/24). - Nothing else bound to 53, 80, or 3000.
systemd-resolvedstub on 127.0.0.53 is OK; a host bind on*:53is not. - Timezone string you like (
America/Chicagoin the sample).
sudo ss -tulpn | grep -E ':53|:80|:3000|:22'
sudo podman ps -a
Remove leftover AdGuard/Pi-hole containers before you start.
Step 1: Create the Quadlet file
Fedora CoreOS uses /etc/containers/systemd/ for Quadlet files, which define containers as systemd services. Let’s create adguardhome.container:
sudo nano /etc/containers/systemd/adguardhome.container
Paste this (modify the network info with yours; AdGuard Home has a setup wizard for passwords and other configs):
[Unit]
Description=AdGuard Home Container
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=adguardhome
Image=docker.io/adguard/adguardhome:latest
AutoUpdate=registry
Network=host
AddCapability=NET_ADMIN
Environment=TZ=America/Chicago
Volume=adguardhome_work:/opt/adguardhome/work:Z
Volume=adguardhome_conf:/opt/adguardhome/conf:Z
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=multi-user.target
Save and exit (Ctrl+O, Enter, Ctrl+X in nano).
Why these keys
Network=host: AdGuard binds DNS and the web UI on the host’s addresses. Published ports are the wrong model here.AutoUpdate=registry: pairs withpodman-auto-updateso the image can roll forward without you SSH’ing topodman pull.TimeoutStartSec=900: first image pull.:Zon volumes: SELinux on FCOS.- No password env vars: unlike Pi-hole v6, the wizard owns the admin account. That is easier to screenshot and easier to forget — set a real password at first login.
Pin a tag instead of :latest once you like a version. Auto-update plus :latest is convenient and also how surprises arrive; I still show it because that is the homelab default.
Optional volume create:
sudo podman volume create adguardhome_work
sudo podman volume create adguardhome_conf
Step 2: Reload systemd
Tell systemd to process the Quadlet file with Podman’s quadlet generator:
sudo systemctl daemon-reload
This generates a transient adguardhome.service in /run/systemd/generator/.
systemctl cat adguardhome.service
If cat fails, the file name or directory is wrong (*.container under /etc/containers/systemd/).
Step 3: Start the container
Kick it off manually the first time:
sudo systemctl start adguardhome.service
Follow logs:
sudo journalctl -u adguardhome.service -f
Check it’s running:
sudo podman ps -a
sudo systemctl status adguardhome.service
You should see your adguardhome container up and humming.
Enable AUTO-UPDATE:
sudo systemctl enable --now podman-auto-update.timer
systemctl list-timers podman-auto-update.timer
Expected: a timer with a next run. Auto-update only restarts units that opted in with AutoUpdate=registry. A Quadlet without that key will not update. After an update, glance at sudo podman ps so you are not surprised by a new major image.
Open the wizard before you put nftables in front, or you may lock yourself out of port 3000:
http://YOUR_COREOS_IP:3000
Complete admin user, upstream DNS (I use 1.1.1.1 / 1.0.0.1 as a starting point, not as a religion), and listen addresses. After the wizard, the UI is usually http://YOUR_COREOS_IP/ on port 80. Confirm with sudo ss -tulpn | grep -E ':80|:3000|:53'.
Step 4: Configure nftables firewall
For security, we’ll set up a lightweight nftables firewall to control access to AdGuard Home and other services on your Fedora CoreOS host. CoreOS may not ship firewalld; nftables is the right small tool. If you already layered firewalld for the beginner Pi-hole guide, do not run both with competing default-drop policies. Pick one.
Create the firewall configuration
sudo nano /etc/nftables.conf
Add the following rules (adjust 192.168.1.0/24 to match your LAN subnet):
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Allow established connections
ct state established,related accept
# Allow loopback
iif lo accept
# Allow DNS from LAN only
ip saddr 192.168.1.0/24 udp dport 53 accept
ip saddr 192.168.1.0/24 tcp dport 53 accept
# Allow AdGuard web interface from LAN
ip saddr 192.168.1.0/24 tcp dport 80 accept
# Allow SSH from LAN only
ip saddr 192.168.1.0/24 tcp dport 22 accept
# Allow ICMP ping
icmp type echo-request accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Save and exit (Ctrl+X, Y, Enter in nano — or Ctrl+O / Ctrl+X if that is your muscle memory).
Why default drop on input? An AdGuard box with host networking is a DNS server and a web UI on the same IP. You do not want port 80 or 53 on the WAN. If this host has a public address, this ruleset is incomplete (no mention of 3000, IPv6, or your VPN interface). Add tcp dport 3000 from LAN if you still need the wizard, or finish the wizard before applying drop.
IPv6: this sample only matches ip saddr (IPv4). If your LAN is v6-heavy, add ip6 saddr analogues or you will think AdGuard is “broken” for v6 clients.
Test and apply the configuration
First, test the configuration for syntax errors:
sudo nft -c -f /etc/nftables.conf
If no errors appear, apply the rules:
sudo nft -f /etc/nftables.conf
Keep an SSH session open and test a second SSH from another terminal before you disconnect. A typo in the SSH rule plus default drop is an unscheduled drive to the homelab.
Enable firewall on boot
sudo systemctl enable nftables
sudo systemctl start nftables
On some FCOS builds you may need to confirm the unit name (systemctl list-unit-files | grep nft). The important part is that /etc/nftables.conf is loaded on boot so a reboot does not leave DNS wide open or, worse, fail closed after you already applied drop once.
Verify the configuration
sudo nft list ruleset
Test DNS from another device on your network:
nslookup google.com YOUR_COREOS_IP
dig @YOUR_COREOS_IP example.com
Your AdGuard Home installation is now secured with a firewall that only allows access from your local network while still permitting system updates and outbound connections (output policy accept).
If nslookup times out after nftables but worked before, your client is not in 192.168.1.0/24 (guest Wi-Fi, VPN, different VLAN). Widen the prefix on purpose or do not use guest isolation with this ruleset.
Step 5: Test autostart on reboot
The WantedBy=multi-user.target line ensures AdGuard Home starts on boot. Test it:
sudo reboot
After reboot, verify:
sudo podman ps -a
sudo systemctl status adguardhome.service nftables
sudo nft list ruleset | head
If adguardhome is running, you’re set—no systemctl enable on the container unit, as Quadlets handle autostart dynamically. nftables should still show the LAN-only DNS rules.
Point DHCP at this IP only after reboot + dig succeed. A nightly host reboot (optional) is CoreOS nightly reboots — schedule it when nobody needs DNS.
Notes
- Why no enable? Generated services in
/run/are transient and can’t be enabled traditionally. The[Install]section ties it to the boot process instead. - Tweaks: Older Quadlet guides mention
HostnameorRestartPolicy, but these are unsupported now—stick to the basics above. - Hostname: Defaults to
adguardhomefromContainerName. If you needadguard-home, you’d need a custom workaround or a classic systemd service. - Initial setup: After starting, access the AdGuard Home web interface at http://your-server-ip:3000 to complete the initial configuration wizard. Set your admin password, upstream DNS servers (e.g., 1.1.1.1), and any other preferences there. Unlike Pi-hole, many settings are handled via the UI rather than environment variables. After the initial setup, your port will change to http://your-server-ip:80.
- Volumes: The persistent volumes (
adguardhome_workandadguardhome_conf) store your configuration and data. Ensure Podman volumes are created if needed (they’ll be auto-created on first run). Back those volumes up if the box is the only DNS for the house. - Port 3000 vs nftables: the sample nftables does not open 3000. Finish the wizard first, or add a LAN-only 3000 rule.
- Encryption / HTTPS on the UI: do that in AdGuard’s settings behind the LAN firewall; do not expose 80 to the internet “just for a cert.”
Troubleshooting
- Wizard never loads: container not running, or you applied nftables too early.
journalctl -u adguardhome.service,ss -tulpn. - DNS works, UI does not: browser is still on 3000 after the wizard moved to 80, or port 80 blocked.
- Auto-update “broke DNS at 4 AM”: pin an image digest/tag, disable the timer, or watch
podman auto-update --dry-run. - Conflict with systemd-resolved: if the host insists on 53, follow the resolved stub disable pattern in the 2026 CoreOS Quadlet guide.
- SELinux volume denials: you dropped
:Z. Put it back.
That’s it! Your AdGuard Home is now a proper Fedora CoreOS citizen, managed via Podman Quadlets. Enjoy ad-free browsing.
Best regards,
The Linux IT Guy