How to Fix “Failed to set capabilities on file node” When Binding Node.js to Port 80 on Ubuntu


6 views

When deploying Node.js applications on Ubuntu, developers often need to bind to privileged ports (below 1024). The standard approach using setcap sometimes fails with:

Failed to set capabilities on file /usr/local/bin/node' (Invalid argument)
The value of the capability argument is not permitted for a file. Or the file is not a regular (non-symlink) file

This typically happens when:

  • Node.js was installed via package manager (creating symlinks)
  • The binary path is incorrect
  • Filesystem permissions prevent capability modification

1. Direct Path to Node Binary

First identify the actual node binary location:

readlink -f $(which node)
# Typical output: /usr/bin/node

Then apply capabilities to the real binary:

sudo setcap cap_net_bind_service=+ep /usr/bin/node

2. Alternative: Authbind Method

For systems where capabilities won't work:

sudo apt install authbind
sudo touch /etc/authbind/byport/80
sudo chown $USER /etc/authbind/byport/80
sudo chmod 755 /etc/authbind/byport/80

Then launch your app with:

authbind --deep node app.js

3. Reverse Proxy Approach

The most production-ready solution:

# Nginx configuration
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

While the capability method works, in production environments consider:

  • Process managers (PM2, Forever)
  • Containerization (Docker)
  • Cloud provider load balancers
  1. Verify node path with which node
  2. Check for symlinks with ls -l $(which node)
  3. Test with simplified app first
  4. Verify port binding with sudo netstat -tulnp | grep :80

When trying to run a Node.js application on port 80 (the default HTTP port) on Ubuntu, you'll typically encounter permission errors because ports below 1024 are restricted to root users. The common solution is to use Linux capabilities to grant Node.js permission to bind to privileged ports without running as root.

The standard recommendation is to run:

sudo setcap cap_net_bind_service=+ep /usr/local/bin/node

However, this often fails with the error:

Failed to set capabilities on file /usr/local/bin/node' (Invalid argument)
The value of the capability argument is not permitted for a file. Or the file is not a regular (non-symlink) file

The error typically occurs because:

  • The Node binary might be a symlink rather than the actual executable
  • The filesystem might not support capabilities (common with network-mounted drives)
  • The binary might be marked as immutable

1. Verify and Use the Actual Binary Path

First, find the actual Node.js binary location:

which node
readlink -f $(which node)

Then apply the capability to the real binary:

sudo setcap cap_net_bind_service=+ep $(readlink -f $(which node))

2. Alternative: Use Authbind

If capabilities don't work, authbind is a good alternative:

sudo apt-get install authbind
sudo touch /etc/authbind/byport/80
sudo chown $USER /etc/authbind/byport/80
sudo chmod 755 /etc/authbind/byport/80

Then run your app with:

authbind --deep node app.js

3. Reverse Proxy Approach

The most production-worthy solution is to use Nginx as reverse proxy:

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

After applying capabilities or authbind, test with a simple HTTP server:

const http = require('http');
http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Running on port 80');
}).listen(80);

Check if it works without sudo and verify the port binding:

ss -tulpn | grep 80
  • Only use these methods when absolutely necessary
  • Consider running as non-root user and dropping privileges after binding
  • Regularly update Node.js to patch security vulnerabilities
  • For production, the reverse proxy method is generally safer