Sign In
Sign In

How to Install Nginx on Ubuntu: Step-by-Step Guide

How to Install Nginx on Ubuntu: Step-by-Step Guide
Hostman Team
Technical writer
Nginx
23.11.2023
Reading time: 7 min

Nginx is one of the most popular open-source web servers. It is often used as a web server, reverse proxy, or mail proxy.

This article will describe how to install Nginx on Ubuntu and make its basic configuration.

Installing Nginx

You will need a local machine or a cloud server with the Ubuntu operating system installed to install the Nginx server. 

Nginx is available in the official Ubuntu repositories, so you can install it using the apt package management system.

First, you need to update the package lists from the repositories:

sudo apt update

After the update is finished, you can install Nginx on the machine:

sudo apt install nginx

Wait for the installation to finish, and then use this command to start up the service at boot:

sudo systemctl enable nginx

Now check that the web server is running and configured to start at boot. Check the status of the web server:

sudo service nginx status

The line "Active: active (running)..." indicates the successful operation of the server. 

There is another way to check it. Paste the server's IP address into the browser's address bar. If the result is the same as in the picture below, the web server is running.

1a2ea369 28cc 4095 A670 374bc0be5629

Now let's check that it's configured to start at boot:

sudo systemctl is-enabled nginx

If the output shows "enabled", the web server has been added to startup.

Below are the basic commands to manage your web server.

Action

Command

Start

sudo systemctl start nginx

Shutdown

sudo systemctl stop nginx

Restart

sudo systemctl restart nginx

Reload

sudo systemctl reload nginx

Status check

sudo systemctl status nginx

Configuration testing

sudo nginx -t

Firewall settings

Installing and configuring a firewall will allow you to close all ports except those that we need: 22 (SSH), 80 (HTTP), 443 (HTTPS). The first one is required to connect to a remote server. The second and third are necessary for communication between the client and the site. 

Install UFW:

sudo apt install ufw

Then add the web server to the available applications list:

sudo nano /etc/ufw/applications.d/nginx.ini

Let's fill the file like this:

[nginx HTTP]
title=Web Server
description=Enable NGINX HTTP traffic
ports=80/tcp

[nginx HTTPS]\
title=Web Server (HTTPS) \
description=Enable NGINX HTTPS traffic
ports=443/tcp

[nginx full]
title=Web Server (HTTP,HTTPS)
description=Enable NGINX HTTP and HTTPS traffic
ports=80,443/tcp

Check the list of available applications:

sudo ufw app list

If there is a web server among them, everything is done correctly. Now you need to start the firewall and allow traffic on the ports we mentioned above:

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

To check the changes, enter the command:

sudo ufw status

The output should list all the ports we need.

-

Setting up Nginx

Web server administration is the modification and maintenance of configuration files. Among them are one configuration file and two directories. These are nginx.conf, sites-available, and sites-enabled, respectively. All of them are in the /etc/nginx directory.

The nginx.conf file is the main configuration file. The sites-available directory contains virtual host configuration files. Each file stores a specific site's name, IP address, and other data. The sites-enabled directory, in turn, consists of active site configurations only. Only the sites-enabled directory reads configuration files for virtual hosts. It also stores links to the sites-available. This structure allows you to temporarily disable sites without losing their configurations.

Let's take a closer look at the main configuration file. To do this, open it using the editor:

sudo nano /etc/nginx/nginx.conf

After executing the command, a file divided into modules will open. By default, it looks like in the image below:

F6aa1f6e E360 4ab1 8333 D4ff31ff9c76

Each module is a directive responsible for specific web server settings. There are simple directives and block ones. In addition to the name and parameters, block directives store additional instructions placed inside curly brackets.

Let's list some of the directives of the main configuration file:

  • user is the user that runs all the worker processes.

  • worker_processes is the number of server worker processes. It shouldn't exceed the number of processor cores. The auto option will set the number automatically.

  • pid is a file showing the main process's ID.

  • include is responsible for including other configuration files matching the specified mask.

  • events consists of directives that manage the network connection.

    • worker_connections is the maximum number of concurrent connections for a single worker process.

    • multi_accept is a flag that can be either enabled (on) or disabled (off). If enabled, the worker process will accept all new connections; otherwise, only one.

    • use specifies the connection handling method. By default, the server chooses the most efficient.

  • http consists of directives responsible for the operation of the HTTP server.

    • sendfile enables (on) or disables (off) the sendfile() data sending method.

    • tcp_nopush, tcp_nodelay are parameters that affect the performance. The first forces the server to send HTTP response headers in one packet, and the second allows you not to buffer the data and send it in short bursts.

    • keepalive_timeout is responsible for the keep-alive connection timeout before the server terminates it.

    • keepalive_requests is the maximum number of requests in one keep-alive connection.

    • error_log is a web server error log. To collect errors for a specific section (http, server, etc.), you need to place the directive inside that section.

    • gzip is for content compression.

Setting up virtual hosts

A server can host multiple sites. All requests come to its IP address, and the web server determines how to respond, depending on the domain. Virtual hosts ensure that the server understands what data belongs to which domain. 

For example, we'll create the site testsite.dev.

Let's create a folder for the site:

sudo mkdir -p /var/www/testsite.dev/html

Then add the index file:

sudo nano /var/www/testsite.dev/html/index.html

Let's fill it with the basic data needed to display the site:

<!DOCTYPE html>
<html lang="en">
<head>
     <title>testsite.dev</title>
     <metacharset="utf-8">
</head>
<body>
     <h1>Hello, user</h1>
</body>
</html>

Then we'll create a site configuration file in the sites-available folder:

sudo nano /etc/nginx/sites-available/testsite.dev.conf

Let's fill it with the simplest configuration:

server {
     listen 80;
     listen[::]:80;
     server_name testsite.dev www.testsite.dev;
     root /var/www/testsite.dev/html;
     index index.html index.xml;
}

The last thing to do is create a link in the sites-enabled directory to the testsite.dev site configuration, so it is added from available to enabled:

sudo ln -s /etc/nginx/sites-available/testsite.dev.conf /etc/nginx/sites-enabled/

Now let's test the configuration:

sudo nginx -t

Disable the default site by deleting the default virtual host entry:

sudo rm /etc/nginx/sites-enabled/default

It is worth clarifying that after we disable the default site, Nginx will use the first server block it encounters as a fallback site (that is, the very first site from the Nginx configuration will open at the server IP address).

Restart the web server:

sudo systemctl restart nginx

Let's check that the site works. To do this, you can paste the server IP address or domain, if it is registered, into the address bar of the browser:

Ac149b6e 120e 490f 9e60 509ad3549c76

Another option is to use the curl command:

A469a009 221f 4f35 9628 F6b555d2e085

Conclusion

In this article, we have shown the process of installing Nginx on Linux, namely on the Ubuntu distribution. 

Using this guide you can set up the web server and deploy your first website. We have also prepared the server to use the encrypted HTTPS protocol. Remember that to set up a secure connection, you will need an SSL certificate. 

Nginx
23.11.2023
Reading time: 7 min

Similar

Nginx

Nginx Location Directive Examples

A useful tool for controlling the handling of several kinds of requests is the Nginx location directive. Specifying rules in the location directive lets you apply particular setups, divert requests to different areas of your application, or altogether refuse some requests. Using the Nginx location directive, you can maintain effective request management, which is important for improving web applications efficiency, security, and functionality. Nginx Location Directive Basic Syntax The Nginx location directive is defined in the configuration file nginx.conf. Below you can see its basic syntax: location [modifier] uri { ... } modifier: An optional parameter that defines the type of match (e.g., exact, prefix, regex). uri: The URI to be matched. Inside the curly braces, you can define various settings and instructions (e.g., proxy_pass, root, index). Exact Match Example An exact match occurs when the requested URI exactly matches the specified string. location = /exact-match { root /var/www/html; index index.html; } In this example, only requests to /exact-match will be processed by this location block. You can incorporate a condition using an if statement as well. Keep in mind that Nginx does not have a direct counterpart to the conventional if-else statement seen in various programming languages. However, similar functionality can be achieved by combining multiple if statements with other directives to simulate conditional branching. location = /exact-match { root /var/www/html; index index.html; if ($http_user_agent ~* "Chrome") { add_header X-Browser "Chrome"; } } In this case, if the user agent is identified as Chrome, the response will include an extra X-Browser header. Prefix Match Example A prefix match is the most common type and matches any URI that starts with the specified string. location /prefix { root /var/www/html; index index.html; if ($request_method = POST) { return 405; # Method Not Allowed } } This location block will handle any request beginning with /prefix—like /prefix/page1 or /prefix/page2. Should the request method be POST, Nginx results in a 405 Method Not Allowed status. Regular Expression Match Example For more difficult matching scenarios, regular expressions are useful. In Nginx, the regular expression match may contain wildcards — characters that fit any character or collection of characters. Common wildcards in Nginx regular expressions are: .: Matches any single character. .*: Matches any sequence of characters (including no characters). ^: Matches the beginning of a string. $: Matches the end of a string. location ~* \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; if ($request_uri ~* "/admin") { return 403; # Forbidden } if ($request_uri !~* "/admin") { add_header X-Admin "Not Admin"; } } In this example: The ~* modifier indicates a case-insensitive regular expression match. The pattern \.php$ matches any URI ending with .php (e.g., index.php, test.PHP). If the requested URI contains /admin, Nginx returns a 403 Forbidden status. If the requested URI does not contain /admin, an X-Admin header is added indicating "Not Admin". Case Insensitive Match Example You can use regular expression with the ~* modifier for case-insensitive matching. location ~* \.jpg$ { root /var/www/images; if ($http_referer !~* "^https?://(www\.)?example\.com") { return 403; # Forbidden } } This matches any URI ending with .jpg, .JPG, .Jpg, etc., and only serves the image files if the referer is from example.com. Otherwise, it returns a 403 Forbidden status. Managed solution for Backend development Priority and Order The priority and order of location blocks are critical for correctly processing requests. Nginx follows these rules: Exact match (=): These have the highest priority. Regular expressions (~ or ~*): Evaluated in the order they are defined in the configuration file. Prefix matches without modifier: These have the lowest priority, but among them, the longest prefix has the highest priority. Example location = /exact { # highest priority } location ~* \.jpg$ { # lower priority than exact match } location / { # lowest priority } Nesting Location Blocks Nginx allows nesting of location blocks within other location blocks for more granular control. location /nested { location /nested/subnested { root /var/www/html; } root /var/www/html; } In this example, requests to /nested/subnested will be processed by the inner location block, while /nested (but not /nested/subnested) will be processed by the outer location block. Real Web Server Example Let's consider a practical example of configuring a local web server with multiple location blocks. server { listen 80; server_name localhost; # Root location block location / { root /var/www/html; index index.html; } # Exact match for a specific page location = /about { root /var/www/html; index about.html; } # Prefix match for an API endpoint location /api { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Regular expression match for PHP files location ~ \.php$ { root /var/www/html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; } # Case insensitive match for image files location ~* \.(jpg|jpeg|png|gif|ico)$ { root /var/www/images; } # Nested location blocks for admin section location /admin { root /var/www/admin; index index.html; location /admin/stats { proxy_pass http://localhost:8080/stats; } } } In this configuration: The root location serves static files from /var/www/html. Requests to /about are served by the exact match block pointing to about.html. The /api prefix is proxied to a backend service running on port 3000. PHP files are processed by a FastCGI server. Image files are served from /var/www/images. Nested location blocks are used to handle /admin and /admin/stats with different settings. Nginx main configuration file contains the core configuration directives for the Nginx server. Nginx config file location is usually /etc/nginx/nginx.conf. After changing the Nginx configuration, you must reload the service to apply the modification. This can be done without dropping active connections by running: sudo systemctl reload nginx Testing and Debugging Location Blocks It's important to verify that each location block works as intended before deploying or relying on a NGINX configuration. We will provide simple ways to test your setup, catch misconfigurations early, and troubleshoot common issues using built-in tools like curl and log files. Check configuration syntax before applying changes this way: sudo nginx -t Next, reload NGINX to apply changes without dropping active connections: sudo systemctl reload nginx Test location behavior with curl and send requests to check if the correct location block responds: curl -I http://localhost/about curl -X POST http://localhost/prefix/form curl -H "User-Agent: Chrome" http://localhost/exact-match Check for status codes, headers, and content. View request handling and errors in real-time: tail -f /var/log/nginx/access.log tail -f /var/log/nginx/error.log Let's write down a location block troubleshooting checklist. Ran nginx -t? Make sure the config syntax is valid. Reloaded NGINX? Remember to reload after changes. Does the request match exactly? Confirm the requested URI matches the block as expected (consider trailing slashes). Is block priority correct? Check if a higher-priority block is taking over. Does the root path exist? Ensure root or alias points to a valid directory/file. Used curl or browser tools? Inspect request/response details to trace behavior. Have you checked the logs? Look for access and error log entries related to your request. Have you tried isolating the block? Temporarily comment out other blocks to confirm which one is handling the request. Sensitive Locations Access Restriction To keep things safe, limiting who can access private routes like /admin, /config, or internal tools is smart. The allow and deny directives in NGINX let you control entry based on an IP address. location /admin { allow 192.168.1.100; deny all; root /var/www/admin; index index.html; } The /admin path can only be reached by the client at 192.168.1.100 in this case. A 403 Forbidden answer will be sent to any other IP that tries to reach it. You can also allow multiple addresses or ranges and deny the rest. This method is simple and effective for securing access without relying on authentication or external tools. Understanding root vs alias in Location Blocks Depending on how your location block is set up, you need to use the right directive when sending static files. This could be root or alias. Misusing these can cause NGINX to fail to locate files correctly. Here’s a quick comparison: Directive Behavior Example URI Config Path Resulting File Path root Appends URI after match /static/css/style.css location /static/ { root /var/www; } /var/www/static/css/style.css alias Replaces matched URI prefix /static/css/style.css location /static/ { alias /var/www/assets/; } /var/www/assets/css/style.css Example using alias, where the request to /files/manual.pdf will serve the file at /data/downloads/manual.pdf: location /files/ { alias /data/downloads/; } Note: When you use alias, always add a backslash (/) to the end of the path if your address ends in /. This will keep the paths from not matching.  Conclusion A solid understanding of the location directive is essential to refine the management of the request handling. This includes an understanding of different matches — exact (=), prefix (/), and regular expression-based (~, ~*) — and how Nginx prioritizes them. Using them right can significantly impact performance and behavior of your website. You can also nest location blocks to apply more targeted rules within broader contexts, giving you tight control over how different URLs are processed. Mastering these patterns helps build a configuration that's both clean and highly efficient.
17 April 2025 · 9 min to read
Nginx

NGINX Access and Error Logs

When working with various applications, it is often necessary to refer to logs that record and store various data, ranging from debugging information to problem occurrences. With Nginx, users can also work with event logs that help resolve different issues.  Nginx uses two types of logs: Access Log records all requests that are received and processed through Nginx. Error Log records various errors, including configuration errors for both Nginx and third-party services (such as php-fpm). Today, we will explore the structure of the access logs and error logs in Nginx. Prerequisites To work with Nginx logs, we will need the following: One server or virtual machine with any pre-installed Linux distribution: Ubuntu, Debian, RHEL, CentOS, and many others. This article will use Ubuntu 22.04. An installed Nginx web server. You can install Nginx on Ubuntu by following this guide. In this article, we will use Nginx 1.18.0 installed from the Ubuntu distribution repositories. Access Log As mentioned earlier, the access_log is used to record all requests from clients. Each time a request from a client is received, Nginx writes this request into the access log. The saved entry contains a timestamp and client information, including the requested resource’s address, client address, and more. By default, the access log is written to the file access.log located at /var/log/nginx. A typical log entry from the access log has the following format: 166.1.227.189 - - [20/Nov/2024:13:52:04 +0300] "GET /favicon.ico HTTP/1.1" 404 197 "http://91.206.179.176/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" Where: 166.1.227.189: Client's IP address that made the request. -: Client identifier. If a dash is returned, it means identd authentication is not used. -: Username. If a dash is returned, it means that authentication on the requested resource is either not required or was not successful. [20/Nov/2024:13:52:04 +0300]: Timestamp of when the request was made: date, time, and timezone. GET /favicon.ico HTTP/1.1: HTTP request string. It consists of: GET: The method used to access the requested resource. /favicon.ico: The requested resource. HTTP/1.1: The version of the HTTP protocol used. 404: HTTP response code. In this case, the 404 code means the requested resource was not found. 197: The size of the transmitted response in bytes (including headers and body). http://91.206.179.176/: HTTP referer (contains the URL of the request's source). Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36: Client's User-Agent (contains information about the browser and operating system). You can configure the access log at the web server level by setting the necessary options in the nginx.conf file or separately for each website by using its configuration file in the /etc/nginx/sites-available directory (if Nginx was installed from the operating system's repositories) or in the /etc/nginx/conf.d directory. Two parameters are used for configuring the access log: log_format: Specifies the log format and the data to be logged. It uses many built-in variables (which will be covered later). access_log: Specifies the location of the access log file. By default, it uses the path /var/log/nginx/access.log. Here’s an example of configuring the access.log in the main configuration file /etc/nginx/nginx.conf: log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent"'; access_log /var/log/nginx/access.log main; access_log /var/log/nginx/access.log main; In this example, we use the following variables: $remote_addr: Records the client's IP address. $remote_user: Records the username passed during authentication (if authentication is enabled on the site). $time_local: Records the local time when the request was made. $request: Records the HTTP request string (including method, URI, and protocol version). $status: Records the HTTP response status. $body_bytes_sent: Records the number of bytes in the response body (excluding headers). $http_referer: Records the HTTP Referer header (the page address from which the request was made). $http_user_agent: Records the User-Agent header (information about the browser/client). Refer to the Nginx documentation for more detailed information on all built-in variables. Here’s the full content of the nginx.conf file: user www-data; worker_processes auto; pid /run/nginx.pid; include /etc/nginx/modules-enabled/*.conf; events { worker_connections 768; # multi_accept on; } http { ## # Basic Settings ## sendfile on; tcp_nopush on; types_hash_max_size 2048; # server_tokens off; # server_names_hash_bucket_size 64; # server_name_in_redirect off; include /etc/nginx/mime.types; default_type application/octet-stream; ## # SSL Settings ## ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE ssl_prefer_server_ciphers on; ## # Logging Settings ## log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent"'; access_log /var/log/nginx/access.log; ## # Gzip Settings ## gzip on; # gzip_vary on; # gzip_proxied any; # gzip_comp_level 6; # gzip_buffers 16 8k; # gzip_http_version 1.1; # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } Error Log For recording various types of errors, there is a second log in Nginx: error_log. This log records errors such as: Configuration errors Problems with connecting to backend servers Errors related to file access/permissions Internal server errors (5xx) SSL-related issues By default, the error log is written to the error.log file located at /var/log/nginx. A typical log entry from the error.log file looks like this: 20/Nov/2024:14:00:00 [error] 1234#5678: *10 open() "/var/www/html/test-page.html" failed (2: No such file or directory), client: 127.0.0.1, server: my-local-server.com, request: "GET /test-page.html HTTP/1.1", host: "example.com" Where: 20/Nov/2024:14:00:00: The date and time when the error occurred. [error]: The error level. It can have values like [error], [info], [warn], [crit], and others. We’ll cover the logging levels in the next chapter. 1234#5678: The process and thread identifier. open() "/var/www/html/test-page.html" failed (2: No such file or directory): Error description. In this case, the request was made for a non-existent file (page) named test-page.html. client: 127.0.0.1: The IP address of the client who made the request. In this example, the request was sent from the local machine (localhost). server: my-local-server.com: The server name to which the request was sent. request: "GET /test-page.html HTTP/1.1": The HTTP request string. It consists of: GET: The method used to access the requested resource. /test-page.html: The requested resource. HTTP/1.1: The version of the HTTP protocol used. host: "example.com": The host name to which the request was sent. You can configure the error log in the same way as the access log by setting the necessary options in the nginx.conf file at the web server level or separately for each website. Error log parameters: log_format: Specifies the log format and the data to be logged.  error_log: Specifies the path to the error log. Here’s an example of configuring the error.log, which should be included in the main configuration file for the web server nginx.conf: log_format error '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" "$gzip_ratio"'; error_log /var/log/nginx/access.log main; Let's look at the variables in more detail: $remote_addr: Records the client's IP address. $remote_user: Records the username passed during authentication (if authentication is enabled on the site). $time_local: Records the local time, i.e., the time when the request was made. $request: Records the HTTP request string (including method, URI, and protocol version). $status: Records the HTTP response status. $body_bytes_sent: Records the number of bytes in the response body (excluding headers). $http_referer: Records the HTTP Referer header (the address of the page from which the request was made). $http_user_agent: Records the User-Agent header (information about the browser/client). $gzip_ratio: Records the Gzip compression ratio (if Gzip compression is enabled in Nginx settings). Here’s the full content of the nginx.conf file: user www-data; worker_processes auto; pid /run/nginx.pid; include /etc/nginx/modules-enabled/*.conf; events { worker_connections 768; # multi_accept on; } http { ## # Basic Settings ## sendfile on; tcp_nopush on; types_hash_max_size 2048; # server_tokens off; # server_names_hash_bucket_size 64; # server_name_in_redirect off; include /etc/nginx/mime.types; default_type application/octet-stream; ## # SSL Settings ## ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE ssl_prefer_server_ciphers on; ## # Logging Settings ## log_format error '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" "$gzip_ratio"'; error_log /var/log/nginx/access.log error; ## # Gzip Settings ## gzip on; # gzip_vary on; # gzip_proxied any; # gzip_comp_level 6; # gzip_buffers 16 8k; # gzip_http_version 1.1; # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } Logging Levels in Nginx Logs Nginx has eight log levels, which apply to both the access log and the error log: debug: Intended for recording the most detailed information, including the internal mechanisms of Nginx. It logs information about each request and module. This level is used for diagnosing complex issues, especially during development or configuration. info: Used for logging informational messages about the server’s operation. These messages might include, for example, information about starting or stopping server processes. notice: Indicates important but non-critical events. These messages help understand how the system is operating but do not require intervention from the web server administrator. An example would be a configuration update. warn: Logs warnings that are not critical but could potentially lead to issues in the future. error: Reports problems that caused the failure of specific requests but do not affect the overall operation of the server. For instance, this could include being unable to open a file or connect to a backend. crit: Indicates critical errors that may disrupt the normal server operation. For example, this could include a module failure or resource shortage. alert: Reports serious issues that require immediate attention. emerg: The highest logging level. Indicates fatal errors that prevent the server from continuing to function. These messages require immediate correction. Conclusion Using Nginx access and error logs can significantly simplify troubleshooting and debugging issues. Nginx logging can be flexibly configured to record only the necessary data. You can also configure logging for each website individually or enable logging at the web server level for all sites.
12 February 2025 · 10 min to read
Nginx

How to Set Up Load Balancing with Nginx

Modern applications can handle many requests simultaneously, and even under heavy load, they must return correct information to users. There are different ways to scale applications: Vertical Scaling: add more RAM or CPU power by renting or purchasing a more powerful server. It is easy during the early stages of the application’s development, but it has drawbacks, such as cost and the limitations of modern hardware. Horizontal Scaling: add more instances of the application. Set up a second server, deploy the same application on it, and somehow distribute traffic between these instances. Horizontal scaling, on the one hand, can be cheaper and less restrictive in terms of hardware. You can simply add more instances of the application. However, now we need to distribute user requests between the different instances of the application. Load Balancing is the process of distributing application requests (network traffic) across multiple devices. A Load Balancer is a middleware program between the user and a group of applications. The general logic is as follows: The user accesses the website through a specific domain, which hides the IP address of the load balancer. Based on its configuration, the load balancer determines which application instance should handle the user's traffic. The user receives a response from the appropriate application instance. Load Balancing Advantages Improved Application Availability: Load balancers have the functionality to detect server failures. If one of the servers goes down, the load balancer can automatically redirect traffic to another server, ensuring uninterrupted service for users. Scalability: One of the main tasks of a load balancer is to distribute traffic across multiple instances of the application. This enables horizontal scaling by adding more application instances, increasing the overall system performance. Enhanced Security: Load balancers can include security features such as traffic monitoring, request filtering, and routing through firewalls and other mechanisms, which help improve the application's security. Using Nginx for Network Traffic Load Balancing There are quite a few applications that can act as a load balancer, but one of the most popular is Nginx.  Nginx is a versatile web server known for its high performance, low resource consumption, and wide range of capabilities. Nginx can be used as: A web server A reverse proxy and load balancer A mail proxy server And much more. You can learn more about Nginx's capabilities on its website. Now, let's move on to the practical setup. Installing Nginx on Ubuntu Nginx can be installed on all popular Linux distributions, including Ubuntu, CentOS, and others. In this article, we will be using Ubuntu. To install Nginx, use the following commands: sudo apt updatesudo apt install nginx To verify that the installation was successful, you can use the command: systemctl status nginx The output should show active (running). The configuration files for Nginx are located in the /etc/nginx/sites-available/ directory, including the default file that we will use for writing our configuration. Example Nginx Configuration First, we need to install nano: sudo apt install nano Now, open the default configuration file: cd /etc/nginx/sites-available/sudo nano default Place the following configuration inside: upstream application { server 10.2.2.11; # IP addresses of the servers to distribute requests between server 10.2.2.12; server 10.2.2.13; } server { listen 80; # Nginx will open on this port location / { # Specify where to redirect traffic from Nginx proxy_pass http://application; } } Setting Up Load Balancing in Nginx To configure load balancing in Nginx, you need to define two blocks in the configuration: upstream — Defines the server addresses between which the network traffic will be distributed. Here, you specify the IP addresses, ports, and, if necessary, load balancing methods. We will discuss these methods later. server — Defines how Nginx will receive requests. Usually, this includes the port, domain name, and other parameters. The proxy_pass path specifies where the requests should be forwarded. It refers to the upstream block mentioned earlier. In this way, Nginx is used not only as a load balancer but also as a reverse proxy. A reverse proxy is a server that sits between the client and backend application instances. It forwards requests from clients to the backend and can provide additional features such as SSL certificates, logging, and more. Load Balancing Methods Round Robin There are several methods for load balancing. By default, Nginx uses the Round Robin algorithm, which is quite simple. For example, if we have three applications (1, 2, and 3), the load balancer will send the first request to the first application, then the second request to the second application, the third request to the third application, and then continue the cycle, sending the next request to the first one again. Let’s look at an example. I have deployed two applications and configured load balancing with Nginx for them: upstream application { server 172.25.208.1:5002; # first server 172.25.208.1:5001; # second } Let’s see how this works in practice: The first request goes to the first server. The second request goes to the second server. Then it goes back to the first server, and so on. However, this algorithm has a limitation: backend instances may be idle simply because they are waiting for their turn. Round Robin with Weights To avoid idle servers, we can use numerical priorities. Each server gets a weight, which determines how much traffic will be directed to that specific application instance. This way, we ensure that more powerful servers will receive more traffic. In Nginx, the priority is specified using server weight as follows: upstream application { server 10.2.2.11 weight=5; server 10.2.2.12 weight=3; server 10.2.2.13 weight=1; } With this configuration, the server at address 10.2.2.11 will receive the most traffic because it has the highest weight. This approach is more reliable than the standard Round Robin, but it still has a drawback. We can manually specify weights based on server power, but requests can still differ in execution time. Some requests might be more complex and slower, while others are fast and lightweight. upstream application { server 172.25.208.1:5002 weight=3; # first server 172.25.208.1:5001 weight=1; # second } Least Connections What if we move away from Round Robin? Instead of simply distributing requests in order, we can base the distribution on certain parameters, such as the number of active connections to the server. The Least Connections algorithm ensures an even distribution of load between application instances by considering the number of active connections to each server. To configure it, simply add least_conn; in the upstream block: upstream application { least_conn; server 10.2.2.11; … } Let’s return to our example. To test how this algorithm works, I wrote a script that sends 500 requests concurrently and checks which application each request is directed to. Here is the output of that script: Additionally, this algorithm can be used together with weights for the addresses, similar to Round Robin. In this case, the weights will indicate the relative number of connections to each address — for example, with weights of 1 and 5, the address with a weight of 5 will receive five times more connections than the address with a weight of 1. Here’s an example of such a configuration: upstream application { least_conn; server 10.2.2.11 weight=5; … } nginx upstream loadbalancer { least_conn; server 172.25.208.1:5002 weight=3; # first server 172.25.208.1:5001 weight=1; # second } And here’s the output of the script: As we can see, the number of requests to the first server is exactly three times higher than to the second. IP Hash This method works based on the client’s IP address. It guarantees that all requests from a specific address will be routed to the same instance of the application. The algorithm calculates a hash of the client’s and server’s addresses and uses this result as a unique key for load balancing. This approach can be useful in blue-green deployment scenarios, where we update each backend version sequentially. We can direct all requests to the backend with the old version, then update the new one and direct part of the traffic to it. If everything works well, we can direct all users to the new backend version and update the old one. Example configuration: upstream app { ip_hash; server 10.2.2.11; … } With this configuration, in our example, all requests will now go to the same application instance: Error Handling When configuring a load balancer, it's also important to detect server failures and, if necessary, stop directing traffic to "down" application instances. To allow the load balancer to mark a server address as unavailable, you must define additional parameters in the upstream block: failed_timeout and max_fails. failed_timeout: This parameter specifies the amount of time during which a certain number of connection errors must occur for the server address in the upstream block to be marked as unavailable. max_fails: This parameter sets the number of connection errors allowed before the server is considered "down." Example configuration: upstream application { server 10.2.0.11 max_fails=2 fail_timeout=30s; … } Now, let's see how this works in practice. We will "take down" one of the test backends and add the appropriate configuration. The first backend instance from the example is now disabled. Nginx redirects traffic only to the second server. Comparative Table of Traffic Distribution Algorithms Algorithm Pros Cons Round Robin Simple and lightweight algorithm. Evenly distributes load across applications. Scales well. Does not account for server performance differences. Does not consider the current load of applications. Weighted Round Robin Allows setting different weights for servers based on performance. Does not account for the current load of applications. Manual weight configuration may be required. Least Connection Distributes load to applications with the fewest active connections. Can lead to uneven load distribution with many slow clients. Weighted Least Connection Takes server performance into account by focusing on active connections. Distributes load according to weights and connection count. Manual weight configuration may be required. IP Hash Ties the client to a specific IP address. Ensures session persistence on the same server. Does not account for the current load of applications. Does not consider server performance. Can result in uneven load distribution with many clients from the same IP. Conclusion In this article, we explored the topic of load balancing. We learned about the different load balancing methods available in Nginx and demonstrated them with examples.
18 November 2024 · 9 min to read

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start.
Email us
Hostman's Support