NGINX Unit

Unit 1.26.0 Released§

Hi,

I’m glad to announce a new release of NGINX Unit.

Please read this letter to the end, as it explains some significant changes in the latest version. But first, I have great news for the PHP users: now the interpreter’s OPcache is shared between the processes of an app.

In previous versions, due to an architecture limitation (which imposed strong isolation, much stronger than was sometimes needed), each PHP process had a separate OPcache memory. As a result, with some workloads (especially involving many dynamic processes), performance could degrade because each new process had to warm up the cache after starting. Also, it required more memory because the bytecode of same PHP scripts was duplicated in each process. Now, all these flaws are finally gone.

Next, we noticed that more and more users use Unit to serve static files, if only because it’s efficient and easy to configure. Modern apps are all dynamic, yes, but at the same time, almost all apps and websites have static resources like images, scripts, styles, fonts, and media files. It’s very important to supply these resources as fast as possible without any delays to smoothen the overall user experience. We take this aspect seriously and continue improving Unit capabilities as a generic static media web server.

This time, all changes are about configuration flexibility. You may know that nginx has a number of different directives that control static file serving:

  • root
  • alias
  • try_files

Some of these are mutually exclusive, some can be combined, some work differently depending on the location type. That gives the configuration a lot of flexibility but may look a bit complicated. Users kept asking us to provide the same functionality in Unit, but instead of just repeating these, we thought about how we can improve this experience to make it easier to understand without losing flexibility.

Finally, we came up with a solution. In previous versions, we introduced the share directive, very similar to the root directive in nginx:

{
    "share": "/path/to/dir/"
}

Basically, it specified the so-called document root directory. To determine a file to serve, Unit appended the URI from the request to this share path. For this request:

GET /some/file.html

The above configuration served /path/to/dir/some/file.html.

In simple times, that’s exactly what you want. Still, there are plenty of cases when a different file needs to be served and the requested URI doesn’t match a path. More, you may want to serve a single file for any requests; the so-called one-page apps often utilize such a scheme.

Such border cases call for a finer degree of control over the full path to a file. As a result, we kept receiving suggestions like:

{
    "share_file": "/path/to/a/file.html"
}

The idea was to specify the full path to the file instead of the document root part only.

In parallel, we work variable support so we thought about introducing variable paths; for instance, you may want to serve different files depending on the normalized Host header:

{
    "share_file": "/www/data/$host/app.html"
}

Sneak peek ahead: we have plans for ways to create custom variables that extract various parts of the request using regular expressions.

Still, look at the suggested configuration:

{
    "share_file": "/www/data/$uri"
}

And compare it to what we had:

{
    "share": "/www/data/"
}

These configurations are essentially equal; why bother with another option at all? Figuring in the maxim that says that explicit is often better than implicit, this value:

"/www/data/$uri"

Is better and more self-descriptive than:

"/www/data/"

The latter only shows a part of the path, so you need to remind yourself that the URI is appended to it. Keeping this in mind and striving to have a cleaner configuration with fewer options to read about and to choose from, we finally decided to alter the behaviour of the share option.

Starting with Unit 1.26.0, the share option specifies the entire path to a shared file rather than just the document root. And yes, the option supports variables, so you can write:

{
    "share": "/www/data/$uri"
}

There won’t be a separate share_file option. I used it only to illustrate the initial idea and the resulting change; the share option assumes all relevant functionality instead.

If you run previous versions of Unit and use share in your configurations, an update to Unit 1.26+ will automatically append $uri to all your share values to preserve the expected behavior.

Configurations like this:

{
    "share": "/www/data/"
}

Are automatically rewritten as follows:

{
    "share": "/www/data/$uri"
}

This occurs only once, after the version update. If you manage your configurations using some scripts and store them somewhere else, make sure to adjust the “share” values there accordingly.

Note that Unit won’t fix your share values that you upload in reconfiguration requests over the control socket API.

To read more about the new share behavior, check the documentation: https://unit.nginx.org/configuration/#static-files

I hope this transition will be easy and as hassle-free as possible for our existing users. For new users, there is nothing to care about, just mind that blog posts or other sources about previous Unit versions can use configurations that rely on the discontinued share behavior, so make the necessary adjustments before copying them. All docs and howtos at the official Unit website were already updated: https://unit.nginx.org/howto/

Are you with me? That’s not the end of news about share. Here’s one more, and it’s pretty exciting. Earlier, to implement a try_files-like behavior, you had to use something like this:

{
    "share": "path1"

    "fallback": {
        "share": "path2"

        "fallback": {
            "pass": "application/blog"
        }
    }
}

This snippet tries to serve a file using path1; if it doesn’t exist or is inaccessible, it falls back to path2, and then passes the request further, to the blog app.

Now it’s much easier to configure:

{
    "share": [ "path1", "path2" ]

    "fallback": {
        "pass": "application/blog"
    }
}

The share directive now can accept an array of paths, trying them one by one until a file is found. If there is no file to serve, the fallback action occurs; if no fallback is defined, the result of the last try is returned. And yes, all these paths can contain variables:

{
    "share": [
        "/www/$host$uri",
        "/www/static$uri",
        "/www/app.html"
    ]
}

For more examples and detailed explanations: https://unit.nginx.org/configuration/#static-files

In future releases, we’ll introduce more variables and the ability to extract various parts of requests and save them into your custom variables, which will provide essentially endless flexibility to manipulate file paths.

There are some more notable features in this release as well:

  1. Variables support in the chroot option to accompany variable-based paths in “share” during static media serving.

    Learn more about Unit’s ability to chroot while serving static assets: https://unit.nginx.org/configuration/#path-restrictions

  2. The query matching option to filter and route requests by arbitrary query string values.

    We already had the arguments option that enabled filtering and routing requests by particular key-value pairs of query string arguments, but the query string doesn’t always fit this format. So, now you can also use regexps and wildcard matching to work on the full query string value.

    Learn more about our very flexible and elaborate request filtering and routing: https://unit.nginx.org/configuration/#routes

The complete change log for this release is below:

Changes with Unit 1.26.0                                         18 Nov 2021

    *) Change: the "share" option now specifies the entire path to the files
       it serves, rather than a document root directory to be prepended to
       the request URI.

    *) Feature: automatic adjustment of existing configurations to the new
       "share" behavior when updating from previous versions.

    *) Feature: variables support in the "share" option.

    *) Feature: multiple paths in the "share" option.

    *) Feature: variables support in the "chroot" option.

    *) Feature: PHP opcache is shared between application processes.

    *) Feature: request routing by the query string.

    *) Bugfix: the router and app processes could crash when the requests
       limit was reached by asynchronous or multithreaded apps.

    *) Bugfix: established WebSocket connections could stop reading frames
       from the client after the corresponding listener had been
       reconfigured.

    *) Bugfix: fixed building with glibc 2.34, notably Fedora 35.

Other major features that we are preparing for the next release include:

  • basic statistics API for monitoring Unit instances
  • various variables for different aspects of request and connection data
  • customization of access log format with variables
  • custom variables out of regexp captures on various request parameters
  • simple request rewrite using variables
  • command-line tool to simplify the use of Unit’s control socket API

There probably will be even more.

To participate, share your ideas, or discuss new features, you’re welcome to visit Unit’s issue tracker on GitHub: https://github.com/nginx/unit/issues

Stay tuned!

wbr, Valentin V. Bartenev