NGINX Unit

Configuration§

Applications§

For each application, you use the API to define a JSON object in the applications section of the Unit configuration. The JSON object defines several characteristics of the application, including the language it’s written in, the number of application processes to run, the directory with the file or files for the application, and parameters that vary by language.

This example runs 20 processes of the PHP application named blogs using the files found in the /www/blogs/scripts directory. The default launch file when the URL doesn’t specify the PHP file is index.php.

{
    "blogs": {
        "type": "php",
        "processes": 20,
        "root": "/www/blogs/scripts",
        "index": "index.php"
    }
}

Listeners§

For an application to be accessible via HTTP, you must define at least one listener for it in the listeners section of the Unit configuration. A listener is an IP address and port on which Unit listens for client requests to a named application. The IP address can be either a full address (for example, 127.0.0.1:8300) or a wildcard (for example, *:8300).

In this example, requests received on port 8300 are sent to the blogs application:

{
    "*:8300": {
        "pass": "applications/blogs"
    }
}

For complete details about the JSON objects for each language, see Application Objects.

Configuration Management§

Minimum Configuration§

In order to run an application, configuration must include at least one listener and associated application, as in this example:

{
    "listeners": {
        "*:8300": {
            "pass": "applications/blogs"
        }
    },

    "applications": {
        "blogs": {
            "type": "php",
            "processes": 20,
            "root": "/www/blogs/scripts",
            "index": "index.php"
        }
    }
}

Creating Objects§

To create a configuration object, specify the JSON data for it in the body of a PUT request. To reduce errors, it makes sense to write the JSON data in a file and specify the file path with the -d option to the curl command.

Create an initial configuration by uploading the contents of the start.json file:

$ curl -X PUT -d @/path/to/start.json  \
       --unix-socket /path/to/control.unit.sock http://localhost/config/

Create a new application object called wiki from the file wiki.json:

$ curl -X PUT -d @/path/to/wiki.json  \
       --unix-socket /path/to/control.unit.sock http://localhost/config/applications/wiki

The contents of wiki.json are:

{
    "type": "python",
    "processes": 10,
    "module": "wsgi",
    "user": "www-wiki",
    "group": "www-wiki",
    "path": "/www/wiki"
}

Displaying Objects§

To display a configuration object, append its path to the curl URL.

Display the complete configuration:

$ curl --unix-socket /path/to/control.unit.sock http://localhost/config/

    {
        "listeners": {
            "*:8300": {
                "pass": applications/blogs"
            }
        },

        "applications": {
            "blogs": {
                "type": "php",
                "user": "nobody",
                "group": "nobody",
                "root": "/www/blogs/scripts",
                "index": "index.php"
            }
        }
    }

Display the data for the wiki application:

$ curl --unix-socket /path/to/control.unit.sock http://localhost/config/applications/wiki

    {
        "type": "python",
        "processes": 10,
        "module": "wsgi",
        "user": "www",
        "group": "www",
        "path": "/www/wiki"
    }

Modifying Objects§

To change a configuration object, use the -d option to the curl command to specify the object’s JSON data in the body of a PUT request.

Change the application object to wiki-dev for the listener on *:8400:

$ curl -X PUT -d '"wiki-dev"' --unix-socket /path/to/control.unit.sock  \
       'http://localhost/config/listeners/*:8400/application'

    {
        "success": "Reconfiguration done."
    }

Change the root object for the blogs application to /www/blogs-dev/scripts:

$ curl -X PUT -d '"/www/blogs-dev/scripts"'  \
       --unix-socket /path/to/control.unit.sock  \
       http://localhost/config/applications/blogs/root

    {
        "success": "Reconfiguration done."
    }

Deleting Objects§

To delete a configuration object, make a DELETE request and append the object’s path to the curl URL.

Delete the listener on *:8400:

$ curl -X DELETE --unix-socket /path/to/control.unit.sock  \
       'http://localhost/config/listeners/*:8400'

    {
        "success": "Reconfiguration done."
    }

Settings Object§

Unit has a global settings configuration object that stores instance-wide preferences. Its http option fine-tunes the handling of HTTP requests from the clients:

OptionDescription
header_read_timeout

Maximum number of seconds to read the header of a client’s request. If Unit doesn’t receive the entire header from the client within this interval, it responds with a 408 Request Timeout error.

The default value is 30.

body_read_timeout

Maximum number of seconds to read data from the body of a client’s request. It limits the interval between consecutive read operations, not the time to read the entire body. If Unit doesn’t receive any data from the client within this interval, it responds with a 408 Request Timeout error.

The default value is 30.

send_timeout

Maximum number of seconds to transmit data in the response to a client. It limits the interval between consecutive transmissions, not the entire response transmission. If the client doesn’t receive any data within this interval, Unit closes the connection.

The default value is 30.

idle_timeout

Maximum number of seconds between requests in a keep-alive connection. If no new requests arrive within this interval, Unit responds with a 408 Request Timeout error and closes the connection.

The default value is 180.

max_body_size

Maximum number of bytes in the body of a client’s request. If the body size exceeds this value, Unit responds with a 413 Payload Too Large error and closes the connection.

The default value is 8388608 (8 MB).

Example:

{
    "settings": {
        "http": {
            "header_read_timeout": 10,
            "body_read_timeout": 10,
            "send_timeout": 10,
            "idle_timeout": 120,
            "max_body_size": 6291456
        }
    }
}

Listener Objects§

OptionDescription
application (deprecated)

App name: "application": "qwk2mart". Mutually exclusive with pass.

Warning

This object is deprecated. Please update your configurations to use pass instead.

pass (required)Qualified app or route name: "pass": "routes/route66", "pass": "applications/qwk2mart". Mutually exclusive with application.
tlsSSL/TLS configuration. Set its only option, certificate, to enable secure communication via the listener. The value must reference a certificate chain that you have uploaded earlier. For details, see SSL/TLS and Certificates.

Example:

{
    "pass": "applications/blogs",
    "tls": {
        "certificate": "blogs-cert"
    }
}

Routes§

Unit configuration offers a routes object to enable elaborate internal routing between listeners and apps. Listeners pass requests to routes or directly to apps. Requests are matched against route step conditions; a request fully matching a step’s condition is passed to the app or the route that the step specifies.

The routes object may contain a single anonymous route array:

{
     "listeners": {
         "*:8300": {
             "pass": "routes"
         }
     },

     "routes": [ "simply referred to as routes" ]
}

Another option is one or more named route arrays:

{
     "listeners": {
         "*:8300": {
             "pass": "routes/main"
         }
     },

     "routes": {
         "main": [ "named route, qualified name: routes/main" ],
         "route66": [ "named route, qualified name: routes/route66" ]
     }
}

Route Object§

Route array contains anonymous objects, or steps; a request passed to a route traverses them sequentially. Steps have the following options:

OptionDescription
match

Object that defines the step condition.

  • If the request fits the match condition, the step’s pass is followed.
  • If the request doesn’t match a step, Unit proceeds to the next step of the route.
  • If the request doesn’t match any steps, a 404 “Not Found” response is returned.

See below for condition matching details.

action/pass (required)Route’s destination; identical to pass in a listener. If you omit match, requests are passed unconditionally; to avoid issues, use no more than one such step per route, placing it last.

An example:

{
    "routes": [
        {
            "match": {
                "host": "example.com",
                "uri": "/admin/*"
            },

            "action": {
                "pass": "applications/php5_app"
             }
        },
        {
            "action": {
                "pass": "applications/php7_app"
             }
        }
     ]
}

A more elaborate example with chained routes:

{
    "routes": {
        "main": [
            {
                "match": {
                    "host": [ "www.example.com", "example.com" ]
                },

                "action": {
                    "pass": "routes/site"
                }
            },
            {
                "match": {
                    "host": "blog.example.com"
                },

                "action": {
                    "pass": "applications/blog"
                }
            }
        ],

        "site": [ "..." ]
    }
}

Condition Matching§

The match condition in a step comprises request property names and corresponding patterns:

{
    "match": {
        "request_property1": "pattern",
        "request_property2": ["pattern", "pattern", "..." ]
    },

    "action": {
        "pass": "..."
     }
}

To fit a step’s condition, the request must match all properties listed in it. Available options:

OptionDescription
hostRequest host from the Host header field without port number, normalized by removing the trailing period (if any); case-insensitive.
methodRequest method from the request line; case-insensitive.
uriRequest URI path without arguments, normalized by decoding the “%XX” sequences, resolving relative path references (“.” and “..”), and compressing adjacent slashes into one; case-sensitive.

Patterns must be exact matches; they also support wildcards (*) and negations (!):

  • A wildcard matches zero or more arbitrary characters; pattern can start with it, end with it, or both.
  • A negation restricts specific patterns; pattern can only start with it.

To be a match against the patterns listed in a condition, the property must meet two requirements:

  • If there are patterns without negation, at least one of them matches.
  • No negation-based patterns match.

Note

This type of matching can be explained with set operations. Suppose set U comprises all possible values of a property; set P comprises strings that match any patterns without negation; set N comprises strings that match any negation-based patterns. In this scheme, the matching set will be:

UP \ N if P ≠ ∅
U \ N if P = ∅

A few examples:

{
    "host": "*.example.com"
}

Only subdomains of example.com will match.

{
    "host": ["*.example.com", "!www.example.com"]
}

Here, any subdomains of example.com will match except www.example.com.

{
    "method": ["!HEAD", "!GET"]
}

Any methods will match except HEAD and GET.

You can also combine special characters in a pattern:

{
    "uri": "!*/api/*"
}

Here, any URIs will match except ones containing /api/.

If all properties match or you omit the condition, Unit routes the request where pass points to:

{
    "match": {
        "host": [ "*.example.com", "!php7.example.com" ],
        "uri": [ "/admin/*", "/store/*" ],
        "method": "POST"
    },

    "action": {
        "pass": "applications/php5_app"
     }
}

Here, all POST requests for URIs prefixed with /admin/ or /store/ within any subdomains of example.com (except php7) are routed to php5_app.

Application Objects§

Each application object has a number of common options that can be specified for any application regardless of its type.

The common options are follows:

OptionDescription
type (required)

Type of the application: external (Go and Node.js), java, perl, php, python, or ruby.

Except with external, you can detail the runtime version: "type": "python 3", "type": "python 3.4", or even "type": "python 3.4.9rc1". Unit searches its modules and uses the latest matching one, reporting an error if none match.

For example, if you have installed only one PHP 7 module, 7.1.9, it will match "php", "php 7", "php 7.1", and "php 7.1.9". If you install two PHP modules, 7.0.2 and 7.0.23, and prefer to use 7.0.2, set "type": "php 7.0.2". If you supply "php 7", PHP 7.0.23 will be used as the latest version available.

limitsAn object that accepts two integer options, timeout and requests. Their values restrict the life cycle of an application process. For details, see Request Limits.
processes

An integer or an object. Integer value configures a static number of application processes. Object accepts dynamic process management settings: max, spare, and idle_timeout. For details, see Process Management.

The default value is 1.

working_directoryWorking directory for the application. If not specified, the working directory of Unit daemon is used.
userUsername that runs the app process. If not specified, nobody is used.
groupGroup name that runs the app process. If not specified, user’s primary group is used.
environmentEnvironment variables to be used by the application.

Example:

{
    "type": "python 3.6",
    "processes": 16,
    "working_directory": "/www/python-apps",
    "path": "blog",
    "module": "blog.wsgi",
    "user": "blog",
    "group": "blog",
    "limits": {
        "timeout": 10,
        "requests": 1000
    },

    "environment": {
        "DJANGO_SETTINGS_MODULE": "blog.settings.prod",
        "DB_ENGINE": "django.db.backends.postgresql",
        "DB_NAME": "blog",
        "DB_HOST": "127.0.0.1",
        "DB_PORT": "5432"
    }
}

Depending on the type of the application, you may need to configure a number of additional options. In the example above, Python-specific options path and module are used.

Process Management and Limits§

Application process behavior in Unit is described by two configuration options, limits and processes.

Request Limits§

The limits object accepts two options:

OptionDescription
timeoutRequest timeout in seconds. If an application process exceeds this limit while processing a request, Unit terminates the process and returns an HTTP error to the client.
requestsMaximum number of requests Unit allows an application process to serve. If this limit is reached, Unit terminates and restarts the application process. This allows to mitigate application memory leaks or other issues that may accumulate over time.

Process Management§

The processes option offers choice between static and dynamic process management model. If you provide an integer value, Unit immediately launches the given number of application processes and maintains them statically without scaling.

Unit also supports a dynamic prefork model for processes that is enabled and configured with the following parameters:

OptionDescription
max

Maximum number of application processes that Unit will maintain (busy and idle).

The default value is 1.

spare

Minimum number of idle processes that Unit will reserve for the application when possible. When Unit starts an application, spare idle processes are launched. As requests arrive, Unit assigns them to existing idle processes and forks new idle ones to maintain the spare level if max permits. When processes complete requests and turn idle, Unit terminates extra ones after a timeout.

The default value is 0. The value of spare cannot exceed max.

idle_timeout

Number of seconds for Unit to wait before it terminates an extra idle process, when the count of idle processes exceeds spare.

The default value is 15.

If processes is omitted entirely, Unit creates 1 static process. If empty object is provided: "processes": {}, dynamic behavior with default parameter values is assumed.

In the following example, Unit tries to keep 5 idle processes, no more than 10 processes in total, and terminates extra idle processes after 20 seconds of inactivity:

{
    "max": 10,
    "spare": 5,
    "idle_timeout": 20
}

Go/Node.js Applications§

To run your Go or Node.js applications in Unit, you need to configure them and modify their source code as suggested below. Let’s start with the application configuration:

OptionDescription
executable (required)

Pathname of the application, absolute or relative to working_directory.

For Node.js, supply your .js pathname and start the file itself with a proper shebang:

#!/usr/bin/env node
argumentsCommand line arguments to be passed to the application. The example below is equivalent to /www/chat/bin/chat_app --tmp-files /tmp/go-cache.

Example:

{
    "type": "external",
    "working_directory": "/www/chat",
    "executable": "bin/chat_app",
    "user": "www-go",
    "group": "www-go",
    "arguments": ["--tmp-files", "/tmp/go-cache"]
}

Before applying the configuration, update the application itself.

Modifying Go Sources§

In the import section, reference the "nginx/unit" package that you have installed earlier:

import (
    ...
    "nginx/unit"
    ...
)

In the main() function, replace the http.ListenandServe call with unit.ListenAndServe:

func main() {
    ...
    http.HandleFunc("/", handler)
    ...
    //http.ListenAndServe(":8080", nil)
    unit.ListenAndServe(":8080", nil)
    ...
}

The resulting application works as follows:

  • When you run it standalone, the unit.ListenAndServe call falls back to http functionality.
  • When Unit runs it, unit.ListenAndServe communicates with Unit’s router process directly, ignoring the address supplied as its first argument and relying on the listener’s settings instead.

Modifying Node.js Sources§

First, you need to have the unit-http package installed. If it’s global, symlink it in your project directory:

# npm link unit-http

Do the same if you move a Unit-hosted application to a new system where unit-http is installed globally.

Next, use unit-http instead of http in your code:

var http = require('unit-http');

Java Application§

ObjectDescription
classpathArray of paths to your app’s required libraries (may point to directories or .jar files).
optionsArray of strings defining JVM runtime options.
webapp (required)Pathname of the application’s packaged or unpackaged .war file.

Example:

{
    "type": "java",
    "classpath": ["/www/qwk2mart/lib/qwk2mart-2.0.0.jar"],
    "options": ["-Dlog_path=/var/log/qwk2mart.log"],
    "webapp": "/www/qwk2mart/qwk2mart.war"
}

Perl Application§

OptionDescription
script (required)PSGI script path.

Example:

{
    "type": "perl",
    "script": "/www/bugtracker/app.psgi",
    "working_directory": "/www/bugtracker",
    "processes": 10,
    "user": "www",
    "group": "www"
}

PHP Application§

OptionDescription
index

Filename appended to any URI paths ending with a slash; applies if script is omitted.

Default value is index.php.

optionsObject that defines php.ini location and options. For details, see below.
root (required)Base directory of your PHP app’s file structure. All URI paths are relative to this value.
scriptFilename of a PHP script; if set, Unit uses this script to serve any requests to this application. Relative to root.

The index and script options enable two modes of operation:

  • If script is set, all requests to the application are handled by the script you provide.
  • Otherwise, the requests are served according to their URI paths; if script name is omitted, index is used.

You can customize php.ini via the options object:

OptionDescription
filePathname of the php.ini file.
admin, userObjects with PHP configuration directives. Directives in admin are set in PHP_INI_SYSTEM mode; it means that your application can’t alter them. Directives in user are set in PHP_INI_USER mode; your application is allowed to update them in runtime.

Directives from php.ini are applied first; next, admin and user objects are applied.

Note

Provide string values for any directives you specify in options (for example, "max_file_uploads": "64" instead of "max_file_uploads": 64). For flags, use "0" and "1" only. For more information about PHP_INI_* modes, see the PHP documentation.

Example:

{
    "type": "php",
    "processes": 20,
    "root": "/www/blogs/scripts",
    "index": "index.php",
    "user": "www-blogs",
    "group": "www-blogs",

    "options": {
        "file": "/etc/php.ini",
        "admin": {
            "memory_limit": "256M",
            "variables_order": "EGPCS",
            "expose_php": "0"
        },
        "user": {
            "display_errors": "0"
        }
    }
}

Python Application§

OptionDescription
module (required)WSGI module name. To run the app, Unit looks for an application callable in the module you supply; the module itself is imported just like in Python.
pathAdditional lookup path for Python modules; this string is inserted into sys.path.
home

Path to Python virtual environment for the application. You can set this value relative to the working_directory of the application.

Note

The Python version used by Unit to run the application is controlled by the type of the application. Unit doesn’t use command line Python interpreter within the virtual environment due to performance considerations.

Example:

{
    "type": "python 3.6",
    "processes": 10,
    "working_directory": "/www/store/",
    "path": "/www/store/cart/",
    "home": "/www/store/.virtualenv/",
    "module": "wsgi",
    "user": "www",
    "group": "www"
}

Ruby Application§

OptionDescription
script (required)Rack script path.

Example:

{
    "type": "ruby",
    "processes": 5,
    "user": "www",
    "group": "www",
    "script": "/www/cms/config.ru"
}

Access log§

To configure access logging, use the access_log parameter in a configuration object to specify the path to the log file.

In the example below, all requests will be logged to /var/log/access.log:

$ curl -X PUT -d '"/var/log/access.log"'  \
       --unix-socket /path/to/control.unit.sock  \
       http://localhost/config/access_log

    {
        "success": "Reconfiguration done."
    }

The log is written in the Combined Log Format. Example of a log line:

127.0.0.1 - - [21/Oct/2015:16:29:00 -0700] "GET / HTTP/1.1" 200 6022 "http://example.com/links.html" "Godzilla/5.0 (X11; Minix i286) Firefox/42"

SSL/TLS and Certificates§

To set up SSL/TLS access for your application, upload a .pem file containing your certificate chain and private key to Unit. Next, reference the uploaded bundle in the listener’s configuration. After that, the listener’s application becomes accessible via SSL/TLS.

First, create a .pem file with your certificate chain and private key:

$ cat cert.pem ca.pem key.pem > bundle.pem

Note

Usually, your website’s certificate (optionally followed by the intermediate CA certificate) is enough to build a certificate chain. If you add more certificates to your chain, order them leaf to root.

Upload the resulting file to Unit’s certificate storage under a suitable name:

$ curl -X PUT --data-binary @bundle.pem 127.1:8443/certificates/<bundle>

    {
        "success": "Certificate chain uploaded."
    }

Warning

Don’t use -d for file upload; this option damages .pem files. Use the --data-binary option when uploading file-based data with curl to avoid data corruption.

Internally, Unit stores uploaded certificate bundles along with other configuration data in its state subdirectory; Unit’s control API maps them to a separate configuration section, aptly named certificates:

{
    "certificates": {
        "<bundle>": {
            "key": "RSA (4096 bits)",
            "chain": [
                {
                    "subject": {
                        "common_name": "example.com",
                        "alt_names": [
                            "example.com",
                            "www.example.com"
                        ],

                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme, Inc."
                    },

                    "issuer": {
                        "common_name": "intermediate.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Certification Authority"
                    },

                    "validity": {
                        "since": "Sep 18 19:46:19 2018 GMT",
                        "until": "Jun 15 19:46:19 2021 GMT"
                    }
                },

                {
                    "subject": {
                        "common_name": "intermediate.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Certification Authority"
                    },

                    "issuer": {
                        "common_name": "root.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Root Certification Authority"
                    },

                    "validity": {
                        "since": "Feb 22 22:45:55 2016 GMT",
                        "until": "Feb 21 22:45:55 2019 GMT"
                    }
                },
            ]
        }
    }
}

Note

You can access individual certificates in your chain, as well as specific alternative names, by their indexes:

$ curl -X GET 127.1:8443/certificates/<bundle>/chain/0/
$ curl -X GET 127.1:8443/certificates/<bundle>/chain/0/subject/alt_names/0/

Next, add a tls object to your listener configuration, referencing the uploaded bundle’s name in certificate:

{
    "listeners": {
        "127.0.0.1:8080": {
            "pass": "applications/wsgi-app",
            "tls": {
                "certificate": "<bundle>"
            }
        }
    }
}

The resulting control API configuration may look like this:

{
    "certificates": {
        "<bundle>": {
            "key": "<key type>",
            "chain": ["<certificate chain, omitted for brevity>"]
        }
    },

    "config": {
        "listeners": {
            "127.0.0.1:8080": {
                "pass": "applications/wsgi-app",
                "tls": {
                    "certificate": "<bundle>"
                }
            }
        },

        "applications": {
            "wsgi-app": {
                "type": "python",
                "module": "wsgi",
                "path": "/usr/www/wsgi-app/"
            }
        }
    }
}

Now you’re solid. The application is accessible via SSL/TLS:

$ curl -v https://127.0.0.1:8080
    ...
    * TLSv1.2 (OUT), TLS handshake, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (IN), TLS handshake, Server finished (14):
    * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
    * TLSv1.2 (OUT), TLS change cipher, Client hello (1):
    * TLSv1.2 (OUT), TLS handshake, Finished (20):
    * TLSv1.2 (IN), TLS change cipher, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Finished (20):
    * SSL connection using TLSv1.2 / AES256-GCM-SHA384
    ...

Finally, you can DELETE a certificate bundle that you don’t need anymore from the storage:

$ curl -X DELETE 127.1:8443/certificates/<bundle>

    {
        "success": "Certificate deleted."
    }

Note

You can’t delete certificate bundles still referenced in your configuration, overwrite existing bundles using PUT, or (obviously) delete non-existent ones.

Happy SSLing!

Full Example§

{
    "certificates": {
        "bundle": {
            "key": "RSA (4096 bits)",
            "chain": [
                {
                    "subject": {
                        "common_name": "example.com",
                        "alt_names": [
                            "example.com",
                            "www.example.com"
                        ],

                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme, Inc."
                    },

                    "issuer": {
                        "common_name": "intermediate.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Certification Authority"
                    },

                    "validity": {
                        "since": "Sep 18 19:46:19 2018 GMT",
                        "until": "Jun 15 19:46:19 2021 GMT"
                    }
                },

                {
                    "subject": {
                        "common_name": "intermediate.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Certification Authority"
                    },

                    "issuer": {
                        "common_name": "root.ca.example.com",
                        "country": "US",
                        "state_or_province": "CA",
                        "organization": "Acme Root Certification Authority"
                    },

                    "validity": {
                        "since": "Feb 22 22:45:55 2016 GMT",
                        "until": "Feb 21 22:45:55 2019 GMT"
                    }
                }
            ]
        }
    },

    "config": {
        "settings": {
            "http": {
                "header_read_timeout": 10,
                "body_read_timeout": 10,
                "send_timeout": 10,
                "idle_timeout": 120,
                "max_body_size": 6291456
            }
        },

        "listeners": {
            "*:8300": {
                "pass": "applications/blogs",
                "tls": {
                    "certificate": "bundle"
                }
            },

            "*:8400": {
                "pass": "applications/wiki"
            },

            "*:8500": {
                "pass": "applications/go_chat_app"
            },

            "127.0.0.1:8600": {
                "pass": "applications/bugtracker"
            },

            "127.0.0.1:8601": {
                "pass": "routes/cms"
            },

            "*:8700": {
                "pass": "applications/qwk2mart"
            }
        },

        "routes" {
            "cms": [
                {
                    "match": {
                        "uri": "!/admin/*"
                    },
                    "action": {
                        "pass": "applications/cms_main"
                    }
                },

                {
                    "action": {
                        "pass": "applications/cms_admin"
                    }
                }
            ]
        },

        "applications": {
            "blogs": {
                "type": "php",
                "processes": 20,
                "root": "/www/blogs/scripts",
                "index": "index.php",
                "limits": {
                    "timeout": 10,
                    "requests": 1000
                },

                "options": {
                    "file": "/etc/php.ini",
                    "admin": {
                        "memory_limit": "256M",
                        "variables_order": "EGPCS",
                        "expose_php": "0"
                    },

                    "user": {
                        "display_errors": "0"
                    }
                }
            },

            "wiki": {
                "type": "python",
                "processes": 10,
                "path": "/www/wiki",
                "module": "wsgi",
                "environment": {
                    "DJANGO_SETTINGS_MODULE": "blog.settings.prod",
                    "DB_ENGINE": "django.db.backends.postgresql",
                    "DB_NAME": "blog",
                    "DB_HOST": "127.0.0.1",
                    "DB_PORT": "5432"
                }
            },

            "go_chat_app": {
                "type": "external",
                "user": "www-chat",
                "group": "www-chat",
                "working_directory": "/www/chat",
                "executable": "bin/chat_app"
            },

            "bugtracker": {
                "type": "perl",
                "processes": {
                    "max": 10,
                    "spare": 5,
                    "idle_timeout": 20
                },

                "working_directory": "/www/bugtracker",
                "script": "app.psgi"
            },

            "cms_main": {
                "type": "ruby",
                "processes": 5,
                "script": "/www/cms/main.ru"
            },

            "cms_admin": {
                "type": "ruby",
                "processes": 1,
                "script": "/www/cms/admin.ru"
            },

            "qwk2mart": {
                "type": "java",
                "classpath": ["/www/qwk2mart/lib/qwk2mart-2.0.0.jar"],
                "options": ["-Dlog_path=/var/log/qwk2mart.log"],
                "webapp": "/www/qwk2mart/qwk2mart.war"
            }
        },

        "access_log": "/var/log/access.log"
    }
}