Skip to content

Systemd Service

Run Dwaar as a managed systemd service on Linux. Systemd handles process supervision, automatic restarts, journal logging, and capability grants — so Dwaar can bind ports 80 and 443 without running as root.

Terminal window
# Copy the unit file
sudo cp /usr/share/dwaar/dwaar.service /etc/systemd/system/dwaar.service
# Reload unit definitions
sudo systemctl daemon-reload
# Enable on boot and start now
sudo systemctl enable --now dwaar
# Confirm it is running
sudo systemctl status dwaar
[Unit]
Description=Dwaar reverse proxy
Documentation=https://dwaar.dev/docs
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/dwaar --config /etc/dwaar/Dwaarfile
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStartSec=30s
TimeoutStopSec=60s
# Run as a dedicated non-root user
User=dwaar
Group=dwaar
# Working directory and environment
WorkingDirectory=/etc/dwaar
EnvironmentFile=-/etc/dwaar/dwaar.env
# Grant permission to bind privileged ports without root
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# PID file (written by Pingora when --daemon is used)
PIDFile=/run/dwaar/dwaar.pid
RuntimeDirectory=dwaar
RuntimeDirectoryMode=0750
[Install]
WantedBy=multi-user.target

Save this to /etc/systemd/system/dwaar.service then run systemctl daemon-reload.

Dwaar binds ports 80 and 443 by default. On Linux, binding ports below 1024 requires either root or CAP_NET_BIND_SERVICE.

SettingValueEffect
AmbientCapabilitiesCAP_NET_BIND_SERVICEGrants the capability to the process at exec time
CapabilityBoundingSetCAP_NET_BIND_SERVICEPrevents any other capability from being added
UserdwaarProcess runs as a non-root user

Create the service account before starting the unit:

Terminal window
sudo useradd --system --no-create-home --shell /usr/sbin/nologin dwaar

If you bind only to ports above 1024 (e.g. behind a load balancer), remove both AmbientCapabilities and CapabilityBoundingSet from the unit file.

Dwaar writes structured JSON logs to stdout. Systemd captures stdout automatically and routes it to the journal.

View live logs:

Terminal window
journalctl -u dwaar -f

View logs since last boot:

Terminal window
journalctl -u dwaar -b

Filter by priority (errors only):

Terminal window
journalctl -u dwaar -p err

Export as JSON for log shippers:

Terminal window
journalctl -u dwaar -o json | jq .

To persist logs across reboots, ensure /var/log/journal exists:

Terminal window
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal

Send SIGHUP to trigger a zero-disruption config reload. The config watcher re-reads the Dwaarfile, recompiles routes, and swaps in new upstream pools without restarting the process or dropping connections.

Terminal window
# Via systemctl (preferred)
sudo systemctl reload dwaar
# Via the CLI
dwaar reload
# Directly (sends SIGHUP)
sudo kill -HUP $(cat /run/dwaar/dwaar.pid)

The ExecReload line in the unit file maps systemctl reload to SIGHUP:

ExecReload=/bin/kill -HUP $MAINPID

Apply these systemd security directives to reduce the attack surface. Add them to the [Service] section.

[Service]
# Filesystem
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/etc/dwaar /var/log/dwaar /run/dwaar /etc/dwaar/certs /etc/dwaar/acme
# Privilege escalation
NoNewPrivileges=true
SecureBits=keep-caps
# Kernel and system calls
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictSUIDSGID=true
# Devices
PrivateDevices=true
# Temporary filesystem
PrivateTmp=true
DirectiveWhat it prevents
ProtectSystem=strictMounts /, /usr, /boot read-only
ProtectHome=trueBlocks access to all home directories
NoNewPrivileges=truePrevents setuid/setgid escalation
PrivateDevices=trueHides all device nodes except pseudo-devices
PrivateTmp=trueIsolates /tmp and /var/tmp
MemoryDenyWriteExecute=trueBlocks JIT and shellcode injection
RestrictAddressFamiliesLimits sockets to IPv4, IPv6, and Unix

Production-ready unit file with all hardening options applied:

[Unit]
Description=Dwaar reverse proxy
Documentation=https://dwaar.dev/docs
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/dwaar --config /etc/dwaar/Dwaarfile
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStartSec=30s
TimeoutStopSec=60s
User=dwaar
Group=dwaar
WorkingDirectory=/etc/dwaar
EnvironmentFile=-/etc/dwaar/dwaar.env
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
PIDFile=/run/dwaar/dwaar.pid
RuntimeDirectory=dwaar
RuntimeDirectoryMode=0750
# Hardening
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/etc/dwaar /var/log/dwaar /run/dwaar /etc/dwaar/certs /etc/dwaar/acme
NoNewPrivileges=true
SecureBits=keep-caps
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictSUIDSGID=true
PrivateDevices=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target

Verify the security score with:

Terminal window
systemd-analyze security dwaar

A well-hardened unit scores below 4.0 (SAFE).