Running Nginx as a Non-Root User

March 1st, 2014 Permalink

There are times when you may find yourself wanting to launch Nginx as a non-root user. Perhaps you are running an automated test suite against a local instance of a web application, for example, and the whole process - including starting up and shutting down Nginx - is operated by a bash script. The simple approach in this sort of situation is to set up the local environment so that you can sudo without password entry:

sudo nginx -c /absolute/path/to/my/nginx.conf

To enable password-free sudo access for any command for a specific user in Ubuntu create the file /etc/sudoers.d/username with the following contents:

username ALL=(ALL) NOPASSWD:ALL

What if you cannot do this, however? Your test automation script might be running on build system servers under a user that by design does not have sudo rights. This is where you will start to find that Nginx really doesn't like to be launched as a non-root user: there are a number of settings that default to assuming that the user has write access to places like /var/log and /var/run. Fortunately all of these settings can be changed. It's just a little painful to wander through the documentation to find them one by one if you don't have a list to hand already.

The following filesystem path configuration options need to be changed, and set to locations to which the user has write access:

  • error_log (in the main scope as well as lower scopes)
  • access_log
  • pid
  • client_body_temp_path
  • fastcgi_temp_path
  • proxy_temp_path
  • scgi_temp_path
  • uwsgi_temp_path

Here is an example configuration file that allows the package installation of Nginx on Ubuntu 12.04 to be launched by a non-root user without using sudo. You can find this set up with a Vagrant VM at GitHub:

#
# A very simple example configuration showing how to launch Nginx as a non-root
# user without sudo access.
#
# Adjust the paths and other settings for your specific circumstances. They are
# currently configured for transient usage - you'd want to pick more permanent
# locations in the filesystem if intending this to run for a while.
#
# Note that as Nginx is not launched as root, it cannot bind to privileged
# ports lower than 1024.
#
# Usage: nginx -c /path/to/this/nginx.conf
#

# This error log will be written regardless of server scope error_log
# definitions, so we have to set this here in the main scope.
#
# Even doing this, Nginx will still try to create the default error file, and
# log a non-fatal error when it fails. After that things will work, however.
error_log /tmp/error.log;

# The pidfile will be written to /var/run unless this is set.
pid /tmp/nginx.pid;

worker_processes 1;

events {
  worker_connections 1024;
}

http {
  # Set an array of temp and cache file options that will otherwise default to
  # restricted locations accessible only to root.
  client_body_temp_path /tmp/client_body;
  fastcgi_temp_path /tmp/fastcgi_temp;
  proxy_temp_path /tmp/proxy_temp;
  scgi_temp_path /tmp/scgi_temp;
  uwsgi_temp_path /tmp/uwsgi_temp;

  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;

  include /etc/nginx/mime.types;
  index index.html index.htm index.php;

  log_format   main '$remote_addr - $remote_user [$time_local] $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

  default_type application/octet-stream;

  server {
    # IPv4.
    listen 8080;
    # IPv6.
    listen [::]:8080 default ipv6only=on;

    root /var/www;

    access_log /tmp/access.log;
    error_log /tmp/error.log;

    location / {
      # First attempt to serve request as file, then as directory, then fall
      # back to index.html.
      try_files $uri $uri/ /index.html;
    }
  }
}