How to Make systemd Services Wait for DNS Resolution Before Starting (Nginx/NTPd Examples)


3 views

When services like Nginx or NTPd attempt to start before DNS resolution is fully operational, you'll encounter errors like:

Aug 09 22:35:25 host.blah ntpd[3574]: restrict: ignoring line 21, address/host 'ntp.blah' unusable.
Aug 09 22:35:26 host.blah ntpd[3574]: restrict: ignoring line 23, address/host 'ntp.blah' unusable.

The common but incorrect approach is using WantedBy=pdns-recursor.service, which doesn't actually guarantee DNS readiness.

Instead of WantedBy, we need to use After= and Requires= directives in the [Unit] section. Here's a proper ntp.service example:

[Unit]
Description=Network Time Service
After=network.target pdns-recursor.service
Requires=pdns-recursor.service
Wants=network.target

[Service]
Type=simple
ExecStart=/usr/sbin/ntpd -n -u ntp:ntp -g
Restart=on-failure

[Install]
WantedBy=multi-user.target

For more complex cases, create a dedicated target that only activates after DNS verification:

# /etc/systemd/system/dns-ready.target
[Unit]
Description=DNS Resolution Available
After=pdns-recursor.service
Requires=pdns-recursor.service

# Verification script
OnFailure=network-online.target

Then modify your services to depend on this target:

[Unit]
After=dns-ready.target
Requires=dns-ready.target

For bulletproof startup, add a verification script that checks DNS resolution:

[Service]
ExecStartPre=/bin/bash -c 'until host example.com; do sleep 1; done'
ExecStart=/usr/sbin/nginx -g "daemon off;"

This will continuously attempt to resolve 'example.com' until successful.

For systems using systemd-networkd, leverage native network status tracking:

[Unit]
After=network-online.target
Wants=network-online.target

Enable network wait:

systemctl enable systemd-networkd-wait-online.service

When dealing with services like Nginx or NTPd that require functional DNS resolution, we often encounter startup failures during boot. The core issue stems from systemd starting these services before DNS becomes fully operational, especially when using local DNS resolvers like pdns-recursor.

The common solution of using WantedBy=pdns-recursor.service in unit files often proves insufficient because:

[Unit]
WantedBy=pdns-recursor.service

This only establishes a weak dependency and doesn't guarantee DNS resolution capability at service startup.

Instead of WantedBy, we should use After and Requires directives combined with a proper test for DNS functionality:

[Unit]
After=network-online.target pdns-recursor.service
Requires=network-online.target pdns-recursor.service

For more robust verification, we can create an ExecStartPre check:

[Service]
ExecStartPre=/bin/sh -c 'until host example.com; do sleep 1; done'

Or for a more sophisticated check:

[Service]
ExecStartPre=/usr/bin/systemd-run --property=After=network-online.target --property=Requires=network-online.target --property=Requires=pdns-recursor.service --wait /bin/true

Here's a complete working example for ntpd:

[Unit]
Description=Network Time Service
After=network-online.target pdns-recursor.service
Requires=network-online.target pdns-recursor.service

[Service]
Type=simple
ExecStartPre=/bin/sh -c 'until host pool.ntp.org; do echo "Waiting for DNS..."; sleep 1; done'
ExecStart=/usr/sbin/ntpd -n -u ntp:ntp -g
Restart=on-failure

[Install]
WantedBy=multi-user.target

For more complex scenarios, consider using path units to monitor DNS readiness:

[Unit]
Description=Monitor DNS resolution

[Path]
PathExists=/run/dns-ready
Unit=dns-ready.target

[Install]
WantedBy=multi-user.target

Then have your resolver create the trigger file when ready.

To verify your configuration works, use:

systemd-analyze verify ntp.service
systemd-analyze critical-chain ntp.service
journalctl -u ntp.service -b