How to Install Nginx on Ubuntu and Red Hat

How to Install Nginx on Ubuntu and Red Hat

Installing Nginx is usually the very first step in setting up any web server, and while the actual install command is simple, I’ve learned over the years that the details around it — which repository you pull from, firewall configuration, and verifying the service actually started correctly — matter just as much as the install itself. In this guide, I’ll walk through installing Nginx on both Ubuntu/Debian and RHEL/CentOS/Fedora, covering the default package manager approach as well as installing directly from the official Nginx repository for the latest stable version.

Why the Installation Source Matters

Most Linux distributions include Nginx in their default package repositories, but that version is often several releases behind the latest stable Nginx release, since distro maintainers prioritize stability over having the newest features. For most standard use cases, the distro-provided version is perfectly fine. But if I need a specific recent feature (like newer HTTP/3 support, or a bug fix that hasn’t been backported), I install directly from Nginx’s official repository instead. I’ll cover both paths.

Installing on Ubuntu / Debian

Method 1: From the Default Ubuntu/Debian Repository (Simplest)

sudo apt update
sudo apt install nginx

This pulls whatever version is packaged for your specific Ubuntu/Debian release. It’s reliable, well-tested against the rest of the system, and receives security patches through the regular apt upgrade cycle.

Method 2: From the Official Nginx Repository (Latest Stable)

If I want the most current stable release directly from Nginx, I add their official repository first.

Step 1: Install prerequisite packages

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

Step 2: Import the Nginx signing key

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

Step 3: Add the repository

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

Step 4: Set repository priority (optional but recommended)

This ensures apt prefers packages from the official Nginx repo over the distro’s default one, avoiding version conflicts:

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

Step 5: Install

sudo apt update
sudo apt install nginx

Installing on Red Hat / CentOS / Rocky / AlmaLinux / Fedora

Method 1: From the Default Repository (Simplest)

On RHEL 8/9, CentOS Stream, Rocky Linux, or AlmaLinux:

sudo dnf install nginx

On older CentOS 7 or systems still using yum:

sudo yum install epel-release
sudo yum install nginx

CentOS 7 requires the EPEL (Extra Packages for Enterprise Linux) repository since Nginx isn’t in the base repos on that release.

Method 2: From the Official Nginx Repository (Latest Stable)

Step 1: Create the repo file

sudo nano /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/rhel/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

Step 2: Install

sudo dnf install nginx

If I specifically want the mainline (development) branch instead of stable, I change nginx-stable to nginx-mainline in the repo file and adjust the baseurl accordingly per Nginx’s official documentation — though for production servers, I always stick with the stable branch.

Step 2 (Both Platforms): Start and Enable Nginx

Regardless of which method I used, the service management commands are identical across distros since both use systemd:

sudo systemctl start nginx
sudo systemctl enable nginx

enable ensures Nginx starts automatically on system boot, which I always want for a production web server.

Step 3: Configure the Firewall

Ubuntu/Debian (UFW)

sudo ufw allow 'Nginx HTTP'
sudo ufw allow 'Nginx HTTPS'
sudo ufw enable
sudo ufw status

If I want both ports at once without HTTPS being configured yet, Nginx Full covers both 80 and 443:

sudo ufw allow 'Nginx Full'

RHEL/CentOS/Fedora (firewalld)

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Step 4: Verify the Installation

I check the installed version and confirm the service is active:

nginx -v
sudo systemctl status nginx

Expected output for the status check includes active (running). Then I test with curl locally:

curl -I http://localhost/

Expected response:

HTTP/1.1 200 OK
Server: nginx/1.27.x

Finally, I open the server’s public IP address in a browser — I should see the default “Welcome to nginx!” page, confirming the web server is reachable externally, not just from localhost.

SELinux Considerations on RHEL-based Systems

RHEL, CentOS, Rocky, and AlmaLinux ship with SELinux enabled by default, which can silently block Nginx from doing things that would otherwise “just work” on Ubuntu — like connecting to a backend proxy or serving files from a non-standard directory. I check SELinux status:

getenforce

If it returns Enforcing and I run into permission-denied errors that don’t make sense given normal Linux file permissions, I check the audit log:

sudo cat /var/log/audit/audit.log | grep nginx | audit2why

Common fixes include allowing Nginx to make outbound network connections (needed for reverse proxying):

sudo setsebool -P httpd_can_network_connect 1

Or fixing file context labels if serving content from a directory other than /usr/share/nginx/html or /var/www:

sudo semanage fcontext -a -t httpd_sys_content_t "/custom/path(/.*)?"
sudo restorecon -Rv /custom/path

I never just disable SELinux entirely as a shortcut — it’s a genuinely useful security layer, and the targeted fixes above are almost always sufficient.

Directory Structure Reference

Knowing where things live saves a lot of time later:

PurposeUbuntu/DebianRHEL/CentOS/Fedora
Main config/etc/nginx/nginx.conf/etc/nginx/nginx.conf
Site configs/etc/nginx/sites-available/ + sites-enabled//etc/nginx/conf.d/
Default web root/var/www/html/usr/share/nginx/html
Access log/var/log/nginx/access.log/var/log/nginx/access.log
Error log/var/log/nginx/error.log/var/log/nginx/error.log
Service unitnginx.service (systemd)nginx.service (systemd)

Troubleshooting Common Issues

Unable to locate package nginx on Ubuntu/Debian. Run sudo apt update first — a stale package index is the most common cause.

Package nginx is not available on CentOS 7. Missing the EPEL repository — install epel-release first as shown above.

Nginx installed but systemctl status nginx shows failed. Check the error log directly for the specific cause:

sudo journalctl -xeu nginx.service

A very common cause is port 80 already being used by another service (like Apache, if it’s also installed):

sudo ss -tulpn | grep :80

If another process holds port 80, I either stop it (sudo systemctl stop apache2) or reconfigure Nginx to listen on a different port for testing.

Can’t reach the server from a browser even though curl localhost works. This is almost always a firewall or cloud security group issue — the server-level firewall (ufw/firewalld) might be open, but a cloud provider’s security group (AWS, DigitalOcean, Azure) also needs port 80/443 explicitly allowed at the network level.

Nginx starts but immediately shows a blank page or 403 Forbidden. Check file permissions on the web root — Nginx’s worker process (usually running as www-data on Debian or nginx on RHEL-based systems) needs read access:

sudo chown -R www-data:www-data /var/www/html   # Debian/Ubuntu
sudo chown -R nginx:nginx /usr/share/nginx/html  # RHEL/CentOS

Security Considerations

  • Install from the official Nginx repository if you need timely security patches ahead of your distro’s release cycle, but weigh this against the stability of sticking with your distro’s tested packages.
  • Keep Nginx updated regularly — sudo apt upgrade or sudo dnf upgrade should be part of routine maintenance, not an afterthought.
  • Don’t leave the default “Welcome to nginx!” page publicly accessible in production — replace it immediately with your actual site or a proper 404/blocked response.
  • Only open the firewall ports you actually need (80/443) rather than broadly allowing all traffic.

Performance Tips

  • The Nginx worker process count and connection limits are configured in nginx.conf (worker_processes, worker_connections) — I usually set worker_processes auto; to match available CPU cores automatically.
  • On RHEL-based systems, verify SELinux booleans are set correctly rather than disabling SELinux, since a misconfigured SELinux policy can silently degrade functionality without an obvious performance-related error message.

Real-World Use Cases

  • Provisioning a fresh VPS or cloud instance (AWS EC2, DigitalOcean Droplet, Linode) as the foundation for a new web application.
  • Setting up a consistent, repeatable install process across a fleet of servers using configuration management tools (Ansible, Terraform) that call these same underlying commands.
  • Standing up a quick local or staging server for testing before deploying to production infrastructure.

Best Practices

  • Always run sudo apt update (Debian/Ubuntu) or ensure repo metadata is current (RHEL-based) before installing.
  • Decide upfront whether you need the distro-default version or the latest stable from Nginx’s official repo, based on your actual feature/patch requirements.
  • Enable the service to start on boot immediately after installation — don’t leave this for later and risk an unexpected downtime after a server reboot.
  • Configure the firewall as part of the same initial setup, not as an afterthought once you’re troubleshooting why the site isn’t reachable.
  • On RHEL-based systems, understand and work with SELinux rather than disabling it outright.

Wrapping Up

Installing Nginx itself takes one command on either platform, but the surrounding steps — firewall rules, SELinux considerations on RHEL-based systems, and verifying the service is genuinely reachable from outside the server — are what actually determine whether the install “worked” in a practical sense. I always finish an installation by confirming all three: the service is active, the firewall allows the right ports, and I can load the page from an external browser, not just from curl localhost on the server itself.

Total
1
Shares

Leave a Reply

Previous Post
How to set up Apache for serving WordPress websites

How to Set Up Apache for Serving WordPress Websites

Next Post
How to Configure Nginx Server Blocks (Virtual Hosts)

How to Configure Nginx Server Blocks (Virtual Hosts)

Related Posts