How to Install and Configure Nginx on an Ubuntu VPS

Practical guide to install and configure Nginx on Ubuntu — server blocks, UFW firewall, HTTPS with Let's Encrypt and basic tuning.

Nginx is the most widely used web server on Linux VPS for a reason: small footprint (around 5-10 MB of RAM at idle), an event-driven model that handles thousands of concurrent connections with just a few workers, and a declarative configuration that scales well from a single static site to a reverse proxy for microservices. But running apt install nginx alone leaves most of the real work undone — default server blocks, no HTTPS, an unconfigured firewall.

This tutorial covers the full sequence to put an Ubuntu VPS serving a site (static or application) with Nginx in production: installation, a per-domain server block, UFW firewall, HTTPS certificate via Let’s Encrypt and minimal performance tuning. Persona: developer about to deploy an application or static site who wants a clean, auditable setup — not a copy-paste that hides decisions.

Estimated time: 25-35 minutes, counting DNS propagation if you have not pointed the domain yet.

Prerequisites

What you need before starting

A VPS with Ubuntu 24.04 LTS (or 22.04 LTS) installed, SSH access with a sudo user, a fixed public IP and a domain with an A record pointing to that IP. Reserve at least 512 MB of free RAM — Nginx itself consumes little, but Certbot and the application behind it add up.

System Ubuntu 24.04 LTS
Access SSH + sudo
Required ports 22, 80, 443
Domain A record on the VPS IP

Confirm the domain resolves correctly before moving on. From another machine, run dig +short yourdomain.com — the answer should be exactly your VPS IP. If you just pointed the record, wait for propagation to complete before trying to issue a certificate, because every Let’s Encrypt failure counts toward the weekly rate limit.

Installing Nginx

The first step is to install the package and verify that the service comes up correctly. Ubuntu ships Nginx packaged and integrated with systemd, so the cycle is straightforward.

01

Update the APT package index to make sure you install the latest version available in the repository:

sudo apt update

This command reads the repositories configured in /etc/apt/sources.list and downloads the updated manifests. Without it you may end up installing an outdated version with vulnerabilities already fixed in newer releases.

02

Install Nginx:

sudo apt install -y nginx

The installer automatically sets up the systemd service, creates the structure under /etc/nginx/ and starts the process. The -y flag accepts prompts automatically — in interactive environments you can omit it.

03

Check that the service is active:

sudo systemctl status nginx

The output should show Active: active (running) in green. If it shows failed, run sudo journalctl -u nginx -n 50 to see the actual error — usually it’s a port conflict (Apache running, for example) or a syntax error in a config file.

Apache conflict

If the VPS came with Apache pre-installed (some images do), stop and disable it first: sudo systemctl stop apache2 && sudo systemctl disable apache2. Two applications cannot bind to port 80 at the same time.

Configuring the UFW firewall

Before exposing ports, configure the firewall. UFW (Uncomplicated Firewall) ships with Ubuntu and uses named profiles to simplify common rules.

04

List the available profiles for Nginx:

sudo ufw app list

You’ll see Nginx Full, Nginx HTTP and Nginx HTTPS. The “Full” profile opens 80 and 443 — that’s what you want for a complete web server.

05

Allow SSH (if not already allowed) and web traffic, then enable the firewall:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Order matters: open SSH before enabling UFW. Otherwise you get disconnected and locked out of the VPS.

You can lock yourself out of the VPS

Never run sudo ufw enable without first allowing the SSH port. If it happens and you lose access, most providers offer a serial console or recovery through the panel — on Hostini, the KVM console in the VPS panel solves it.

Server blocks for multiple domains

Server blocks (the equivalent of Apache virtual hosts) let you serve more than one site on the same VPS, each with its own domain and file root. Even if you only have one site for now, setting it up via a server block makes future expansion easier.

06

Create the root folder for your domain and adjust permissions:

sudo mkdir -p /var/www/yourdomain.com/html
sudo chown -R $USER:$USER /var/www/yourdomain.com/html
sudo chmod -R 755 /var/www/yourdomain.com

Ownership stays with your user so you can deploy without sudo every time. Nginx (running as www-data) only needs read access — 755 covers it.

07

Create a simple index.html for testing:

echo "<h1>It works</h1>" > /var/www/yourdomain.com/html/index.html
08

Create the server block at /etc/nginx/sites-available/yourdomain.com:

server {
    listen 80;
    listen [::]:80;

    root /var/www/yourdomain.com/html;
    index index.html index.htm;

    server_name yourdomain.com www.yourdomain.com;

    location / {
        try_files $uri $uri/ =404;
    }
}

The try_files directive tries to serve the requested file, then treats it as a directory, and finally returns 404 — the standard pattern for static content. For Node/Python/PHP applications you swap that part for proxy_pass or fastcgi_pass.

09

Enable the site by creating a symlink in sites-enabled and disable the default:

sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default

Nginx on Ubuntu loads everything in sites-enabled. Leaving default active causes a server_name conflict and Nginx picks one arbitrarily.

10

Test the syntax and reload:

sudo nginx -t
sudo systemctl reload nginx

nginx -t validates the entire config without applying it — always run it before reload. If it passes, the reload is zero-downtime: old workers finish their requests while new ones come up.

HTTPS with Let’s Encrypt

Without HTTPS today you lose SEO, ranking, every modern browser shows “Not secure”, and forms break. Certbot from the Let’s Encrypt project automates free issuance and renewal.

11

Install Certbot and the Nginx plugin:

sudo apt install -y certbot python3-certbot-nginx

The plugin reads your server blocks, detects the domains and adjusts the SSL directives automatically — you don’t need to edit nginx.conf manually.

12

Issue the certificate:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot asks for an email (for expiration notices), accepts the ToS, and offers to redirect HTTP to HTTPS automatically — pick “redirect” (option 2). Validation happens via the HTTP-01 challenge on port 80, so confirm that firewall and DNS are correct beforehand.

Automatic renewal

The Certbot package already installs a systemd timer (certbot.timer) that renews certificates close to expiration. Check it with sudo systemctl list-timers | grep certbot. There’s nothing to configure — renewal runs by itself twice a day.

Verification

Confirm everything is live. Open https://yourdomain.com in the browser — it should load the test page with a green padlock. From the command line, validate the TLS handshake and headers:

curl -I https://yourdomain.com

The expected response starts with HTTP/2 200 (Certbot enables HTTP/2 by default) and includes server: nginx/1.x.x. If it shows HTTP/1.1, your configuration is missing http2 — add it in listen 443 ssl http2; inside the server block.

To inspect the certificate itself:

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates -issuer

You’ll see notBefore and notAfter (90-day validity) and the issuer “Let’s Encrypt”.

Basic tuning for production

The default Nginx config works, but three parameters are worth adjusting before taking real traffic. Edit /etc/nginx/nginx.conf:

  • worker_processes auto; — uses one worker per CPU core automatically. Already set to auto in recent versions, just confirm.
  • worker_connections 4096; — raises the simultaneous connection limit per worker (the default 768 is conservative). On small VPS with little RAM, keep 1024.
  • keepalive_timeout 30; — dropping from 65s to 30s frees workers faster on short HTTP workloads.

After editing, run sudo nginx -t && sudo systemctl reload nginx.

Gzip and static asset caching

On sites with heavy CSS/JS, enable gzip on; (already active on Ubuntu) and add expires 30d; inside a location block for static files. Combined with HTTP/2, it cuts load time by 30-50% without changing anything in the application.

Next steps

With Nginx up and serving HTTPS, common next moves:

  • Reverse proxy for Node/Python/Go — replace try_files with proxy_pass http://127.0.0.1:3000; pointing to your application running on an internal port.
  • HTTP/3 (QUIC) — requires Nginx 1.25+ compiled with proper TLS support; evaluate whether the latency gain justifies leaving the official Ubuntu package.
  • Rate limitinglimit_req_zone protects against abuse on sensitive endpoints (login, public APIs).
  • Structured logs (JSON) — makes ingestion into observability tools easier. Configure it via a custom log_format.
  • Backup of /etc/nginx/ — versioning your configs in a private Git repository avoids hours of rebuilding after a reinstall.

If you are putting this in production, a Hostini VPS ships with Ubuntu LTS, a dedicated IPv4 and a KVM console in the panel — useful when you lock yourself out editing firewall rules and need to recover access without opening a ticket.

Frequently asked questions

What's the difference between Nginx from apt and Nginx from the official nginx.org repository?

The Ubuntu package (`nginx`) is stable and well integrated with systemd, but it usually trails the upstream by 1-2 minor versions. The official nginx.org repository ships newer releases (mainline or stable) with additional modules and faster security updates. For production without specific requirements, the Ubuntu package is enough. For HTTP/3, recent dynamic modules, or new TLS 1.3 features, prefer the official repository.

Do I need to stop Apache before installing Nginx?

Yes, if Apache is running and listening on port 80 or 443. Run `sudo systemctl stop apache2 && sudo systemctl disable apache2` before starting Nginx — two applications cannot bind to the same port. If you want to keep both, configure Apache on an internal port (8080) and use Nginx as a reverse proxy in front.

Where does Nginx write the error log when something breaks?

By default in `/var/log/nginx/error.log` (global) and in a per-server-block `error_log` if you configured one. For startup failures that never reach the log file, use `sudo journalctl -u nginx -n 50` — it captures stderr from the process before Nginx opens its own log files.

How do I reload the configuration without restarting Nginx and dropping connections?

Use `sudo systemctl reload nginx` (or `sudo nginx -s reload`). The master process validates the new config, spawns new workers with it and only kills the old ones after their in-flight requests finish. A full restart is only required for binary or module changes.

Why does Certbot fail with 'unauthorized' when the domain points correctly?

It's almost always a DNS or firewall issue. Let's Encrypt needs to send an HTTP GET to your IP on port 80 from their servers. Check: (1) the A/AAAA record points to the VPS IP — `dig +short yourdomain.com`; (2) port 80 is open in UFW AND in the provider's firewall; (3) no proxy/CDN sits between the validation and your server (temporarily disable Cloudflare proxy).

Is it worth enabling HTTP/2 or HTTP/3 on Nginx?

HTTP/2 yes, always — enable it with `listen 443 ssl http2;`. It reduces latency on sites with many assets through multiplexing over a single TCP connection. HTTP/3 (QUIC) helps on unstable networks (mobile, lossy links) but requires Nginx 1.25+ compiled with BoringSSL or quictls — the default Ubuntu package does not ship it. Evaluate case by case.

Topics:
Next steps Ryzen cloud with NVMe storage and always-on DDoS protection.Go live on a Hostini VPS →
Was this tutorial helpful?
Chat on WhatsApp