Which Linux Capabilities Does Your VPN Process Need?
Interface setup, routes, device access and ordinary sockets are separate privileges. Name the operation that failed before granting CAP_NET_ADMIN.
A VPN daemon needs privileged network operations even though ordinary applications open outgoing sockets without any privilege at all. That gap is where most capability mistakes start: somebody sees a permission error, grants the whole set, and the service works for reasons nobody can explain later. The walkthrough below starts from the failed operation, maps it to one authority, and then checks the context the process actually runs in. Running as unrestricted root is a valid answer for some deployments, but it should be a decision rather than a default.
Start from the operation that failed
Read the error before you read a tutorial. "Operation not permitted" on an interface change, a refusal to open a device node, and a bind failure on a low port are three different problems with three different grants. If the process starts and only fails at the first route change, that also tells you the interface creation already succeeded, which narrows the answer considerably.
Reproduce the failure with the service stopped, then start it in the foreground under the same user if the implementation allows it. An error you can see beats a log line you have to guess at, and it makes the difference between a capability problem and an SELinux or AppArmor denial obvious.
Map each operation to one authority
The table is a starting map, not a substitute for the implementation's documentation:
| Operation | Usual authority | Watch for |
|---|---|---|
| Create or configure tun/tap | CAP_NET_ADMIN in the relevant namespace | Creating the device and opening it are separate steps |
| Add routes, rules, firewall entries | CAP_NET_ADMIN | Same capability, different namespace than you think |
| Open /dev/net/tun | Device permissions, not a capability | Group membership or a udev rule may be enough |
| Raw sockets | CAP_NET_RAW | Needed by some user-space tunnel implementations |
| Bind a port below 1024 | CAP_NET_BIND_SERVICE | Irrelevant for most modern tunnel ports |
Two rows in that table are the ones people conflate. Opening /dev/net/tun is a file-permission question; asking the kernel to configure what you opened is a capability question. Granting CAP_NET_ADMIN does not fix a device node your service user cannot read, and fixing the node does not give you route authority. Check both before you change either.
Kernel WireGuard and user-space tunnel programs do not have identical requirements either. The kernel module path configures an existing interface through netlink, while a user-space implementation may also need the device and, for some transports, raw sockets.
Read the context: service, container or shell
A capability in the host's network namespace can affect far more than the same operation inside a deliberately isolated stack. A container runtime typically starts with a reduced bounding set; adding NET_ADMIN to a container that shares the host network is a much larger grant than adding it to one with its own network namespace and no host mounts.
For systemd units, read what is actually applied rather than what the unit file appears to say: systemctl show -p CapabilityBoundingSet -p AmbientCapabilities reports the effective values, which may be set by a drop-in you forgot. Ask for the effective value, never the intended one. Ambient capabilities matter because a non-root service loses capabilities across exec without them.
Use the package or image's supported setup as the baseline. Do not add raw-socket permissions or privileged mode because another tutorial listed them for another tool; that reasoning transfers nothing and costs you the isolation you paid for. When in doubt, grant nothing and read the implementation notes for the exact operation you need.
Watch what happens after privileges drop
Many daemons start privileged, configure the interface, then drop to an unprivileged user and keep the tunnel. From that point the process can no longer add a route, rewrite a firewall rule or open a new privileged device, which is fine until a reconnect needs exactly that. Later file reads and hook execution can fail for the same reason.
Test the whole lifecycle rather than the first second: startup, a renewal or reconnect, and cleanup on stop. A first successful interface creation does not establish that every later privileged operation will still work, and the failure usually appears hours later in production.
Implementations that keep the tunnel open across a user switch usually pair that with options to persist the tunnel and keys. On the systemd side, the equivalent question is whether the unit's capability set survives the user switch you configured.
Record the grant and its reason
Write down the exact grant, the operation it enables and the error it fixed. One line in the deployment notes — "CAP_NET_ADMIN in the unit's network namespace, needed for route changes after reconnect" — is enough to stop the next person from widening the grant again.
Then re-read the effective set after every upgrade. Vendors add features, images change defaults, and a capability that was unnecessary last year can become load-bearing without anyone noticing.