Skip to content

Installing and layout

Scope: getting nginx from your distribution's package, and the geography of /etc/nginx — including the fact that there are two layout traditions and your tutorials may assume the other one.

Install

On Debian/Ubuntu:

$ apt-get install -y nginx
$ nginx -v
nginx version: nginx/1.24.0 (Ubuntu)

The package registers a systemd service (nginx.service, started on boot on a normal machine) and creates the config tree below. Your distribution's version will lag the upstream releases; for the things in this guide that has never mattered.

The Ubuntu/Debian layout

$ ls /etc/nginx
conf.d            koi-utf        proxy_params    snippets
fastcgi.conf      koi-win        scgi_params     uwsgi_params
fastcgi_params    mime.types     sites-available
modules-available nginx.conf     sites-enabled
modules-enabled   win-utf

The main file pulls the rest in:

$ grep -n "include" /etc/nginx/nginx.conf
5:include /etc/nginx/modules-enabled/*.conf;
26: include /etc/nginx/mime.types;
59: include /etc/nginx/conf.d/*.conf;
60: include /etc/nginx/sites-enabled/*;

Two directories do the organisational work:

  • sites-available/ holds one file per site you might serve. sites-enabled/ holds symlinks into it — a site is live exactly when its symlink exists:

$ ls -l /etc/nginx/sites-enabled lrwxrwxrwx 1 root root 34 ... default -> /etc/nginx/sites-available/default

Enabling a site is ln -s; disabling is removing the link. The site's file stays put either way. - conf.d/ holds global .conf fragments (settings for the whole http block), included in name order.

The other tradition

nginx built from upstream source — and several other distributions — skip the sites-* machinery entirely. There is one nginx.conf plus a conf.d/ directory, and each site is a server block written either into the main file or into a conf.d/*.conf fragment. Neither tradition is wrong; they are the same config grammar with different filing. When a tutorial says "add this to your nginx.conf" and your system has sites-*, the translation is: make a file in sites-available, symlink it into sites-enabled. The server block itself is identical.

How this guide runs nginx

Rather than touch the system instance, the lab runs its own: a separate master with its own config file, pid, logs, and ports (8080/8443 on loopback), started with

nginx -p /home/you/nginx-lab/prefix -c /home/you/nginx-lab/nginx.conf

-p sets the prefix (the directory nginx treats as home for relative paths), -c the config file. This is a genuinely useful trick beyond the lab: it's how you test a candidate config, or a whole second nginx, without disturbing the one in production.

The user line, and a sandbox confession

In the stock Ubuntu config, one line near the top says user www-data; — the workers run as that unprivileged user while the master stays root. Leave it alone.

The lab sets user root; instead, and here is the whole reason, captured when the lab config was first tested without it:

$ nginx -t
nginx: the configuration file .../nginx.conf syntax is ok
2026/10/20 20:46:54 [emerg] ... chown(".../prefix/client_body", 65534) failed (1: Operation not permitted)
nginx: configuration file .../nginx.conf test failed

Starting a root master with non-root workers, nginx hands its temp directories to the worker user with a chown() call. This build machine's sandbox refuses chown() outright — for nobody (uid 65534) and for www-data (uid 33) alike; both attempts are in the lab transcripts. With user root; there is no handover and the test passes. On any real machine this does not happen, the stock www-data is correct, and running workers as root would be a needless risk. The lab does it because the alternative was no lab.

Check: ls /etc/nginx/sites-enabled on your machine should show what is actually live — the gap between sites-available and sites-enabled contents is the first thing to look at when "I edited the config and nothing changed."