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."