Installing Nginx on Ubuntu / Debian #

Ubuntu and Debian are the two most popular Linux distributions for server environments, whether for traditional web hosting or modern microservices architectures in the cloud. Both distributions use the same package management system, the Advanced Package Tool (apt), which makes the software installation process very structured.

When you want to install Nginx on an Ubuntu- or Debian-based system, you’re faced with two main repository paths: using the distribution’s built-in repository (distro repository) or using the official repository managed directly by the Nginx development team (Nginx.org repository). Both options have different runtime consequences and maintenance cycles. This article covers both methods in depth, guiding you through apt package priority configuration (apt pinning), managing the service with an understanding of kernel signals, and securing the server using the built-in firewall.

Choosing a Repository: Distro Default vs Official Nginx #

Before you run the installation command, you need to understand the fundamental difference between these two package sources. This choice will affect which Nginx feature version you get and how you receive updates in the future.

Distribution Built-in Repository (Distro Repo) #

The built-in Ubuntu and Debian repositories prioritize overall operating system stability. The Nginx packages provided here have been rigorously tested for compatibility with other system libraries.

  • Version Characteristics: These packages usually use a legacy stable version that’s already quite old. For example, Ubuntu 22.04 LTS provides Nginx version 1.18.0 by default, even though the official Nginx stable version has long surpassed it.
  • Maintenance: Updates from the distro security team are generally backports (only patching security holes without bumping the minor or major version number).
  • Use Cases: A great fit if you don’t need modern web protocol features (like HTTP/3 or QUIC) and prioritize a server that needs no additional configuration after installation.

Official Nginx Repository (Nginx.org) #

This repository is maintained directly by the Nginx developers. Here you get access to two release branches: Stable (focused on stability with important bug fixes) and Mainline (packed with the latest performance features).

  • Version Characteristics: Provides the newest versions instantly (e.g., 1.24.x or 1.26.x and above), with full support for HTTP/2, HTTP/3, and the latest TLS encryption algorithms.
  • Maintenance: Updates are released as soon as the upstream source code is declared stable.
  • Use Cases: Highly recommended for high-traffic production infrastructure, CDN servers, API gateways, or any project that wants to maximize modern web protocol efficiency.

Here’s the repository configuration decision flow before you start typing commands in the terminal:

flowchart LR
    Start["Choose Repository"] --> CheckFitur{"Do we need HTTP/3 <br> or the latest TLS features?"}
    CheckFitur -->|"No"| Distro["Use the OS Built-in Repository <br> (apt install nginx)"]
    CheckFitur -->|"Yes"| NginxOrg["Use the Nginx.org Repository <br> (Needs GPG Key & Pinning)"]
    
    NginxOrg --> ChooseBranch{"Choose Release Branch"}
    ChooseBranch -->|"Stable & Minimal Risk"| StableBranch["Stable Branch <br> (Recommended for Production)"]
    ChooseBranch -->|"Experimental / New Features"| MainlineBranch["Mainline Branch <br> (Latest Features)"]
    
    style Distro stroke:#f57c00,stroke-width:2px
    style StableBranch stroke:#388e3c,stroke-width:2px
    style MainlineBranch stroke:#0288d1,stroke-width:2px

Method 1: Installing from the Distribution’s Built-in Repository #

If you choose the distro’s built-in stability and want a quick setup, you can use the OS’s default repository directly. On Debian and Ubuntu, this process is very easy because the Nginx package is already indexed by default.

You can install it by running the following series of commands:

# Update the local package index to make sure you get the latest version indexed by the OS
sudo apt update

# Install the Nginx package along with all its dependencies
sudo apt install nginx -y

After the installation finishes, Ubuntu will generally start Nginx automatically in the background as a systemd service. You can do a quick check to verify the installed Nginx version:

# Check the installed Nginx version
nginx -v
# Example output on Ubuntu 22.04:
# nginx version: nginx/1.18.0 (Ubuntu)

Method 2: Installing from the Official Nginx.org Repository #

To get the latest stable version straight from upstream, you need to add the official Nginx repository to your apt package management system. This process requires several security steps to ensure the packages you download are genuinely signed by the Nginx developers and haven’t been modified by third parties.

Step 1: Installing Initial Dependencies #

You need a few system helper tools to secure the cryptographic key transfer process and detect your distribution codename automatically.

sudo apt update
sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring

Step 2: Importing the Official Nginx GPG Signing Key #

Apt uses the GNU Privacy Guard (GPG) digital signature system to verify package integrity. You need to download the official Nginx public key and store it in your system keyring:

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

After downloading the key, you must verify that the public key’s fingerprint matches the official key published by Nginx. This is important to prevent Man-in-the-Middle attacks:

gpg --dry-run --quiet --no-keyring \
    --import --import-options import-show \
    /usr/share/keyrings/nginx-archive-keyring.gpg

Check the fingerprint output line in your terminal. Make sure it matches the following code exactly: 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62

If the fingerprint doesn’t match, delete the keyring file and don’t continue the installation.

Step 3: Adding the Repository Source #

After the key is verified, you can add the official repository line to your apt source configuration file. The command below uses lsb_release -cs dynamically to detect your Ubuntu or Debian version (e.g., jammy, focal, or bookworm):

# For Ubuntu (using the Stable branch):
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

# Note for Debian users, change the link above to:
# http://nginx.org/packages/debian $(lsb_release -cs) nginx

[!TIP] If you want to use the Mainline branch for the latest experimental features, change the repository URL to http://nginx.org/packages/mainline/ubuntu or http://nginx.org/packages/mainline/debian.

Step 4: Configuring APT Preferences (Apt Pinning) #

This step is crucial yet often overlooked. If you don’t set package priority preferences (pinning), the OS may keep choosing the Nginx package from the distro’s built-in repository during system updates (apt upgrade), because distro packages often have release naming structures the OS considers higher priority.

You need to instruct apt to prioritize packages from nginx.org by creating a preferences configuration file:

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

With a priority of 900, packages from the official Nginx repository will always take precedence over the distro’s built-in packages, which usually have a default priority of 500.

Step 5: Installing the Latest Nginx Version #

Now you can update the local index and install the latest Nginx version:

sudo apt update
sudo apt install nginx -y

Let’s verify to make sure the installed version is the latest:

nginx -v
# The output should show the latest stable version from upstream:
# nginx version: nginx/1.26.1

Service Management via Systemd #

On modern Ubuntu and Debian, background services are managed by the systemd system manager. Nginx is installed as a service unit named nginx.service. You can manage the Nginx process lifecycle using the systemctl commands.

Basic Service Management Commands #

Here’s a list of daily administration commands you should know:

# Check the active status of the Nginx process
sudo systemctl status nginx

# Start the Nginx service
sudo systemctl start nginx

# Stop the Nginx service
sudo systemctl stop nginx

# Enable autostart when the server boots/reboots
sudo systemctl enable nginx

# Disable autostart at boot
sudo systemctl disable nginx

Understanding the Difference Between restart vs reload (An OS Signal Review) #

Many novice web administrators treat restart and reload as the same thing. In reality, at the operating system level, they trigger very different kernel mechanisms:

flowchart TD
    subgraph RestartAction["systemctl restart nginx (Downtime)"]
        direction TB
        CmdRest["Send SIGTERM / SIGKILL signal"] --> StopProc["All Master & Worker processes killed immediately"]
        StopProc --> CloseSock["TCP port 80/443 sockets closed abruptly"]
        CloseSock --> StartNew["New Master Process created, parses config, opens sockets"]
        StartNew --> DoneRest["Active client connections dropped abruptly"]
    end
    
    subgraph ReloadAction["systemctl reload nginx (Zero Downtime)"]
        direction TB
        CmdRel["Send SIGHUP (1) signal to Master"] --> ParseNew["Master reads & validates the new configuration"]
        ParseNew --> SpawnNew["Master spawns new Workers with the new config"]
        SpawnNew --> SignalOld["Master sends SIGQUIT signal to old Workers"]
        SignalOld --> GracefulOld["Old Workers finish active requests then exit gracefully"]
        GracefulOld --> DoneRel["Client connections stay served with no interruption"]
    end
    
    style RestartAction stroke:#d32f2f,stroke-width:1.5px
    style ReloadAction stroke:#388e3c,stroke-width:1.5px
  • systemctl restart nginx: This command sends a SIGTERM signal (or SIGKILL if it doesn’t respond within a certain time) to the master process. This kills the entire Nginx process tree instantly, closes TCP ports 80/443, and forcibly cuts off client connections currently downloading data. After that, a new process is started from scratch. This triggers brief downtime.
  • systemctl reload nginx (or nginx -s reload): This command sends a SIGHUP signal (hangup signal number 1) to the Nginx master process. The master process reads and validates the new configuration. If valid, it spawns new worker processes that immediately handle incoming connections using the new configuration. Simultaneously, the master sends a SIGQUIT signal to old workers, ordering them into graceful shutdown mode (finishing in-flight client transactions before exiting peacefully). This guarantees zero-downtime.

[!WARNING] Never run reload or restart without testing your configuration first using sudo nginx -t. If there are syntax errors in the new configuration and you immediately reload, the Nginx master process will indeed reject that configuration and keep running with the old one, but in some critical failure cases (like shared memory allocation errors or missing SSL certificates), the Nginx process could die suddenly and take your website down.

Get into the habit of always running the following command sequence when applying new configuration:

# Test your configuration first
sudo nginx -t

# If the output shows "syntax is ok" and "test is successful", do the reload
sudo nginx -s reload

Directory Structure and File Conventions #

After Nginx is installed on your Ubuntu/Debian system, you need to understand the default directory structure so you don’t get confused when placing configuration files or static web files.

1. Configuration Directory /etc/nginx/ #

This is the center of all your Nginx configuration.

  • nginx.conf: The main global configuration file. It sets the number of worker processes, system user location, global logging format, and includes configuration files from other sub-directories.
  • conf.d/: The default directory for placing Virtual Host or server block configuration files. All files ending in .conf in this folder are automatically loaded by Nginx.
  • sites-available/ and sites-enabled/: A configuration layout convention inherited from the Debian/Ubuntu packaging system. Configurations are placed in sites-available/, then activated by creating a symbolic link to sites-enabled/ using the ln -s command.
    • Important Difference: The official Nginx.org repository does not use this dual structure by default, for efficiency and to simplify configuration directly inside conf.d/. You’re advised to use the conf.d/ directory so your configuration stays portable across Linux distributions.
  • mime.types: The file that maps file extensions (like .html, .css, .js, .png) to the appropriate HTTP MIME types so client browsers can render pages correctly.

2. Log File Directory /var/log/nginx/ #

Nginx writes all its records in this directory:

  • access.log: Stores the history of every HTTP request that hits your server (client IP, requested URL, HTTP status code, browser agent, etc.).
  • error.log: Stores system-level error logs, SSL warnings, and backend proxy connection failures. This is the first file you should check when troubleshooting.

3. Web Root Directory /var/www/html/ #

On a default Ubuntu/Debian installation, this directory contains the default Nginx HTML file. When you access the server IP in a browser for the first time, the index.html file in this directory is what gets served to your screen.


Opening Port Access Through the UFW Firewall #

The Ubuntu operating system includes a user-friendly firewall management tool called Uncomplicated Firewall (ufw). By default, after you enable UFW, all incoming ports are closed except the ones you explicitly allow (like SSH port 22).

If you want Nginx to be accessible from the internet, you need to open the HTTP (80) and HTTPS (443) ports. Nginx automatically registers application profiles into the UFW system when installed.

You can check the registered Nginx application profiles using:

sudo ufw app list
# Example output:
# Available applications:
#   Nginx Full      (Opens port 80 + 443)
#   Nginx HTTP      (Opens port 80 only)
#   Nginx HTTPS     (Opens port 443 only)
#   OpenSSH

To allow incoming web traffic, you’re advised to choose the Nginx Full profile so the server is ready to serve both plain HTTP traffic and encrypted HTTPS traffic:

# Allow the Nginx Full profile through the firewall
sudo ufw allow 'Nginx Full'

# Check the firewall status to make sure the new rule is active
sudo ufw status
# Example output:
# Status: active
# To                   Action      From
# --                   ------      ----
# OpenSSH              ALLOW       Anywhere
# Nginx Full           ALLOW       Anywhere

Final Installation Verification #

To make sure Nginx is installed correctly, running actively, and listening for network traffic on the right ports, do the following three verification steps:

1. Check Listening Network Sockets #

You can check which process is listening on port 80 using the ss command:

sudo ss -tlnp | grep nginx
# The output should show the Nginx master process bound to port 80 (*:80 or 0.0.0.0:80):
# LISTEN   0        511              0.0.0.0:80             0.0.0.0:*      users:(("nginx",pid=1234,fd=6))

2. Test the HTTP Header Response #

Test the Nginx server response locally using the curl utility, requesting only its headers:

curl -I http://localhost
# Example output:
# HTTP/1.1 200 OK
# Server: nginx/1.26.1
# Content-Type: text/html
# ...

3. Check System Processes #

Make sure the master process runs as root and the worker processes run as a non-privileged user (e.g., user nginx or www-data):

ps aux | grep nginx
# Example output:
# root      1234  0.0  0.1  40232  3120 ?        Ss   12:00   0:00 nginx: master process /usr/sbin/nginx
# nginx     1235  0.0  0.2  40680  5210 ?        S    12:00   0:00 nginx: worker process
# nginx     1236  0.0  0.2  40680  5210 ?        S    12:00   0:00 nginx: worker process

Summary #

  • The Official Nginx Repository (nginx.org) provides the latest upstream stable version ready for HTTP/3 protocol implementation, while the Distro Repository offers old-version stability.
  • Use a pinning priority of 900 at /etc/apt/preferences.d/99nginx to prevent the default repository from overriding your official Nginx packages.
  • systemctl reload sends the HUP (1) signal for configuration updates without cutting client connections (zero-downtime), while restart sends the TERM (15) signal which forces the entire process tree to die instantly.
  • Always run nginx -t before reloading to avoid downtime from configuration syntax errors.
  • Open the UFW firewall with the Nginx Full profile to open ports 80 (HTTP) and 443 (HTTPS) simultaneously.

← Previous: Event-Driven Architecture   Next: CentOS / RHEL →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact