Contents
Introduction
Good afternoon. Today I want to tell you about working with Nginx Unit in simple words. Let's talk about what NGINX UNIT is, how to work with it, what problems may arise. I won't drag on, so let's get to the review :)
What is Nginx Unit?
NGINX Unit is a new web server released in 2017 by the developers of the original NGINX, for running web applications. The web server is open source and is available on Github.
NGINX UNIT is dynamic and does not require rebooting the web server to apply configuration files due to the use of REST APIs to interact with it.
On NGINX UNIT you can configure the server to use static files (e.g. run a one-page website or a built React application) or use one of the built-in modules to run applications in Python, PHP, NodeJS, Java and other languages. You can see the full list on the official website.
Those who want to try the web server can follow the article and thus learn how to work with it.
Installation
For the example, we will install Nginx Unit latest version from the official repository on a virtual machine on Ubuntu 22.04. If you are using a different OS, a guide can be found here.
Downloading and saving the Nginx Unit keys
sudo curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg
Adding a repository. It is needed to locate the package on the Nginx Unit servers. To do this, you need to create a file at the path /etc/apt/sources.list.d/unit.list
. This can be done by opening the file with one of the editors in Ubuntu (vim, nano and so on). In the example I will use the nano text editor. By executing the command below we will create a new file.
nano /etc/apt/sources.list.d/unit.list
Next, paste in there the strings to define the repository. These are different for each version of Ubuntu, so if your version of Ubuntu is different from the example (Ubuntu 22.04), they can be found on the official website at the link.
Save the file, close it and update the system's package list with the apt update
command (The command updates the Linux system's package index or package lists).
apt update
Once the update is complete install Nginx Unit with the apt install <package name>
command (The apt install
command finds the package in the operating system package index and downloads them along with their dependencies).
apt install unit
You can read about apt (apt-get) here.
After the installation we execute the command to restart the Nginx Unit service in the operating system:
systemctl restart unit
You can check the installation with the command:
unitd --version
This completes the installation. As you can see, Nginx Unit is installed version 1.30 (the latest version at the time of writing). Also below the version is the configuration command that you may need to build Nginx Unit from source.
Other installation methods
Configuration
Once the Nginx Unit is installed we will use the config key to work with it. This part of the web server API is responsible for configuration and contains: listeners, routes, applications and load balancers.
{
"listeners": {
// ...
},
"routes": [
// ...
],
"applications": {
// ...
},
"upstreams": {
// ...
}
}
To load the configuration we will access the API via socket.
To make it easier to store your results, I recommend creating JSON files. They are easy to store and you won't need to upload the config file all the time. Using the example of JSON files in the example I will update Nginx Unit settings. The file name for the example file will be config.json, and the command to upload and update config.
curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config
Paths to load into config
Listeners
/config/listeners
.Routes
/config/routes
Applications
/config/applications
Listeners
Listeners is the block that is responsible for incoming traffic. The web server can listen for Ip addresses and port. For example 127.0.0.1:8080
for use within the server or *:8080
to receive traffic coming to the server from a client application for example. (On Linux systems you can use abstract sockets. Example unix@abstract_socket
)
To get all listeners, execute a socket command to access the API. Example command below:
curl -X GET --unix-socket /var/run/control.unit.sock http://localhost/config/listeners
{ // The object we get in the response
"*:8080": { // Port 8080 Listener
"pass": "routes" // Using routing for the listener
}
}
The command makes a GET request to the socket using the --unix-socket
flag which points to a running Nginx Unit. http://localhost/config/listeners
.
Routes
To get all the routes, make an API call through the socket command:
curl -X GET --unix-socket /var/run/control.unit.sock http://localhost/config/routes
If the routes are empty, you will get an error as in the screenshot below.
Routes can be an array of parameters or an array of keys.
An example that performs all actions from the array when the listener accesses routes
{
"listeners": {
"*:80": {
"pass": "routes"
}
},
"routes": [
{
"action": {
"share": "/var/www/example$uri"
}
}
]
}
Specify keys for the array so that listeners can filter traffic:
{
"listeners": {
"*:80": {
"pass": "routes/http"
},
"*:443": {
"pass": "routes/https",
"tls": {
"certificate": "bundle"
}
}
},
"routes": {
"http": {
"action": {
"share": "/var/www/example$uri"
}
},
"https": {
"action": {
"share": "/var/www/example_secure$uri"
}
},
}
}
Applications and language modules
List of supported programming languages for running applications
Go
JavaScript (Node.js)
Java
Perl
PHP
Python
Ruby
Compiled binaries are also supported. (This will not be covered in this article.)
To get all applications, make an API call via the socket command:
curl -X GET --unix-socket /var/run/control.unit.sock http://localhost/config/applications
PHP application example. It runs 20 processes on port 80 using a PHP module. In order to install it you need to run the command:
apt install unit-php
{
"listeners": {
"*:80": {
"pass": "routes"
}
},
"applications": {
"blogs": {
"type": "php",
"processes": 20,
"root": "/var/www/blogs/scripts/"
}
}
}
This was the simplest example, but every application has additional keys that need to be flexible. You can read more about the applications in the documentation.
Simple application
Let's create an index.html file. To do this, open a text editor
nano /var/www/example/index.html
and paste the lines
<!DOCTYPE html>
<html>
<head>
<title>Welcome to NGINX Unit!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to NGINX Unit!</h1>
<p>If you see this page, the NGINX Unit web server is successfully
installed and working. Further configuration is required.
</p>
<p>For online documentation and support, please refer to
<a href="https://unit.nginx.org/">unit.nginx.org</a>.<br/>
</p>
<p><em>Thank you for using NGINX Unit.</em></p>
</body>
</html>
Create a config.json file and paste the lines below:
{
"listeners": {
"*:80": {
"pass": "routes"
}
},
"routes": [
{
"action": {
"share": "/var/www/example$uri"
}
}
]
}
Load the configuration file with the command:
curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config
Contact the server on port 80 (HTTP) http://<domain name>:80
:80 is not a required parameter, when explicitly inserted it points to port :80 (HTTP)
But since HTTP connection is not secure the next point shows how to connect certificates and activate HTTPS.
Connecting certificates
Uploading TLS certificates to NGINX UNIT is simple, just like the configuration via API. For this purpose, the received certificates, generated for example with certbot, are bundled into one and transferred as a single file:
cat cert.pem ca.pem key.pem > bundle.pem
Bundle.pem - the final file, which is the certificate to be loaded into the NGINX UNIT. After that we make a request to the Unit to which we pass bundle.pem:
curl -X PUT --data-binary @bundle.pem --unix-socket /var/run/control.unit.sock http://localhost/certificates/bundle
Path to load the certificate /certificates/<name of certificate>
Here we already use a PUT request to update the list of certificates. The name of certificate that we used during the bundling does not matter, because we specify a new one when we load it. This is what we will use in our config. Here is an example of such a configuration file:
{
"listeners": {
"*:443": { // Using port 443 to activate HTTPS
"pass": "routes",
"tls": { // Use of the certificate
"certificate": "bundle" // The name of the certificate you uploaded
}
}
}
}
Deleting a certificate goes the same way as downloading it, but using DELETE instead of PUT in the request
Collecting logs and statistics
Statistics and health check
By making a GET request to the API you can get information about connections, requests, applications.
curl -X GET --unix-socket /var/run/control.unit.sock http://localhost/status
And now a little more detail on each of the keys:
Connections (connections) contains 4 parameters:
accepted - The number of connections during the lifetime of the current Nginx Unit socket
active - Current number of connections
idle - Number of idle connections
closed - Number of closed connections during the lifetime of the current Nginx Unit socket.
Requests contains a single total key that displays the total number of requests not related to the web server API, over the lifetime of the socket.
Applications contains information about each application. Example wordpress from the official documentation:
{
"wp": {
"processes": {
"running": 14, // Number of running processes
"starting": 0, // Starting processes
"idle": 4 // Idle processes
},
"requests": {
"active": 10 // Similar to queries, but contains information
} // on the application only
}
}
This is all the statistics that Nginx Unit provides, however it is concise and allows you to monitor everything together and for each application separately.
Bonus
As you can see in the image above, we can offload data from the web server in JSON format, which can be useful for analysis. After that, we can configure CRON on the server, to retrieve data at a given interval.
Logs
By standard, Nginx Unit logs are stored in a file at /var/log/unit.log
. This file contains data on startup, errors and information about web server operation. Example:
2023/06/07 09:48:34 [notice] 266515#266515 no modules matching: "/usr/lib/unit/modules/*.unit.so" found
2023/06/07 09:48:34 [info] 266514#266514 controller started
2023/06/07 09:48:34 [notice] 266514#266514 process 266515 exited with code 0
2023/06/07 09:48:34 [info] 266517#266517 router started
2023/06/07 09:48:34 [info] 266517#266517 OpenSSL 3.0.2 15 Mar 2022, 30000020
2023/06/07 09:49:34 [notice] 266514#266514 process 266516 exited with code 0
2023/06/07 09:49:34 [notice] 266514#266514 process 266517 exited with code 0
2023/06/07 09:49:34 [info] 266834#266834 discovery started
2023/06/07 09:49:34 [notice] 266834#266834 no modules matching: "/usr/lib/unit/modules/*.unit.so" found
2023/06/07 09:49:34 [info] 266833#266833 controller started
2023/06/07 09:49:34 [notice] 266833#266833 process 266834 exited with code 0
In this example, nginx unit loads language modules at initialisation, then handles routing.
Problems when working with Nginx Unit
Lack of necessary modules, or rather the versions you need. For example, version 1.30.0 does not support PHP 7.4, which is still in active use at the time of writing, so you will have to build manually or use docker containers.
Using curl with the -d parameter instead of --data-binary. Here the problem is obvious when loading certificates. Linux newbies may accept the example of using the -d flag to load JSON, but when loading certificates they will encounter a problem and will not understand what is going on.
Conclusion
In conclusion, I can say that using NGINX UNIT lowers the threshold of entry for beginners who want to deploy web applications. It is self-explanatory as it operates with JSON and uses REST API. But that doesn't take away from the fact that Unit is a powerful tool that is still evolving and fills the gaps and fixes the problems of its big brother NGINX web server.