Hardening

Harden any service with sandboxing directives

5 min · updated June 15, 2026

systemd can sandbox a service with kernel features (namespaces, seccomp, capabilities) — no container required. Add these to [Service], then loosen only what breaks.

A solid baseline

[Service]
# Privilege
NoNewPrivileges=true
CapabilityBoundingSet=
AmbientCapabilities=

# Filesystem
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/myapp /var/log/myapp

# Kernel & devices
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectClock=true
PrivateDevices=true

# Network & syscalls
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
MemoryDenyWriteExecute=true
LockPersonality=true
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
SystemCallArchitectures=native

What the key ones do:

Allow back what it needs

Apply and score it

sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemd-analyze security myapp.service     # 0 = locked down, 10 = exposed
journalctl -u myapp.service -e             # check nothing got blocked

systemd-analyze security lists every directive with its exposure weight, so you can see exactly what to tighten next.


Method: start strict, restart, watch the journal for EPERM/“Operation not permitted”, and relax one directive at a time. Gotchas: MemoryDenyWriteExecute=true breaks JITs (some JVMs, V8); drop it for those. SystemCallFilter can block a runtime’s startup — if a service dies instantly after adding it, that’s usually the cause. Test on a non-production box first.

← All recipes