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
- Verify node path with
which node
- Check for symlinks with
ls -l $(which node)
- Test with simplified app first
- 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