Docs / Linux Basics / Understanding Linux Capabilities vs Running as Root

Understanding Linux Capabilities vs Running as Root

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 176 views · 3 min read

Traditionally, Linux had two levels of privilege: root (can do anything) and regular user (limited). Linux capabilities break root privileges into granular permissions, allowing processes to have only the specific privileges they need without full root access.

Why Capabilities Matter

Running a web server as root just because it needs to bind to port 80 gives it far more power than necessary. With capabilities, you can grant only the port-binding privilege.

Common Capabilities

# Key capabilities for server administrators:
# CAP_NET_BIND_SERVICE — Bind to ports below 1024
# CAP_NET_RAW          — Use raw sockets (ping, tcpdump)
# CAP_SYS_ADMIN        — Broad system admin (mount, syslog)
# CAP_DAC_OVERRIDE     — Bypass file permission checks
# CAP_CHOWN            — Change file ownership
# CAP_SETUID/SETGID    — Change process UID/GID
# CAP_SYS_PTRACE       — Trace/debug other processes
# CAP_NET_ADMIN        — Configure networking

Viewing Capabilities

# Check capabilities of a running process
cat /proc/$(pgrep nginx | head -1)/status | grep Cap

# Decode capability bitmask
capsh --decode=00000000a80425fb

# Check capabilities of a binary file
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep

# List all files with capabilities set
getcap -r / 2>/dev/null

Setting Capabilities on Files

# Allow a web server to bind to port 80 without root
sudo setcap cap_net_bind_service=+ep /usr/local/bin/myserver

# Allow a binary to use raw sockets
sudo setcap cap_net_raw=+ep /usr/local/bin/mytool

# Remove capabilities from a file
sudo setcap -r /usr/local/bin/myserver

# Capability flags:
# e = effective (capability is active)
# p = permitted (capability can be used)
# i = inheritable (passed to child processes)

Using Capabilities with systemd

# Instead of running a service as root, use capabilities in the unit file
sudo systemctl edit myapp.service

[Service]
User=myapp
Group=myapp
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

# This lets myapp bind to port 443 without running as root

Practical Examples

Node.js on Port 80 Without Root

# Option 1: Set capability on the node binary
sudo setcap cap_net_bind_service=+ep $(which node)

# Option 2: Use systemd capabilities (preferred)
# In your service file:
[Service]
User=nodeapp
AmbientCapabilities=CAP_NET_BIND_SERVICE
ExecStart=/usr/bin/node /var/www/app/server.js

# Option 3: Use a reverse proxy (most common)
# Run nginx on port 80/443, proxy to Node.js on port 3000

Tcpdump Without Root

# Allow a user to run tcpdump without sudo
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/tcpdump

# Now any user can capture packets:
tcpdump -i eth0 -c 10

Security Best Practices

  1. Principle of least privilege — Grant only the capabilities a process actually needs
  2. Use systemd capabilities — Prefer AmbientCapabilities over setcap on binaries
  3. Audit capabilities — Regularly check which files have capabilities set
  4. Use NoNewPrivileges=true — Prevent processes from gaining additional privileges
  5. Prefer reverse proxies — Instead of giving web apps CAP_NET_BIND_SERVICE, use nginx/Caddy as a reverse proxy

Was this article helpful?