[Unit]
Description=Runbound DNS Server
Documentation=https://github.com/runbound/runbound
After=network-online.target
Wants=network-online.target
# Restart up to 5 times within 5 minutes before giving up
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=simple
ExecStart=/usr/local/sbin/runbound /etc/runbound/unbound.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
# User / group created by install.sh (useradd -r runbound)
User=runbound
Group=runbound
# API key persistence — set here to survive restarts without regeneration.
# Leave commented to auto-generate on first start (saved to /etc/runbound/api.key).
# EnvironmentFile=-/etc/runbound/environment
Environment="RUST_LOG=runbound=info"
WorkingDirectory=/var/lib/runbound
# ── Systemd hardening (defence-in-depth) ────────────────────────────────────
# No privilege escalation from runbound user
NoNewPrivileges=true
# Private /tmp and /dev — the server has no reason to touch shared temp files
PrivateTmp=true
PrivateDevices=true
# Filesystem: read-only except for the two directories we actually need
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/etc/runbound /var/lib/runbound
# Kernel hardening
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
# Namespace isolation
RestrictNamespaces=true
LockPersonality=true
# Syscall filter — DNS server only needs inet sockets and file I/O
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
# No memory regions executable + writable simultaneously
MemoryDenyWriteExecute=true
# No realtime scheduling (unnecessary, prevents priority inversion attacks)
RestrictRealtime=true
RestrictSUIDSGID=true
# Clean up IPC objects on exit
RemoveIPC=true
# ── Capabilities ─────────────────────────────────────────────────────────────
# CAP_NET_BIND_SERVICE: bind to port 53 (< 1024) as non-root
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
# ── Resource limits ──────────────────────────────────────────────────────────
# 512 MB hard cap — the memory guard already purges caches at 80 % system RAM,
# but this systemd limit is a belt-and-suspenders last line of defence.
MemoryMax=512M
# File descriptors: 32 SO_REUSEPORT UDP sockets + TCP + DoT + feeds HTTP
LimitNOFILE=65536
# Tokio thread pool + hickory tasks
TasksMax=4096
[Install]
WantedBy=multi-user.target