Category: Development

About development

  • Nginx Unit: how to configure

    In the past few days, I dove into NGINX Unit, a server developed by the NGINX team itself — designed to replace the traditional combo “NGINX + Gunicorn + uWSGI.”

    The promise sounds great: dynamic configuration via API, support for multiple apps (Python, PHP, Go, etc.) in the same process, and even a reverse proxy for other containers.

    But in practice, Unit can be full of surprises — and not always pleasant ones.

    Here’s a summary of what I learned — including mistakes, fixes, and real code examples.

    What is NGINX Unit

    NGINX Unit is a modular application server created by the NGINX team, but with a very different philosophy:

    It doesn’t use configuration files in the classic nginx.conf format.

    All configuration is done through an HTTP API (or Unix socket). Everything can be updated in real time, without restarting the service.

    You can, for example, add a new website, change the runtime language, or create a reverse proxy — all by sending a JSON POST request.

    Running Unit with Docker

    The Compose file for the Unit container looks like this YAML:

    services:
    
    unit:
    image: nginx/unit:1.32.1
    ports:
    - "8080:80"
    volumes:
    - ./config:/docker-entrypoint.d
    - ./sites:/var/www/sites

    To apply a configuration, use the following command:

    curl -X PUT --data-binary @config.json http://localhost:8080/config

    Configuring Static Sites

    In order to configure a site that serves only static content, you can use a JSON like this:

    {
        "listeners": {
        "*:80": {
           "pass": "routes/main"
        }
    },
        "routes": {
            "main": [
                {
                     "match": {
                     "host": "site1.example.com"
                },
            "action": {
                "share": "/var/www/sites/site1"
            }
          }
         ]
        }
    }

    💡 Tip: The share path must exist inside the container, so make sure to mount your volume correctly in docker-compose.yml.

    Networking Details

    Unit doesn’t recognize container names (like app:8000) because it doesn’t use Docker’s internal DNS.

    You’ll need to use either a fixed IP (i.e., the app’s IP configured in your Compose file) or an alias added via extra_hosts.

    The JSON applied to Unit will have a similar structure, except for this section:

    "routes": { 
        "main": [ 
                   { 
                      "match": { 
                          "host": "deployer.linkei.app.br" 
                       }, 
                  "action": { 
                             "proxy": "http://172.28.0.11:8000", 
                             "rewrite": "$uri" 
                  } 
                 } 
               ] 
    }

    ⚠️ Important: The rewrite field must be a string, not an object.

    Common Errors and Fixes

    A very short list of errors and respective solutions.

    ERRORCAUSESOLUTION
    403 ForbiddenUnit doesn’t has the permissions to access the share directoryAdjust the permissions(chmod -R a+rX /var/www/sites)
    Request "pass" points to invalid locationReference error in the "pass" fieldUse "pass": "routes/main", not "routes/main/"
    connection resetUnit can’t reach out the backendUse a fixed IP or extra_hosts
    types value must be string or arrayIncorrect JSON when defining MIME typesUse "types": ["text/html", "text/css", ...]

    Conclusion

    NGINX Unit is a powerful — yet unusual — tool. It requires some experimentation and a careful read of the official documentation, especially because tiny JSON details can cause big headaches.

    But once you understand it, it offers incredible flexibility:
    You can deploy and update static sites, APIs, and apps in real time, via API — without restarting anything.

    It’s ideal for those building automated deployment platforms.