Configuration — NGINX Unit Error 403. Error 403.
As of October 2025, NGINX Unit is archived and unmaintained.
v. 1.34.2
About
Key features
News
Installation
Control API
Configuration
Listeners
SSL/TLS configuration
IP, protocol forwarding
Overwriting protocol scheme
Originating IP identification
Routes
Route steps
Matching conditions
Match resolution
Pattern syntax
Handling actions
Variables
URI rewrite
Response headers
Instant responses, redirects
Static files
MIME filtering
Path restrictions
Fallback action
Proxying
Load balancing
Applications
Process management
Process isolation
Request limits
Application processes
Go
Java
Node.js
Perl
PHP
Targets
Python
Targets
Ruby
WebAssembly
wasm-wasi-component
unit-wasm
Settings
Access log
Custom log formatting
JSON log format
Conditional access log
Scripting
SSL/TLS certificates
Status API
CLI (unitctl)
How-to
Troubleshooting
Community
Note
The commands in this document starting with a hash (#) must be run as root or
with superuser privileges.
Configuration
The
/config
section of the
control API
handles Unit’s general configuration with entities such as
listeners
routes
applications
or
upstreams
Listeners
To accept requests,
add a listener object in the
config/listeners
API section;
the object’s name can be:
A unique IP socket:
127.0.0.1:80
[::1]:8080
A wildcard that matches any host IPs on the port:
*:80
On Linux-based systems,
abstract UNIX sockets
can be used as well:
unix:@abstract_socket
Note
Also on Linux-based systems,
wildcard listeners can’t overlap with other listeners
on the same port
due to rules imposed by the kernel.
For example,
*:8080
conflicts with
127.0.0.1:8080
in particular,
this means
*:8080
can’t be
immediately
replaced
by
127.0.0.1:8080
(or vice versa)
without deleting it first.
Unit dispatches the requests it receives
to destinations referenced by listeners.
You can plug several listeners into one destination
or use a single listener
and hot-swap it between multiple destinations.
Available listener options:
Option
Description
pass
(required)
Destination to which the listener passes incoming requests.
Possible alternatives:
Application
applications/qwk2mart
PHP target
or
Python target
applications/myapp/section
Route
routes/route66
routes
Upstream
upstreams/rr-lb
The value is
variable
-interpolated;
if it matches no configuration entities after interpolation,
a 404 “Not Found” response is returned.
forwarded
Object;
configures client IP address and protocol
replacement
tls
Object;
defines SSL/TLS
settings
backlog
Integer;
controls the ‘backlog’ parameter to the
listen(2)
system-call.
This essentially limits the number of pending connections waiting
to be accepted.
The default varies by system.
On Linux, FreeBSD, OpenBSD and macOS the default is
-1
which
means use the OS default. For example. on Linux since 5.4, this is
4096
(previously
128
) and on FreeBSD it’s
128
On other systems the default is
511
NOTE: Whatever limit you set here will be limited by the OS
system-wide sysctl. For example. on Linux that is
net.core.somaxconn
and on BSD it’s
kern.ipc.somaxconn
(since 1.33.0)
Here, a local listener accepts requests at port 8300
and passes them to the
blogs
app
target
identified by the
uri
variable
The wildcard listener on port 8400
relays requests at any host IPs
to the
main
route
"127.0.0.1:8300"
"pass"
"applications/blogs
$uri
},
"*:8400"
"pass"
"routes/main"
Also,
pass
values can be
percent encoded
For example, you can escape slashes in entity names:
"listeners"
"*:80"
"pass"
"routes/slashes%2Fin%2Froute%2Fname"
},
"routes"
"slashes/in/route/name"
[]
SSL/TLS configuration
The
tls
object provides the following options:
Option
Description
certificate
(required)
String or an array of strings;
refers to one or more
certificate bundles
uploaded earlier,
enabling secure communication via the listener.
conf_commands
Object;
defines the OpenSSL
configuration commands
to be set for the listener.
To have this option,
Unit must be built and run with OpenSSL 1.0.2+:
openssl
version
OpenSSL 1.1.1d 10 Sep 2019
Also, make sure your OpenSSL version supports the commands
set by this option.
session
Object; configures the TLS session cache and tickets
for the listener.
To use a certificate bundle you
uploaded
earlier,
name it in the
certificate
option of the
tls
object:
"listeners"
"127.0.0.1:443"
"pass"
"applications/wsgi-app"
"tls"
"certificate"
bundle
Configuring multiple bundles
Since version 1.23.0,
Unit supports configuring
Server Name Indication (SNI)
on a listener
by supplying an array of certificate bundle names
for the
certificate
option value:
"*:443"
"pass"
"routes"
"tls"
"certificate"
"bundleA"
"bundleB"
"bundleC"
If the connecting client sends a server name,
Unit responds with the matching certificate bundle.
If the name matches several bundles,
exact matches have priority over wildcards;
if this doesn’t help, the one listed first is used.
If there’s no match or no server name was sent, Unit uses
the first bundle on the list.
To set custom OpenSSL
configuration commands
for a listener,
use the
conf_commands
object in
tls
"tls"
"certificate"
bundle
"conf_commands"
"ciphersuites"
TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
"minprotocol"
"TLSv1.3"
The
session
object in
tls
configures the session settings of the listener:
Option
Description
cache_size
Integer;
sets the number of sessions in the TLS session cache.
The default is
(caching is disabled).
tickets
Boolean, string, or an array of strings;
configures TLS session tickets.
The default is
false
(tickets are disabled).
timeout
Integer;
sets the session timeout for the TLS session cache.
When a new session is created,
its lifetime derives from current time and
timeout
If a cached session is requested past its lifetime,
it is not reused.
The default is
300
(5 minutes).
Example:
"tls"
"certificate"
bundle
"session"
"cache_size"
10240
"timeout"
60
"tickets"
"k5qMHi7IMC7ktrPY3lZ+sL0Zm8oC0yz6re+y/zCj0H0/sGZ7yPBwGcb77i5vw6vCx8vsQDyuvmFb6PZbf03Auj/cs5IHDTYkKIcfbwz6zSU="
"3Cy+xMFsCjAek3TvXQNmCyfXCnFNAcAOyH5xtEaxvrvyyCS8PJnjOiq2t4Rtf/Gq"
"8dUI0x3LRnxfN0miaYla46LFslJJiBDNdFiPJdqr37mYQVIzOWr+ROhyb1hpmg/QCM2qkIEWJfrJX3I+rwm0t0p4EGdEVOXQj7Z8vHFcbiA="
The
tickets
option works as follows:
Boolean values enable or disable session tickets;
with
true
, a random session ticket key is used:
"session"
"tickets"
true
A string enables tickets
and explicitly sets the session ticket key:
"session"
tickets
"IAMkP16P8OBuqsijSDGKTpmxrzfFNPP4EdRovXH2mqstXsodPC6MqIce5NlMzHLP"
This enables ticket reuse in scenarios
where the key is shared between individual servers.
Shared key rotation
If multiple Unit instances need to recognize tickets
issued by each other
(for example, when running behind a load balancer),
they should share session ticket keys.
For example,
consider three SSH-enabled servers named
unit*.example.com
with Unit installed and identical
*:443
listeners configured.
To configure a single set of three initial keys on each server:
SERVERS
"unit1.example.com
unit2.example.com
unit3.example.com"
KEY1
$(
openssl
rand
-base64
48
KEY2
$(
openssl
rand
-base64
48
KEY3
$(
openssl
rand
-base64
48
for
SRV
in
$SERVERS
do
ssh
root
$SRV
curl
-X
PUT
-d
'["$KEY1", "$KEY2", "$KEY3"]'
--unix-socket
/path/to/control.unit.sock
'http://localhost/config/listeners/*:443/tls/session/tickets/'
done
To add a new key on each server:
NEWKEY
$(
openssl
rand
-base64
48
for
SRV
in
$SERVERS
do
ssh
root
$SRV
curl
-X
POST
-d
'\"$NEWKEY\"'
--unix-socket
/path/to/control.unit.sock
'http://localhost/config/listeners/*:443/tls/session/tickets/'
done
To delete the oldest key after adding the new one:
for
SRV
in
$SERVERS
do
ssh
root
$SRV
curl
-X
DELETE
--unix-socket
/path/to/control.unit.sock
'http://localhost/config/listeners/*:443/tls/session/tickets/0'
done
This scheme enables safely sharing session ticket keys
between individual Unit instances.
Unit supports AES256 (80-byte keys) or AES128 (48-byte keys);
the bytes should be encoded in Base64:
openssl
rand
-base64
48
LoYjFVxpUFFOj4TzGkr5MsSIRMjhuh8RCsVvtIJiQ12FGhn0nhvvQsEND1+OugQ7
openssl
rand
-base64
80
GQczhdXawyhTrWrtOXI7l3YYUY98PrFYzjGhBbiQsAWgaxm+mbkm4MmZZpDw0tkK
YTqYWxofDtDC4VBznbBwTJTCgYkJXknJc4Gk2zqD1YA=
An array of strings just like the one above:
"session"
tickets
"IAMkP16P8OBuqsijSDGKTpmxrzfFNPP4EdRovXH2mqstXsodPC6MqIce5NlMzHLP"
"Ax4bv/JvMWoQG+BfH0feeM9Qb32wSaVVKOj1+1hmyU8ORMPHnf3Tio8gLkqm2ifC"
Unit uses these keys to decrypt the tickets submitted by clients
who want to recover their session state;
the last key is always used to create new session tickets
and update the tickets created earlier.
Note
An empty array effectively disables session tickets,
same as setting
tickets
to
false
IP, protocol forwarding
Unit enables the
X-Forwarded-*
header fields
with the
forwarded
object and its options:
Option
Description
source
(required)
String or an array of strings;
defines
address-based patterns
for trusted addresses.
Replacement occurs only if the source IP of the request is a
match
A special case here is the
“unix”
string;
it matches
any
UNIX domain sockets.
client_ip
String;
names the HTTP header fields to expect in the request.
They should use the
X-Forwarded-For
format where the value is a comma- or space-separated list
of IPv4s or IPv6s.
protocol
String;
defines the relevant HTTP header field to look for in the request.
Unit expects it to follow the
X-Forwarded-Proto
notation,
with the field value itself
being
http
https
, or
on
recursive
Boolean;
controls how the
client_ip
fields are traversed.
The default is
false
(no recursion).
Note
Besides
source
the
forwarded
object must specify
client_ip
protocol
, or both.
Warning
Before version 1.28.0,
Unit provided the
client_ip
object
that evolved into
forwarded
client_ip
(pre-1.28.0)
forwarded
(post-1.28.0)
header
client_ip
recursive
recursive
source
source
N/A
protocol
This old syntax still works but will be eventually deprecated,
though not earlier than version 1.30.0.
When
forwarded
is set,
Unit respects the appropriate header fields
only if the immediate source IP of the request
matches
the
source
option.
Mind that it can use not only subnets but any
address-based patterns
"forwarded"
"client_ip"
"X-Forwarded-For"
"source"
198.51.100.1-198.51.100.254
!198.51.100.128/26
203.0.113.195
Overwriting protocol scheme
The
protocol
option enables overwriting
the incoming request’s protocol scheme
based on the header field it specifies.
Consider the following
forwarded
configuration:
"forwarded"
"protocol"
"X-Forwarded-Proto"
"source"
"192.0.2.0/24"
"198.51.100.0/24"
Suppose a request arrives with the following header field:
X-Forwarded-Proto: https
If the source IP of the request matches
source
Unit handles this request as an
https
one.
Originating IP identification
Unit also supports identifying the clients’ originating IPs
with the
client_ip
option:
"forwarded"
"client_ip"
"X-Forwarded-For"
"recursive"
false
"source"
"192.0.2.0/24"
"198.51.100.0/24"
Suppose a request arrives with the following header fields:
X-Forwarded-For: 192.0.2.18
X-Forwarded-For: 203.0.113.195, 198.51.100.178
If
recursive
is set to
false
(default),
Unit chooses the
rightmost
address of the
last
field
named in
client_ip
as the originating IP of the request.
In the example,
it’s set to 198.51.100.178 for requests from 192.0.2.0/24 or 198.51.100.0/24.
If
recursive
is set to
true
Unit inspects all
client_ip
fields in reverse order.
Each is traversed from right to left
until the first non-trusted address;
if found, it’s chosen as the originating IP.
In the previous example with
“recursive”: true
the client IP would be set to 203.0.113.195
because 198.51.100.178 is also trusted;
this simplifies working behind multiple reverse proxies.
Routes
The
config/routes
configuration entity
defines internal request routing.
It receives requests
from
listeners
and filters them through
sets of conditions
to be processed by
apps
proxied
to external servers or
load-balanced
between them,
served with
static content
answered
with arbitrary status codes, or
redirected
In its simplest form,
routes
is an array
that defines a single route:
"listeners"
"*:8300"
"pass"
"routes"
},
routes
...
Another form is an object
with one or more named route arrays as members:
"listeners"
"*:8300"
"pass"
"routes/main"
},
"routes"
main
...
],
route66
...
Route steps
route
array contains step objects as elements;
they accept the following options:
Option
Description
action
(required)
Object;
defines how matching requests are
handled
match
Object;
defines the step’s
conditions
to be matched.
A request passed to a route traverses its steps sequentially:
If all
match
conditions in a step are met,
the traversal ends
and the step’s
action
is performed.
If a step’s condition isn’t met,
Unit proceeds to the next step of the route.
If no steps of the route match,
a 404 “Not Found” response is returned.
Warning
If a step omits the
match
option,
its
action
occurs automatically.
Thus, use no more than one such step per route,
always placing it last to avoid potential routing issues.
Ad-Hoc examples
A basic one:
"routes"
"match"
"host"
"example.com"
"scheme"
"https"
"uri"
"/php/*"
},
"action"
"pass"
"applications/php_version"
},
"action"
"share"
"/www/static_version
$uri
This route passes all HTTPS requests
to the
/php/
subsection of the
example.com
website
to the
php_version
app.
All other requests are served with static content
from the
/www/static_version/
directory.
If there’s no matching content,
a 404 “Not Found” response is returned.
A more elaborate example with chained routes and proxying:
"routes"
"main"
"match"
"scheme"
"http"
},
"action"
"pass"
"routes/http_site"
},
"match"
"host"
"blog.example.com"
},
"action"
"pass"
"applications/blog"
},
"match"
"uri"
"*.css"
"*.jpg"
"*.js"
},
"action"
"share"
"/www/static
$uri
],
"http_site"
"match"
"uri"
"/v2_site/*"
},
"action"
"pass"
"applications/v2_site"
},
"action"
"proxy"
"http://127.0.0.1:9000"
Here, a route called
main
is explicitly defined,
so
routes
is an object instead of an array.
The first step of the route passes all HTTP requests
to the
http_site
app.
The second step passes all requests
that target
blog.example.com
to the
blog
app.
The final step serves requests for certain file types
from the
/www/static/
directory.
If no steps match,
a 404 “Not Found” response is returned.
Matching conditions
Conditions in a
route step
’s
match
object
define patterns to be compared to the request’s properties:
Property
Patterns Are Matched Against
Case‑
Sensitive
arguments
Arguments supplied with the request’s
query string
these names and value pairs are
percent decoded
with plus signs
replaced by spaces.
Yes
Cookies supplied with the request.
Yes
destination
Target IP address and optional port of the request.
No
headers
Header fields
supplied with the request.
No
host
Host
header field
converted to lower case and normalized
by removing the port number and the trailing period
(if any).
No
method
Method
from the request line,
uppercased.
No
query
Query string
percent decoded
with plus signs
replaced by spaces.
Yes
scheme
URI
scheme
Accepts only two patterns,
either
http
or
https
No
source
Source IP address and optional port of the request.
No
uri
Request target
percent decoded
and normalized
by removing the
query string
and resolving
relative references
(“.” and “..”, “//”).
Yes
Arguments vs. query
Both
arguments
and
query
operate on the query string,
but
query
is matched against the entire string
whereas
arguments
considers only the key-value pairs
such as
key1=4861&key2=a4f3
Use
arguments
to define conditions
based on key-value pairs in the query string:
"arguments"
"key1"
"4861"
"key2"
"a4f3"
Argument order is irrelevant:
key1=4861&key2=a4f3
and
key2=a4f3&key1=4861
are considered the same.
Also, multiple occurrences of an argument must all match,
so
key=4861&key=a4f3
matches this:
"arguments"
:{
"key"
"*"
But not this:
"arguments"
:{
"key"
"a*"
To the contrary,
use
query
if your conditions concern query strings
but don’t rely on key-value pairs:
"query"
"utf8"
"utf16"
This only matches query strings
of the form
or
Match resolution
To be a match,
the property must meet two requirements:
If there are patterns without negation
(the
prefix),
at least one of them matches the property value.
No negated patterns match the property value.
Formal explanation
This logic can be described with set operations.
Suppose set
comprises all possible values of a property;
set
comprises strings that match any patterns without negation;
set
comprises strings that match any negation-based patterns.
In this scheme,
the matching set is:
if
≠ ∅
if
= ∅
Here, the URI of the request must fit
pattern3
but must not match
pattern1
or
pattern2
"match"
"uri"
"!pattern1"
"!pattern2"
"pattern3"
},
"action"
"pass"
...
Additionally, special matching logic applies to
arguments
, and
headers
Each of these can be either
a single object that lists custom-named properties and their patterns
or an array of such objects.
To match a single object,
the request must match
all
properties named in the object.
To match an object array,
it’s enough to match
any
single one of its item objects.
The following condition matches only
if the request arguments include
arg1
and
arg2
and both match their patterns:
"match"
"arguments"
"arg1"
"pattern"
"arg2"
"pattern"
},
"action"
"pass"
...
With an object array,
the condition matches
if the request’s arguments include
arg1
or
arg2
(or both)
that matches the respective pattern:
"match"
"arguments"
"arg1"
"pattern"
},
"arg2"
"pattern"
},
"action"
"pass"
...
The following example combines all matching types.
Here,
host
method
uri
arg1
and
arg2
either
cookie1
or
cookie2
and either
header1
or
header2
and
header3
must be matched
for the
action
to be taken
host & method & uri & arg1 & arg2 & (cookie1 | cookie2)
& (header1 | (header2 & header3))
):
"match"
"host"
"pattern"
"method"
"!pattern"
"uri"
"pattern"
"!pattern"
],
"arguments"
"arg1"
"pattern"
"arg2"
"!pattern"
},
"cookies"
"cookie1"
"pattern"
},
"cookie2"
"pattern"
],
"headers"
"header1"
"pattern"
},
"header2"
"pattern"
"header3"
"pattern"
},
"action"
"pass"
...
Object pattern examples
This requires
mode=strict
and any
access
argument other than
access=full
in the URI query:
"match"
"arguments"
"mode"
"strict"
"access"
"!full"
},
"action"
"pass"
...
This matches requests that
either use
gzip
and identify as
Mozilla/5.0
or list
curl
as the user agent:
"match"
"headers"
"Accept-Encoding"
"*gzip*"
"User-Agent"
"Mozilla/5.0*"
},
"User-Agent"
"curl*"
},
"action"
"pass"
...
Pattern syntax
Individual patterns can be
address-based
source
and
destination
or string-based
(other properties).
String-based patterns must match the property to a character;
wildcards or
regexes
modify this behavior:
A wildcard pattern may contain any combination of wildcards
),
each standing for an arbitrary number of characters:
How*s*that*to*you
A regex pattern starts with a tilde
):
~^\d+\.\d+\.\d+\.\d+
(escaping backslashes is a
JSON requirement
).
The regexes are
PCRE
-flavored.
Percent encoding in arguments, query, and URI patterns
Argument names, non-regex string patterns in
arguments
query
, and
uri
can be
percent encoded
to mask special characters
is
%21
is
%7E
is
%2A
is
%25
or even target single bytes.
For example, you can select diacritics such as Ö or Å
by their starting byte
0xC3
in UTF-8:
"match"
"arguments"
"word"
"*%C3*"
},
"action"
"pass"
...
Unit decodes such strings
and matches them against respective request entities,
decoding these as well:
"routes"
"match"
"query"
%7E
fuzzy word search"
},
"action"
"return"
200
This condition matches the following percent-encoded request:
curl
%20
word
%20
-v
> GET /?~fuzzy%20word%20search HTTP/1.1
...
< HTTP/1.1 200 OK
...
Note that the encoded spaces
%20
in the request
match their unencoded counterparts in the pattern;
vice versa, the encoded tilde
%7E
in the condition matches
in the request.
String pattern examples
A regular expression that matches any
.php
files
in the
/data/www/
directory and its subdirectories.
Note the backslashes;
escaping is a JSON-specific requirement:
"match"
"uri"
"~^/data/www/.*\\.php(/.*)?$"
},
"action"
"pass"
...
Only subdomains of
example.com
match:
"match"
"host"
"*.example.com"
},
"action"
"pass"
...
Only requests for
.php
files
located in
/admin/
’s subdirectories
match:
"match"
"uri"
"/admin/*/*.php"
},
"action"
"pass"
...
Here, any
eu-
subdomains of
example.com
match
except
eu-5.example.com
"match"
"host"
"eu-*.example.com"
"!eu-5.example.com"
},
"action"
"pass"
...
Any methods match
except
HEAD
and
GET
"match"
"method"
"!HEAD"
"!GET"
},
"action"
"pass"
...
You can also combine certain special characters in a pattern.
Here, any URIs match
except the ones containing
/api/
"match"
"uri"
"!*/api/*"
},
"action"
"pass"
...
Here, URIs of any articles
that don’t look like
YYYY-MM-DD
dates
match.
Again, note the backslashes;
they are a JSON requirement:
"match"
"uri"
"/articles/*"
"!~/articles/\\d{4}-\\d{2}-\\d{2}"
},
"action"
"pass"
...
Address-based patterns define individual IPv4
(dot-decimal or
CIDR
),
IPv6 (hexadecimal or
CIDR
),
or any
UNIX domain socket
addresses
that must exactly match the property;
wildcards and ranges modify this behavior:
Wildcards
can only match arbitrary IPs
*:
).
Ranges
work with both IPs
(in respective notation)
and ports
).
Address-based allow-deny lists
Addresses come in handy
when implementing an allow-deny mechanism
with routes,
for instance:
"routes"
"match"
"source"
"!192.168.1.1"
"!10.1.1.0/16"
"192.168.1.0/24"
"2001:0db8::/32"
},
"action"
"share"
"/www/data
$uri
See
here
for details of pattern resolution order;
this corresponds to the following
nginx
directive:
location
deny
10
.1.1.0/16
deny
192
.168.1.1
allow
192
.168.1.0/24
allow
2001
db8::/32
deny
all
root
/www/data
Address pattern examples
This uses IPv4-based matching with wildcards and ranges:
"match"
"source"
"192.0.2.1-192.0.2.200"
"198.51.100.1-198.51.100.200:8000"
"203.0.113.1-203.0.113.200:8080-8090"
"*:80"
],
"destination"
"192.0.2.0/24"
"198.51.100.0/24:8000"
"203.0.113.0/24:8080-8090"
"*:80"
},
"action"
"pass"
...
This uses IPv6-based matching with wildcards and ranges:
"match"
"source"
"2001:0db8::-2001:0db8:aaa9:ffff:ffff:ffff:ffff:ffff"
"[2001:0db8:aaaa::-2001:0db8:bbbb::]:8000"
"[2001:0db8:bbbb::1-2001:0db8:cccc::]:8080-8090"
"*:80"
],
"destination"
"2001:0db8:cccd::/48"
"[2001:0db8:ccce::/48]:8000"
"[2001:0db8:ccce:ffff::/64]:8080-8090"
"*:80"
},
"action"
"pass"
...
This matches any of the listed IPv4 or IPv6 addresses:
"match"
"destination"
"127.0.0.1"
"192.168.0.1"
"::1"
"2001:0db8:1::c0a8:1"
},
"action"
"pass"
...
Here, any IPs from the range match
except
192.0.2.9
"match"
"source"
"192.0.2.1-192.0.2.10"
"!192.0.2.9"
},
"action"
"pass"
...
This matches any IPs but limits the acceptable ports:
"match"
"source"
"*:80"
"*:443"
"*:8000-8080"
},
"action"
"pass"
...
This matches any UNIX domain sockets:
"match"
"source"
"unix"
},
"action"
"pass"
...
Handling actions
If a request matches all
conditions
of a route step
or the step itself omits the
match
object,
Unit handles the request with the respective
action
The mutually exclusive
action
types are:
Option
Description
Details
pass
Destination for the request,
identical to a listener’s
pass
option.
Listeners
proxy
Socket address of an HTTP server
to where the request is proxied.
Proxying
return
HTTP status code
with a context-dependent redirect location.
Instant responses, redirects
share
File paths that serve the request with static content.
Static files
An additional option is applicable to any of these actions:
Option
Description
Details
response_headers
Updates the header fields
of the upcoming response.
Response headers
rewrite
Updated the request URI,
preserving the query string.
URI rewrite
An example:
"routes"
"match"
"uri"
"/v1/*"
"/v2/*"
},
"action"
"rewrite"
"/app/
$uri
"pass"
"applications/app"
},
"match"
"uri"
"~\\.jpe?g$"
},
"action"
"share"
"/var/www/static
$uri
"/var/www/static/assets
$uri
],
"fallback"
"pass"
"upstreams/cdn"
},
"match"
"uri"
"/proxy/*"
},
"action"
"proxy"
"http://192.168.0.100:80"
},
"match"
"uri"
"/return/*"
},
"action"
"return"
301
"location"
"https://www.example.com"
Variables
Some options in Unit configuration
allow the use of
variables
whose values are calculated at runtime.
There’s a number of built-in variables available:
Variable
Description
arg_*
cookie_*
header_*
Variables that store
request arguments, cookies, and header fields
such as
arg_queryTimeout
cookie_sessionId
or
header_Accept_Encoding
The names of the
header_*
variables are case insensitive.
body_bytes_sent
Number of bytes sent in the response body.
dollar
Literal dollar sign (
),
used for escaping.
header_referer
Contents of the
Referer
request
header field
header_user_agent
Contents of the
User-Agent
request
header field
host
Host
header field
converted to lower case and normalized
by removing the port number
and the trailing period (if any).
method
Method
from the request line.
remote_addr
Remote IP address of the request.
request_id
Contains a string generated with random data. Can be used as a unique
request identifier.
request_line
Entire
request line
request_time
Request processing time in milliseconds,
formatted as follows:
1.234
request_uri
Request target
path
including
the
query
normalized by resolving relative path references
(“.” and “..”)
and collapsing adjacent slashes.
response_header_*
Variables that store
response header fields
such as
response_header_content_type
The names of these variables are case insensitive.
status
HTTP
status code
of the response.
time_local
Local time,
formatted as follows:
31/Dec/1986:19:40:00 +0300
uri
Request target
path
without
the
query
part,
normalized by resolving relative path references
(“.” and “..”)
and collapsing adjacent slashes.
The value is
percent decoded
Unit interpolates all percent-encoded entities
in the request target
path
These variables can be used with:
pass
in
listeners
and
actions
to choose between routes, applications, app targets, or upstreams.
rewrite
in
actions
to enable
URI rewriting
share
and
chroot
in
actions
to control
static content serving
location
in
return
actions
to enable HTTP redirects.
format
in the
access log
to customize Unit’s log output.
To reference a variable,
prefix its name with the dollar sign character
),
optionally enclosing the name in curly brackets
{}
to separate it from adjacent text
or enhance visibility.
Variable names can contain letters and underscores
),
so use the brackets
if the variable is immediately followed by such characters:
"listeners"
"*:80"
"pass"
"routes/
${method}
_route"
},
"routes"
"GET_route"
"action"
"return"
201
],
"PUT_route"
"action"
"return"
202
],
"POST_route"
"action"
"return"
203
To reference an
arg_*
cookie_*
or
header_*
variable,
add the name you need to the prefix.
A query string of
Type=car&Color=red
yields two variables,
$arg_Type
and
$arg_Color
Unit additionally normalizes capitalization and hyphenation
in header field names,
so the
Accept-Encoding
header field
can also be referred to as
$header_Accept_Encoding
$header_accept-encoding
or
$header_accept_encoding
Note
With multiple argument instances
(think
Color=Red&Color=Blue
),
the rightmost one is used (
Blue
).
At runtime,
variables expand into dynamically computed values
(at your risk!).
The previous example targets an entire set of routes,
picking individual ones by HTTP verbs
from the incoming requests:
curl
-i
-X
GET
HTTP/1.1 201 Created
curl
-i
-X
PUT
HTTP/1.1 202 Accepted
curl
-i
-X
POST
HTTP/1.1 203 Non-Authoritative Information
curl
-i
--head
# Bumpy ride ahead, no route defined
HTTP/1.1 404 Not Found
If you reference a non-existing variable,
it is considered empty.
Examples
This configuration selects the static file location
based on the requested hostname;
if nothing’s found,
it attempts to retrieve the requested file
from a common storage:
"listeners"
"*:80"
"pass"
"routes"
},
"routes"
"action"
"share"
"/www/
$host
$uri
"/www/storage
$uri
Another use case is employing the URI
to choose between applications:
"listeners"
"*:80"
"pass"
"applications
$uri
},
"applications"
"blog"
"root"
"/path/to/blog_app/"
"script"
"index.php"
},
"sandbox"
"type"
"php"
"root"
"/path/to/sandbox_app/"
"script"
"index.php"
This way, requests are routed between applications by their target URIs:
curl
# Targets the 'blog' app
curl
# Targets the 'sandbox' app
A different approach puts the
Host
header field
received from the client
to the same use:
"listeners"
"*:80"
"pass"
"applications/
$host
},
"applications"
"localhost"
"root"
"/path/to/admin_section/"
"script"
"index.php"
},
"www.example.com"
"type"
"php"
"root"
"/path/to/public_app/"
"script"
"index.php"
You can use multiple variables in a string,
repeating and placing them arbitrarily.
This configuration picks an app target
(supported for
PHP
and
Python
apps)
based on the requested hostname and URI:
"listeners"
"*:80"
"pass"
"applications/app_
$host
$uri
At runtime,
a request for
example.com/myapp
is passed to
applications/app_example.com/myapp
To select a share directory
based on an
app_session
cookie:
"action"
"share"
"/data/www/
$cookie_app_session
Here, if
$uri
in
share
resolves to a directory,
the choice of an index file to be served
is dictated by
index
"action"
"share"
"/www/data
$uri
"index"
"index.htm"
Here, a redirect uses the
$request_uri
variable value
to relay the request,
including
the query part,
to the same website over HTTPS:
"action"
"return"
301
"location"
"https://
$host
$request_uri
URI rewrite
All route step
actions
support the
rewrite
option
that updates the URI of the incoming request
before the action is applied.
It does not affect the
query
but changes the
uri
and
$request_uri
variables
This
match
-less action
prefixes the request URI with
/v1
and returns it to routing:
"action"
"rewrite"
"/v1
$uri
"pass"
"routes"
Warning
Avoid infinite loops
when you
pass
requests
back to
routes
This action
normalizes the request URI
and passes it to an application:
"match"
"uri"
"/fancyAppA"
"/fancyAppB"
},
"action"
"rewrite"
"/commonBackend"
"pass"
"applications/backend"
Response headers
All route step
actions
support the
response_headers
option
that updates the header fields of Unit’s response
before the action is taken:
"action"
"share"
"/www/static/
$uri
"response_headers"
"Cache-Control"
"max-age=60, s-maxage=120"
"CDN-Cache-Control"
"max-age=600"
This works only for the
2XX
and
3XX
responses;
also,
Date
Server
, and
Content-Length
can’t be set.
The option sets given string values
for the header fields of the response
that Unit will send for the specific request:
If there’s no header field associated with the name
(regardless of the case),
the value is set.
If a header field with this name is already set, its value is updated.
If
null
is supplied for the value, the header field is
deleted
If the action is taken and Unit issues a response,
it sends the header fields
this specific
action specifies.
Only the last action
along the entire routing path of a request
affects the resulting response headers.
The values support
variables
and
template literals
which enables arbitrary runtime logic:
"response_headers"
"Content-Language"
${
uri
startsWith
'/uk'
'en-GB'
'en-US'
Finally, there are the
response_header_*
variables
that evaluate to the header field values set with the response
(by the app, upstream, or Unit itself;
the latter is the case with
$response_header_connection
$response_header_content_length
and
$response_header_transfer_encoding
).
One use is to update the headers in the final response;
this extends the
Content-Type
issued by the app:
"action"
"pass"
"applications/converter"
"response_headers"
"Content-Type"
${response_header_content_type}
;charset=iso-8859-1"
Alternatively, they will come in handy with
custom log formatting
Instant responses, redirects
You can use route step
actions
to instantly handle certain conditions
with arbitrary
HTTP status codes
"match"
"uri"
"/admin_console/*"
},
"action"
"return"
403
The
return
action provides the following options:
return
(required)
Integer (000–999);
defines the HTTP response status code
to be returned.
location
String URI;
used if the
return
value implies redirection.
Use the codes according to their intended
semantics
if you use custom codes,
make sure that user agents can understand them.
If you specify a redirect code (3xx),
supply the destination
using the
location
option
alongside
return
"action"
"return"
301
"location"
"https://www.example.com"
Besides enriching the response semantics,
return
simplifies
allow-deny lists
instead of guarding each action with a filter,
add
conditions
to deny unwanted requests as early as possible,
for example:
"routes"
"match"
"scheme"
"http"
},
"action"
"return"
403
},
"match"
"source"
"!192.168.1.1"
"!10.1.1.0/16"
"192.168.1.0/24"
"2001:0db8::/32"
},
"action"
"return"
403
Static files
Unit is capable of acting as a standalone web server,
efficiently serving static files
from the local file system;
to use the feature,
list the file paths
in the
share
option
of a route step
action
share
-based action provides the following options:
share
(required)
String or an array of strings;
lists file paths that are tried
until a file is found.
When no file is found,
fallback
is used if set.
The value is
variable
-interpolated.
index
Filename;
tried if
share
is a directory.
When no file is found,
fallback
is used if set.
The default is
index.html
fallback
Action-like
object
used if the request
can’t be served by
share
or
index
types
Array
of
MIME type
patterns;
used to filter the shared files.
chroot
Directory pathname that
restricts
the shareable paths.
The value is
variable
-interpolated.
follow_symlinks
traverse_mounts
Booleans;
turn on and off symbolic link and mount point
resolution
respectively;
if
chroot
is set,
they only
affect
the insides of
chroot
The default for both options is
true
(resolve links and mounts).
Note
To serve the files,
Unit’s router process must be able to access them;
thus, the account this process runs as
must have proper permissions
assigned
When Unit is installed from the
official packages
the process runs as
unit:unit
for details of other installation methods,
see
Installation
Consider the following configuration:
"listeners"
"*:80"
"pass"
"routes"
},
"routes"
"action"
"share"
"/www/static/
$uri
It uses
variable interpolation
Unit replaces the
$uri
reference
with its current value
and tries the resulting path.
If this doesn’t yield a servable file,
a 404 “Not Found” response is returned.
Warning
Before version 1.26.0,
Unit used
share
as the document root.
This was changed for flexibility,
so now
share
must resolve to specific files.
A common solution is
to append
$uri
to your document root.
Pre-1.26,
the snippet above would’ve looked like this:
"action"
"share"
"/www/static/"
Mind that URI paths always start with a slash,
so there’s no need to separate the directory
from
$uri
even if you do, Unit compacts adjacent slashes
during path resolution,
so there won’t be an issue.
If
share
is an array,
its items are searched in order of appearance
until a servable file is found:
"share"
"/www/
$host
$uri
"/www/error_pages/not_found.html"
This snippet tries a
$host
-based directory first;
if a suitable file isn’t found there,
the
not_found.html
file is tried.
If neither is accessible,
a 404 “Not Found” response is returned.
Finally, if a file path points to a directory,
Unit attempts to serve an
index
-indicated file from it.
Suppose we have the following directory structure
and share configuration:
/www/static/
├── ...
└──default.html
"action"
"share"
"/www/static
$uri
"index"
"default.html"
The following request returns
default.html
even though the file isn’t named explicitly:
curl
-v
...
< HTTP/1.1 200 OK
< Last-Modified: Fri, 20 Sep 2021 04:14:43 GMT
< ETag: "5d66459d-d"
< Content-Type: text/html
< Server: Unit/1.34.2
...
Note
Unit’s ETag response header fields
use the
MTIME-FILESIZE
format,
where
MTIME
stands for file modification timestamp
and
FILESIZE
stands for file size in bytes,
both in hexadecimal.
MIME filtering
To filter the files a
share
serves
by their
MIME types
define a
types
array of string patterns.
They work like
route patterns
but are compared to the MIME type of each file;
the request is served only if it’s a
match
"share"
"/www/data/static
$uri
"types"
"!text/javascript"
"!text/css"
"text/*"
"~video/3gpp2?"
This sample configuration blocks JS and CSS files with
negation
but allows all other text-based MIME types with a
wildcard pattern
Additionally, the
.3gpp
and
.3gpp2
file types
are allowed by a
regex pattern
If no MIME types match the request, a 403 “Forbidden” response is
returned. You can pair that behavior with a
fallback
option that will be called
when a 40x response would be returned.
"share"
"/www/data/static
$uri
"types"
"image/*"
"font/*"
"text/*"
],
"response_headers"
"Cache-Control"
"max-age=1209600"
},
"fallback"
"share"
"/www/data/static
$uri
Here, all requests to images, fonts, and any text-based files will have
a cache control header added to the response. Any other requests will still
serve the files, but this time without the header. This is useful
for serving common web page resources that do not change; web browsers
and proxies are informed that this content should be cached.
If the MIME type of a requested file isn’t recognized,
it’s considered empty
“”
).
Thus, the
“!”
pattern
(“deny empty strings”)
can be used to restrict all file types
unknown
to Unit:
"share"
"/www/data/known-types-only
$uri
"types"
"!"
If a share path specifies only the directory name,
Unit
doesn’t
apply MIME filtering.
Path restrictions
Note
To have these options,
Unit must be built and run
on a system with Linux kernel version 5.6+.
The
chroot
option confines the path resolution
within a share to a certain directory.
First, it affects symbolic links:
any attempts to go up the directory tree
with relative symlinks like
../../var/log
stop at the
chroot
directory,
and absolute symlinks are treated as relative
to this directory to avoid breaking out:
"action"
"share"
"/www/data
$uri
"chroot"
/www/data/
Here, a request for
/log
initially resolves to
/www/data/log
however, if that’s an absolute symlink to
/var/log/app.log
the resulting path is
/www/data/var/log/app.log
Another effect is that any requests
for paths that resolve outside the
chroot
directory
are forbidden:
"action"
"share"
"/www
$uri
"chroot"
/www/data/
Here, a request for
/index.xml
elicits a 403 “Forbidden” response
because it resolves to
/www/index.xml
which is outside
chroot
The
follow_symlinks
and
traverse_mounts
options
disable resolution of symlinks and traversal of mount points
when set to
false
(both default to
true
):
"action"
"share"
"/www/
$host
/static
$uri
"follow_symlinks"
false
"traverse_mounts"
false
Here, any symlink or mount point in the entire
share
path
results in a 403 “Forbidden” response.
With
chroot
set,
follow_symlinks
and
traverse_mounts
only affect portions of the path
after
chroot
"action"
"share"
"/www/
$host
/static
$uri
"chroot"
"/www/
$host
/"
"follow_symlinks"
false
"traverse_mounts"
false
Here,
www/
and interpolated
$host
can be symlinks or mount points,
but any symlinks and mount points beyond them,
including the
static/
portion,
won’t be resolved.
Details
Suppose you want to serve files from a share
that itself includes a symlink
(let’s assume
$host
always resolves to
localhost
and make it a symlink in our example)
but disable any symlinks inside the share.
Initial configuration:
"action"
"share"
"/www/
$host
/static
$uri
"chroot"
/www/
$host
Create a symlink to
/www/localhost/static/index.html
mkdir
-p
/www/localhost/static/
&&
cd
/www/localhost/static/
cat
index.html
<<
EOF
> index.html
> EOF
ln
-s
index.html
/www/localhost/static/symlink
If symlink resolution is enabled
(with or without
chroot
),
a request that targets the symlink works:
curl
index.html
curl
index.html
Now set
follow_symlinks
to
false
"action"
"share"
"/www/
$host
/static
$uri
"chroot"
/www/
$host
"follow_symlinks"
false
The symlink request is forbidden,
which is presumably the desired effect:
curl
index.html
curl
Lastly, what difference does
chroot
make?
To see, remove it:
"action"
"share"
"/www/
$host
/static
$uri
"follow_symlinks"
false
Now,
“follow_symlinks”: false
affects the entire share,
and
localhost
is a symlink,
so it’s forbidden:
curl
Fallback action
Finally, within an
action
you can supply a
fallback
option
beside a
share
It specifies the
action
to be taken
if the requested file can’t be served
from the
share
path:
"share"
"/www/data/static
$uri
"fallback"
"pass"
"applications/php"
Serving a file can be impossible for different reasons, such as:
The request’s HTTP method isn’t
GET
or
HEAD
The file’s MIME type doesn’t match the
types
array
The file isn’t found at the
share
path.
The router process has
insufficient permissions
to access the file or an underlying directory.
In the previous example,
an attempt to serve the requested file
from the
/www/data/static/
directory
is made first.
Only if the file can’t be served,
the request is passed to the
php
application.
If the
fallback
itself is a
share
it can also contain a nested
fallback
"share"
"/www/data/static
$uri
"fallback"
"share"
"/www/cache
$uri
"chroot"
"/www/"
"fallback"
"proxy"
"http://127.0.0.1:9000"
The first
share
tries to serve the request
from
/www/data/static/
on failure, the second
share
tries the
/www/cache/
path
with
chroot
enabled.
If both attempts fail,
the request is proxied elsewhere.
Examples
One common use case that this feature enables
is the separation of requests
for static and dynamic content
into independent routes.
The following example relays all requests
that target
.php
files
to an application
and uses a catch-all static
share
with a
fallback
"routes"
"match"
"uri"
"*.php"
},
"action"
"pass"
"applications/php-app"
},
"action"
"share"
"/www/php-app/assets/files
$uri
"fallback"
"proxy"
"http://127.0.0.1:9000"
],
"applications"
"php-app"
"type"
"php"
"root"
"/www/php-app/scripts/"
You can reverse this scheme for apps
that avoid filenames in dynamic URIs,
listing all types of static content
to be served from a
share
in a
match
condition
and adding an unconditional application path:
"routes"
"match"
"uri"
"*.css"
"*.ico"
"*.jpg"
"*.js"
"*.png"
"*.xml"
},
"action"
"share"
"/www/php-app/assets/files
$uri
"fallback"
"proxy"
"http://127.0.0.1:9000"
},
"action"
"pass"
"applications/php-app"
],
"applications"
"php-app"
"type"
"php"
"root"
"/www/php-app/scripts/"
If image files should be served locally
and other proxied,
use the
types
array
in the first route step:
"match"
"uri"
"*.css"
"*.ico"
"*.jpg"
"*.js"
"*.png"
"*.xml"
},
"action"
"share"
"/www/php-app/assets/files
$uri
"types"
"image/*"
],
"fallback"
"proxy"
"http://127.0.0.1:9000"
Another way to combine
share
types
, and
fallback
is exemplified by the following compact pattern:
"share"
"/www/php-app/assets/files
$uri
"types"
"!application/x-httpd-php"
],
"fallback"
"pass"
"applications/php-app"
It forwards explicit requests for PHP files
to the app
while serving all other types of files
from the share;
note that a
match
object
isn’t needed here to achieve this effect.
Proxying
Unit’s routes support HTTP proxying
to socket addresses
using the
proxy
option
of a route step
action
"routes"
"match"
"uri"
"/ipv4/*"
},
"action"
"proxy"
},
"match"
"uri"
"/ipv6/*"
},
"action"
"proxy"
},
"match"
"uri"
"/unix/*"
},
"action"
"proxy"
As the example suggests,
you can use UNIX, IPv4, and IPv6 socket addresses
for proxy destinations.
Note
The HTTPS scheme is not supported yet.
Load balancing
Besides proxying requests to individual servers,
Unit can also relay incoming requests to
upstreams
An upstream is a group of servers
that comprise a single logical entity
and may be used as a
pass
destination
for incoming requests in a
listener
or a
route
Upstreams are defined
in the eponymous
/config/upstreams
section of the API:
"listeners"
"*:80"
"pass"
"upstreams/rr-lb"
},
"upstreams"
rr-lb
servers
192.168.0.100:8080
{},
"192.168.0.101:8080"
"weight"
0.5
An upstream must define a
servers
object
that lists socket addresses
as server object names.
Unit dispatches requests between the upstream’s servers
in a round-robin fashion,
acting as a load balancer.
Each server object can set a numeric
weight
to adjust the share of requests
it receives via the upstream.
In the above example,
192.168.0.100:8080
receives twice as many requests
as
192.168.0.101:8080
Weights can be specified as integers or fractions
in decimal or scientific notation:
"servers"
"192.168.0.100:8080"
weight
1e1
},
"192.168.0.101:8080"
weight
10.0
},
"192.168.0.102:8080"
weight
10
The maximum weight is
1000000
the minimum is
(such servers receive no requests);
the default is
Applications
Each app that Unit runs
is defined as an object
in the
/config/applications
section of the control API;
it lists the app’s language and settings,
its runtime limits,
process model,
and various language-specific options.
Note
Our official
language-specific packages
include end-to-end examples of application configuration,
available for your reference at
/usr/share/doc/
after package installation.
Here, Unit runs 20 processes of a PHP app called
blogs
stored in the
/www/blogs/scripts/
directory:
"blogs"
"type"
"php"
"processes"
20
"root"
"/www/blogs/scripts/"
App objects have a number of options
shared between all application languages:
Option
Description
type
(required)
Application type:
external
(Go and Node.js),
java
perl
php
python
ruby
or
wasm
(WebAssembly).
Except for
external
and
wasm
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 only one PHP module,
7.1.9,
it matches
“php”
“php 7”
“php 7.1”
and
“php 7.1.9”
If you have modules for versions 7.0.2 and 7.0.23,
set
“type”: “php 7.0.2”
to specify the former;
otherwise, PHP 7.0.23 will be used.
environment
String-valued object;
environment variables to be passed to the app.
Unit passes the environment variables to the app without modification,
even if no enviroment configuration object is specified.
Any data specified in the
environment
object gets merged to the
existing environment variables.
If an environment variable already exists in the system and gets declared
in this object, the object’s value takes precedence and gets passed to
the application.
group
String;
group name that runs the
app process
The default is the
user
’s primary group.
isolation
Object; manages the isolation
of an application process.
For details, see
here
limits
Object; accepts two integer options,
timeout
and
requests
Their values govern the life cycle of an application process.
For details, see
here
processes
Integer or object;
integer sets a static number of app processes,
and object options
max
spare
and
idle_timeout
enable dynamic management.
For details, see
here
The default is 1.
stderr
stdout
Strings;
filenames where Unit redirects the application’s output.
The default when running
with
--no-daemon
is to send
stdout
to the
console
and
stderr
to Unit’s
log
The default when running
without
--no-daemon
is to send
stdout
to
/dev/null
and
stderr
to Unit’s
log
These options have
no
effect when running with
--no-daemon
user
String;
username that runs the
app process
The default is the username configured at
build time
or at
startup
working_directory
String;
the app’s working directory.
The default is
the working directory
of Unit’s
main process
Also, you need to set
type
-specific options
to run the app.
This
Python app
sets
path
and
module
"type"
"python 3.6"
"processes"
16
"working_directory"
"/www/python-apps"
"path"
"blog"
"module"
"blog.wsgi"
"user"
"blog"
"group"
"blog"
"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"
Process management
Unit has three per-app options
that control how the app’s processes behave:
isolation
limits
, and
processes
Also, you can
GET
the
/control/applications/
section of the API
to restart an app:
curl
-X
GET
--unix-socket
/path/to/control.unit.sock
app_name
/restart
Unit handles the rollover gracefully,
allowing the old processes
to deal with existing requests
and starting a new set of processes
(as defined by the
processes
option
to accept new requests.
Process isolation
You can use
namespace
and
file system
isolation for your apps
if Unit’s underlying OS supports them:
ls
/proc/self/ns/
cgroup
mnt
net
pid ...
user
uts
The
isolation
application option
has the following members:
Option
Description
automount
Object;
controls mount behavior
if
rootfs
is enabled.
By default, Unit automatically mounts the
language runtime dependencies
procfs
at
/proc/
and a
tmpfs
at
/tmp/
but you can disable any of these default mounts:
"isolation"
"automount"
"language_deps"
false
"procfs"
false
"tmpfs"
false
cgroup
Object;
defines the app’s
cgroup
Option
Description
path
(required)
String;
configures absolute or relative path of the app
in the
cgroups v2 hierarchy
The limits trickle down the hierarchy,
so child cgroups can’t exceed parental thresholds.
gidmap
Same as
uidmap
but configures group IDs instead of user IDs.
namespaces
Object; configures
namespace
isolation scheme for the application.
Available options
(system-dependent;
check your OS manual for guidance):
cgroup
Creates a new
cgroup
namespace for the app.
credential
Creates a new
user
namespace for the app.
mount
Creates a new
mount
namespace for the app.
network
Creates a new
network
namespace for the app.
pid
Creates a new
PID
namespace for the app.
uname
Creates a new
UTS
namespace for the app.
All options listed above are Boolean;
to isolate the app,
set the corresponding namespace option to
true
to disable isolation,
set the option to
false
(default).
rootfs
String; pathname of the directory
to be used as the new
file system root
for the app.
uidmap
Array of user ID
mapping objects
each array item must define the following:
container
Integer;
starts the user ID mapping range
in the app’s namespace.
host
Integer;
starts the user ID mapping range
in the OS namespace.
size
Integer;
size of the ID range
in both namespaces.
A sample
isolation
object
that enables all namespaces
and sets mappings for user and group IDs:
"namespaces"
"cgroup"
true
"credential"
true
"mount"
true
"network"
true
"pid"
true
"uname"
true
},
"cgroup"
"path"
"/unit/appcgroup"
},
"uidmap"
"host"
1000
"container"
"size"
1000
],
"gidmap"
"host"
1000
"container"
"size"
1000
Using control groups
A control group (cgroup) commands
the use of computational resources
by a group of processes
in a unified hierarchy.
Cgroups are defined
by their
paths
in the cgroups file system.
The
cgroup
object
defines the cgroup
for a Unit app;
its
path
option
can set an absolute
(starting with
or a relative value.
If the path doesn’t exist
in the cgroups file system,
Unit creates it.
Relative paths are implicitly placed
inside the cgroup of Unit’s
main process
this setting effectively puts the app
to the
/
cgroup:
"isolation"
"cgroup"
"path"
"production/app"
An absolute pathname places the application
under a separate cgroup subtree;
this configuration puts the app under
/staging/app
"isolation"
"cgroup"
"path"
"/staging/app"
A basic use case
would be to set a memory limit on a cgroup.
First,
find the cgroup mount point:
mount
-l
grep
cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
Next, check the available controllers
and set the
memory.high
limit:
cat
/sys/fs/cgroup/
/staging/app
/cgroup.controllers
cpuset cpu io memory pids
echo
1G
/sys/fs/cgroup
/staging/app
/memory.high
For more details
and possible options,
refer to the
admin guide
Note
To avoid confusion,
mind that the
namespaces/cgroups
option
controls the application’s cgroup
namespace
instead, the
cgroup/path
option
specifies the cgroup where Unit puts the application.
Changing root directory
The
rootfs
option confines the app
to the directory you provide,
making it the new
file system root
To use it,
your app should have the corresponding privilege
(effectively,
run as
root
in most cases).
The root directory is changed
before the language module starts the app,
so any path options for the app
should be relative to the new root.
Note the
path
and
settings:
"type"
"python 2.7"
"path"
"home"
/venv/
"module"
"wsgi"
"isolation"
"rootfs"
"/var/app/sandbox/"
Warning
When using
rootfs
with
credential
set to
true
"isolation"
"rootfs"
"/var/app/sandbox/"
"namespaces"
"credential"
true
Ensure that the user the app
runs as
can access the
rootfs
directory.
Unit mounts language-specific files and directories
to the new root
so the app stays operational:
Language
Language-Specific Mounts
Java
JVM’s
libc.so
directory
Java module’s
directory
Python
Python’s
sys.path
directories
Ruby
Ruby’s header, interpreter, and library
directories
rubyarchhdrdir
rubyhdrdir
rubylibdir
rubylibprefix
sitedir
and
topdir
Ruby’s gem installation directory
gem env gemdir
Ruby’s entire gem path list
gem env gempath
Using "uidmap", "gidmap"
The
uidmap
and
gidmap
options
are available only
if the underlying OS supports
user namespaces
If
uidmap
is omitted but
credential
isolation is enabled,
the effective UID (EUID) of the application process
in the host namespace
is mapped to the same UID
in the container namespace;
the same applies to
gidmap
and GID, respectively.
This means that the configuration below:
"user"
"some_user"
"isolation"
"namespaces"
"credential"
true
Is equivalent to the following
(assuming
some_user
’s EUID and EGID are both equal to 1000):
"user"
"some_user"
"isolation"
"namespaces"
"credential"
true
},
"uidmap"
"host"
"1000"
"container"
"1000"
"size"
],
"gidmap"
"host"
"1000"
"container"
"1000"
"size"
Request limits
The
limits
object
controls request handling by the app process
and has two integer options:
Option
Description
requests
Integer;
maximum number of requests
an app process can serve.
When the limit is reached,
the process restarts;
this mitigates possible memory leaks
or other cumulative issues.
timeout
Integer;
request timeout in seconds.
If an app process exceeds it
while handling a request,
Unit cancels the request
and returns a 503 “Service Unavailable” response
to the client.
Note
Now, Unit doesn’t detect freezes,
so the hanging process stays on
the app’s process pool.
Example:
"type"
"python"
"working_directory"
"/www/python-apps"
"module"
"blog.wsgi"
"limits"
"timeout"
10
"requests"
1000
Application processes
The
processes
option
offers a choice
between static and dynamic process management.
If you set it to an integer,
Unit immediately launches the given number of app processes
and keeps them without scaling.
To enable a dynamic prefork model for your app,
supply a
processes
object with the following options:
Option
Description
idle_timeout
Number of seconds
Unit waits for
before terminating an idle process
that exceeds
spare
max
Maximum number of application processes
that Unit maintains
(busy and idle).
The default is 1.
spare
Minimum number of idle processes
that Unit tries to maintain for an app.
When the app is started,
spare
idles are launched;
Unit passes new requests to existing idles,
forking new idles
to keep the
spare
level
if
max
allows.
When busy processes complete their work
and turn idle again,
Unit terminates extra idles
after
idle_timeout
If
processes
is omitted entirely,
Unit creates 1 static process.
If an empty object is provided:
“processes”: {}
dynamic behavior
with default option values
is assumed.
Here, Unit allows 10 processes maximum,
keeps 5 idles,
and terminates extra idles after 20 seconds:
"max"
10
"spare"
"idle_timeout"
20
Note
For details of manual application process restart, see
here
Go
To run a Go app on Unit,
modify its source
to make it Unit-aware
and rebuild the app.
Updating Go apps to run on Unit
Unit uses
cgo
to invoke C code from Go,
so check the following prerequisites:
The
CGO_ENABLED
variable is set to
go
env
CGO_ENABLED
go
env
-w
CGO_ENABLED
If you installed Unit from the
official packages
install the development package:
Debian, Ubuntu
apt
install
unit-dev
Amazon, Fedora, RHEL
yum
install
unit-devel
If you installed Unit from
source
install the include files and libraries:
make
libunit-install
In the
import
section,
list the
unit.nginx.org/go
package:
import
...
"unit.nginx.org/go"
...
Replace the
http.ListenAndServe
call
with
unit.ListenAndServe
func
main
()
...
http
HandleFunc
"/"
handler
...
// http.ListenAndServe(":8080", nil)
unit
ListenAndServe
":8080"
nil
...
If you haven’t done so yet,
initialize the Go module for your app:
go
mod
init
example.com/app
go: creating new go.mod: module example.com/app
Install the newly added dependency
and build your application:
go
get
unit.nginx.org/go@1.34.2
go: downloading unit.nginx.org
go
build
-o
app
app.go
If you update Unit to a newer version,
repeat the two commands above
to rebuild your app.
The resulting executable works as follows:
When you run it standalone,
the
unit.ListenAndServe
call
falls back to
http
functionality.
When Unit runs it,
unit.ListenAndServe
directly communicates
with Unit’s router process,
ignoring the address supplied as its first argument
and relying on the
listener’s settings
instead.
Next, configure the app on Unit;
besides the
common options
you have:
Option
Description
executable
(required)
String;
pathname of the application,
absolute or relative to
working_directory
arguments
Array of strings;
command-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"
Note
For Go-based examples,
see our
Grafana
howto or a basic
sample
Java
First, make sure to install Unit
along with the
Java language module
Besides the
common options
you have:
Option
Description
webapp
(required)
String;
pathname
of the application’s
.war
file
(packaged or unpackaged).
classpath
Array of strings;
paths to your app’s required libraries
(may point to directories
or individual
.jar
files).
options
Array of strings;
defines JVM runtime options.
Unit itself
exposes the
-Dnginx.unit.context.path
option
that defaults to
use it to customize the
context path
thread_stack_size
Integer;
stack size of a worker thread
(in bytes,
multiple of memory page size;
the minimum value is usually architecture specific).
The default is usually system dependent
and can be set with
ulimit -s
threads
Integer;
number of worker threads
per
app process
When started,
each app process creates this number of threads
to handle requests.
The default is
Example:
"type"
"java"
"classpath"
"/www/qwk2mart/lib/qwk2mart-2.0.0.jar"
],
"options"
"-Dlog_path=/var/log/qwk2mart.log"
],
"webapp"
"/www/qwk2mart/qwk2mart.war"
Note
For Java-based examples,
see our
Jira
OpenGrok
and
Spring Boot
howtos or a basic
sample
Node.js
First, you need to have the
unit-http
module
installed
If it’s global,
symlink it in your project directory:
npm
link
unit-http
Do the same if you move a Unit-hosted app
to a new system
where
unit-http
is installed globally.
Also, if you update Unit later,
update the Node.js module as well
according to your
installation method
Next, to run your Node.js apps on Unit,
you need to configure them.
Besides the
common options
you have:
Option
Description
executable
(required)
String;
pathname of the app,
absolute or relative to
working_directory
Supply your
.js
pathname here
and start the file itself
with a proper shebang:
#!/usr/bin/env node
Note
Make sure to
chmod +x
the file you list here
so Unit can start it.
arguments
Array of strings;
command-line arguments
to be passed to the app.
The example below is equivalent to
/www/apps/node-app/app.js --tmp-files /tmp/node-cache
Example:
"type"
"external"
"working_directory"
"/www/app/node-app/"
"executable"
"app.js"
"user"
"www-node"
"group"
"www-node"
"arguments"
"--tmp-files"
"/tmp/node-cache"
You can run Node.js apps without altering their code,
using a loader module
we provide with
unit-http
Apply the following app configuration,
depending on your version of Node.js:
14.16.x and later
"type"
"external"
"executable"
/usr/bin/env
"arguments"
"node"
"--loader"
"unit-http/loader.mjs"
"--require"
"unit-http/loader"
app.js
14.15.x and earlier
"type"
"external"
"executable"
/usr/bin/env
"arguments"
"node"
"--require"
"unit-http/loader"
app.js
The loader overrides the
http
and
websocket
modules
with their Unit-aware versions
and starts the app.
You can also run your Node.js apps without the loader
by updating the application source code.
For that, use
unit-http
instead of
http
in your code:
var
http
require
'unit-http'
);
To use the WebSocket protocol,
your app only needs to replace the default
websocket
var
webSocketServer
require
'unit-http/websocket'
).
server
Note
For Node.js-based examples,
see our
Apollo
Express
Koa
and
Docker
howtos or a basic
sample
Perl
First, make sure to install Unit along with the
Perl language module
Besides the
common options
you have:
Option
Description
script
(required)
String;
PSGI script path.
thread_stack_size
Integer;
stack size of a worker thread
(in bytes,
multiple of memory page size;
the minimum value is usually architecture specific).
The default is usually system dependent
and can be set with
ulimit -s
threads
Integer;
number of worker threads
per
app process
When started,
each app process creates this number of threads
to handle requests.
The default is
Example:
"type"
"perl"
"script"
"/www/bugtracker/app.psgi"
"working_directory"
"/www/bugtracker"
"processes"
10
"user"
"www"
"group"
"www"
Note
For Perl-based examples of Perl,
see our
Bugzilla
and
Catalyst
howtos or a basic
sample
PHP
First, make sure to install Unit along with the
PHP language module
Besides the
common options
, you have:
Option
Description
root
(required)
String;
base directory
of the app’s file structure.
All URI paths are relative to it.
index
String;
filename added to URI paths
that point to directories
if no
script
is set.
The default is
index.php
options
Object;
defines
the
php.ini
location and options.
script
String;
filename of a
root
-based PHP script
that serves all requests to the app.
targets
Object;
defines application sections with
custom
root
script
, and
index
values.
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 specify in this option.
Otherwise, the requests are served
according to their URI paths;
if they point to directories,
index
is used.
You can customize
php.ini
via the
options
object:
Option
Description
admin
user
Objects for extra directives.
Values in
admin
are set in
PHP_INI_SYSTEM
mode,
so the app can’t alter them;
user
values are set in
PHP_INI_USER
mode
and can be
updated
at runtime.
The objects override the settings
from any
*.ini
files
The
admin
object can only set what’s
listed
as
PHP_INI_SYSTEM
for other modes,
set
user
Neither
admin
nor
user
can set directives listed as
php.ini only
except for
disable_classes
and
disable_functions
file
String;
pathname of the
php.ini
file with
PHP configuration directives
To load multiple
.ini
files,
use
environment
with
PHP_INI_SCAN_DIR
to
scan a custom directory
"applications"
"hello-world"
"type"
"php"
"root"
"/www/public/"
"script"
"index.php"
"environment"
"PHP_INI_SCAN_DIR"
/tmp/php.inis/"
Mind that the colon that prefixes the value here is a path separator;
it causes PHP to scan the directory preconfigured with the
--with-config-file-scan-dir
option,
which is usually
/etc/php.d/
and then the directory you set here, which is
/tmp/php.inis/
To skip the preconfigured directory, drop the
prefix.
Note
Values in
options
must be strings
(for example,
“max_file_uploads”: “4”
not
“max_file_uploads”: 4
);
for boolean flags,
use
“0”
and
“1”
only.
For details aof
PHP_INI_*
modes,
see the
PHP docs
Note
Unit implements the
fastcgi_finish_request()
function
in a
manner similar to PHP-FPM.
Example:
"type"
"php"
"processes"
20
"root"
"/www/blogs/scripts/"
"user"
"www-blogs"
"group"
"www-blogs"
"options"
"file"
"/etc/php.ini"
"admin"
"memory_limit"
"256M"
"variables_order"
"EGPCS"
},
"user"
"display_errors"
"0"
Targets
You can configure up to 254 individual entry points
for a single PHP app:
"applications"
"php-app"
"type"
"php"
"targets"
"front"
"script"
"front.php"
"root"
"/www/apps/php-app/front/"
},
"back"
"script"
"back.php"
"root"
"/www/apps/php-app/back/"
Each target is an object
that specifies
root
and can define
index
or
script
just like a regular app does.
Targets can be used by the
pass
options
in listeners and routes
to serve requests:
"listeners"
"127.0.0.1:8080"
"pass"
"applications/php-app/front"
},
"127.0.0.1:80"
"pass"
"routes"
},
"routes"
"match"
"uri"
"/back"
},
"action"
"pass"
"applications/php-app/back"
App-wide settings
isolation
limits
options
processes
are shared by all targets within the app.
Warning
If you specify
targets
there should be no
root
index
, or
script
defined at the app level.
Note
For PHP-based examples,
see our
CakePHP
CodeIgniter
DokuWiki
Drupal
Laravel
Lumen
Matomo
MediaWiki
MODX
NextCloud
phpBB
phpMyAdmin
Roundcube
Symfony
WordPress
and
Yii
howtos or a basic
sample
Python
First, make sure to install Unit along with the
Python language module
Besides the
common options
you have:
Option
Description
module
(required)
String;
app’s module name.
This module is
imported
by Unit
the usual Python way.
callable
String;
name of the
module
-based callable
that Unit runs as the app.
The default is
application
factory
Boolean:
when enabled, Unit treats
callable
as a factory.
The default is
false
Note:
Unit does
not
support passing arguments to factories.
(since 1.33.0)
String;
path to the app’s
virtual environment
Absolute or relative to
working_directory
Note
The Python version used to run the app
is determined by
type
for performance,
Unit doesn’t use the command-line interpreter
from the virtual environment.
ImportError: No module named 'encodings'
Seeing this in Unit’s
log
after you set up
for your app?
This usually occurs
if the interpreter can’t use the virtual environment,
possible reasons including:
Version mismatch
between the
type
setting
and the virtual environment;
check the environment’s version:
source
/path/to/venv/
bin/activate
(venv)
python
--version
Unit’s unprivileged user
(usually
unit
having no access to the environment’s files;
assign the necessary rights:
chown
-R
unit:unit
/path/to/venv/
path
String or an array of strings;
additional Python module lookup paths.
These values are prepended to
sys.path
prefix
String;
SCRIPT_NAME
context value for WSGI
or the
root_path
context value for ASGI.
Should start with a slash
).
protocol
String;
hints Unit that the app uses a certain interface.
Can be
asgi
or
wsgi
targets
Object;
app sections with
custom
module
and
callable
values.
thread_stack_size
Integer;
stack size of a worker thread
(in bytes,
multiple of memory page size;
the minimum value is usually architecture specific).
The default is usually system dependent
and can be set with
ulimit -s
threads
Integer;
number of worker threads
per
app process
When started,
each app process creates this number of threads
to handle requests.
The default is
Example:
"type"
"python"
"processes"
10
"working_directory"
"/www/store/cart/"
"path"
/www/store/
"home"
.virtualenv/
"module"
cart.run
"callable"
"app"
"prefix"
/cart
"user"
"www"
"group"
"www"
This snippet runs the
app
callable
from the
/www/store/cart/run.py
module
with
/www/store/cart/
as the working directory
and
/www/store/.virtualenv/
as the virtual environment;
the
path
value
accommodates for situations
when some modules of the app
are imported
from outside the
cart/
subdirectory.
You can provide the callable in two forms.
The first one uses WSGI
PEP 333
or
PEP 3333
):
def
application
environ
start_response
):
start_response
'200 OK'
[(
'Content-Type'
'text/plain'
)])
yield
'Hello, WSGI
\n
The second one,
supported with Python 3.5+,
uses
ASGI
async
def
application
scope
receive
send
):
await
send
({
'type'
'http.response.start'
'status'
200
})
await
send
({
'type'
'http.response.body'
'body'
'Hello, ASGI
\n
})
Note
Legacy
two-callable
ASGI 2.0 applications
were not supported prior to Unit 1.21.0.
Choose either one according to your needs;
Unit tries to infer your choice automatically.
If this inference fails,
use the
protocol
option
to set the interface explicitly.
Note
The
prefix
option
controls the
SCRIPT_NAME
WSGI
or
root_path
ASGI
setting in Python’s context,
allowing to route requests
regardless of the app’s factual path.
Targets
You can configure up to 254 individual entry points
for a single Python app:
"applications"
"python-app"
"type"
"python"
"path"
"/www/apps/python-app/"
"targets"
"front"
"module"
"front.wsgi"
"callable"
"app"
},
"back"
"module"
"back.wsgi"
"callable"
"app"
Each target is an object
that specifies
module
and can also define
callable
and
prefix
just like a regular app does.
Targets can be used by the
pass
options
in listeners and routes
to serve requests:
"listeners"
"127.0.0.1:8080"
"pass"
"applications/python-app/front"
},
"127.0.0.1:80"
"pass"
"routes"
},
"routes"
"match"
"uri"
"/back"
},
"action"
"pass"
"applications/python-app/back"
The
path
protocol
threads
, and
thread_stack_size
settings
are shared by all targets in the app.
Warning
If you specify
targets
there should be no
module
or
callable
defined at the app level.
Moreover, you can’t combine WSGI and ASGI targets
within a single app.
Note
For Python-based examples,
see our
Bottle
Datasette
Django
Django Channels
Falcon
FastAPI
Flask
Guillotina
Mailman Web
Mercurial
MoinMoin
Plone
Pyramid
Quart
Responder
Review Board
Sanic
Starlette
Trac
and
Zope
howtos or a basic
sample
Ruby
First, make sure to install Unit along with the
Ruby language module
Note
Unit uses the
Rack
interface
to run Ruby scripts;
you need to have it installed as well:
gem
install
rack
Besides the
common options
you have:
Option
Description
script
(required)
String; rack script pathname, including the
.ru
extension,
for instance:
/www/rubyapp/script.ru
hooks
String; pathname of the
.rb
file setting the event hooks invoked
during the app’s lifecycle.
threads
Integer; number of worker threads per
app process
. When started, each app process
creates this number of threads to handle requests. The default is
Example:
"type"
"ruby"
"processes"
"user"
"www"
"group"
"www"
"script"
"/www/cms/config.ru"
"hooks"
"hooks.rb"
The
hooks
script
is evaluated when the app starts.
If set, it can define blocks of Ruby code named
on_worker_boot
on_worker_shutdown
on_thread_boot
or
on_thread_shutdown
If provided,
these blocks are called
at the respective points
of the app’s lifecycle,
for example:
@mutex
Mutex
new
File
write
"./hooks.
#{
Process
pid
"hooks evaluated"
# Runs once at app load.
on_worker_boot
do
File
write
"./worker_boot.
#{
Process
pid
"worker boot"
end
# Runs at worker process boot.
on_thread_boot
do
@mutex
synchronize
do
# Avoids a race condition that may crash the app.
File
write
"./thread_boot.
#{
Process
pid
#{
Thread
current
object_id
"thread boot"
end
end
# Runs at worker thread boot.
on_thread_shutdown
do
@mutex
synchronize
do
# Avoids a race condition that may crash the app.
File
write
"./thread_shutdown.
#{
Process
pid
#{
Thread
current
object_id
"thread shutdown"
end
end
# Runs at worker thread shutdown.
on_worker_shutdown
do
File
write
"./worker_shutdown.
#{
Process
pid
"worker shutdown"
end
# Runs at worker process shutdown.
Use these hooks
to add custom runtime logic
to your app.
Note
For Ruby-based examples,
see our
Ruby on Rails
and
Redmine
howtos or a basic
sample
WebAssembly
wasm-wasi-component
First, make sure to install Unit along with the
WebAssembly language module
Besides the
common options
you have:
Option
Description
component
(required)
String; WebAssembly component pathname, including the
.wasm
extension, for instance: “/var/www/wasm/component.wasm”
access
Object; its only array member,
filesystem
, lists
directories to which the application has access:
"access"
"filesystem"
"/tmp/"
"/var/tmp/"
Example:
"listeners"
"127.0.0.1:8080"
"pass"
"applications/wasm"
},
"applications"
"wasm"
"type"
"wasm-wasi-component"
"component"
"/var/www/app/component.wasm"
"access"
"filesystem"
"/tmp/"
"/var/tmp/"
Note
A good, first Rust-based project is available at
sunfishcode/hello-wasi-http
It also includes all the important steps to get started with WebAssembly, WASI, and Rust.
unit-wasm
Warning
The
unit-wasm
module is deprecated.
We recommend using
wasm-wasi-component
instead, which supports
WebAssembly Components using standard WASI 0.2 interfaces.
The
wasm-wasi-component
module is available in Unit 1.32 and later.
First, make sure to install Unit along with the
WebAssembly language module
Besides the
common options
you have:
Option
Description
module
(required)
String; WebAssembly module pathname, including the
.wasm
extension,
for instance:
applications/wasmapp/module.wasm
request_handler
(required)
String; name of the request handler function. If you use Unit
with the official
unit-wasm
package
the value is language specific; see the
SDK
documentation for details.
Otherwise, use the name of your custom implementation.
The runtime calls this handler, providing the address of the
shared memory block used to pass data in and out the app.
malloc_handler
(required)
String; name of the memory allocator function. See note above regarding
language-specific handlers in the official
unit-wasm
package.
The runtime calls this handler at language module startup to allocate
the shared memory block used to pass data in and out the app.
free_handler
(required)
String; name of the memory deallocator function. See note above regarding
language-specific handlers in the official
unit-wasm
package.
The runtime calls this handler at language module shutdown to free
the shared memory block used to pass data in and out the app.
access
Object; its only array member,
filesystem
, lists directories
the application can access:
"access"
"filesystem"
"/tmp/"
"/var/tmp/"
module_init_handler
String;
name of the module initilization function.
If you use Unit with the official
unit-wasm
package
the value is language specific;
see the
SDK
documentation for details.
Otherwise, use the name of your custom implementation.
It is invoked by the WebAssembly language module
at language module startup,
after the WebAssembly module was initialized.
module_end_handler
String;
name of the module finalization function.
If you use Unit with the official
unit-wasm
package
the value is language specific;
see the
SDK
documentation for details.
Otherwise, use the name of your custom implementation.
It is invoked by the WebAssembly language module
at language module shutdown.
request_init_handler
String;
name of the request initialization function.
If you use Unit with the official
unit-wasm
package
the value is language specific;
see the
SDK
documentation for details.
Otherwise, use the name of your custom implementation.
It is invoked by the WebAssembly language module
at the start of each request.
request_end_handler
String;
name of the request finalization function.
If you use Unit with the official
unit-wasm
package
the value is language specific;
see the
SDK
documentation for details.
Otherwise, use the name of your custom implementation.
It is invoked by the WebAssembly language module
at the end of each request,
when the headers and the request body were received.
response_end_handler
String;
name of the response finalization function.
If you use Unit with the official
unit-wasm
package
the value is language specific;
see the
SDK
documentation for details.
Otherwise, use the name of your custom implementation.
It is invoked by the WebAssembly language module
at the end of each response,
when the headers and the response body were sent.
Example:
"type"
"wasm"
"module"
"/www/webassembly/unitapp.wasm"
"request_handler"
"my_custom_request_handler"
"malloc_handler"
"my_custom_malloc_handler"
"free_handler"
"my_custom_free_handler"
"access"
"filesystem"
"/tmp/"
"/var/tmp/"
},
"module_init_handler"
"my_custom_module_init_handler"
"module_end_handler"
"my_custom_module_end_handler"
"request_init_handler"
"my_custom_request_init_handler"
"request_end_handler"
"my_custom_request_end_handler"
"response_end_handler"
"my_custom_response_end_handler"
Use these handlers to add custom runtime logic to your app; for a detailed
discussion of their usage and requirements, see the
SDK
source code and documentation.
Note
For WASM-based examples, see our
Rust and C samples
Settings
Unit has a global
settings
configuration object
that stores instance-wide preferences.
Option
Description
listen_threads
Integer;
controls the number of router threads created to handle client
connections. Each thread includes all the configured listeners.
By default, we create as many threads as the number of CPUs that
are available to run on.
(since 1.33.0)
http
Object;
fine-tunes handling of HTTP requests
from the clients.
js_module
String or an array of strings;
lists enabled
njs
modules
uploaded
via the
control API
telemetry
Object:
OpenTelemetry configuration
(since 1.34.0)
In turn, the
http
option exposes the following settings:
Option
Description
body_read_timeout
Maximum number of seconds
to read data from the body
of a client’s request.
This is 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 returns a 408 “Request Timeout” response.
The default is 30.
discard_unsafe_fields
Boolean;
controls header field name parsing.
If it’s set to
true
Unit only processes header names
made of alphanumeric characters and hyphens
(see
RFC 9110
);
otherwise,
these characters are also permitted:
.!#$%&’*+^_`|~
The default is
true
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 returns a 408 “Request Timeout” response.
The default is 30.
idle_timeout
Maximum number of seconds
between requests
in a keep-alive connection.
If no new requests
arrive within this interval,
Unit returns a 408 “Request Timeout” response
and closes the connection.
The default is 180.
log_route
Boolean;
enables or disables
router logging
The default is
false
(disabled).
max_body_size
Maximum number of bytes
in the body of a client’s request.
If the body size exceeds this value,
Unit returns a 413 “Payload Too Large” response
and closes the connection.
The default is 8388608 (8 MB).
send_timeout
Maximum number of seconds
to transmit data
as a response to the client.
This is the interval
between consecutive transmissions,
not the time for the entire response.
If no data
is sent to the client
within this interval,
Unit closes the connection.
The default is 30.
server_version
Boolean;
if set to
false
Unit omits version information
in its
Server
response
header fields
The default is
true
(since 1.30.0)
static
Object;
configures static asset handling.
Has a single object option named
mime_types
that defines specific
MIME types
as options.
Their values
can be strings or arrays of strings;
each string must specify a filename extension
or a specific filename
that’s included in the MIME type.
You can override default MIME types
or add new types:
curl
-X
PUT
-d
'{"text/x-code": [".c", ".h"]}'
/path/to/control.unit.sock
"success": "Reconfiguration done."
Defaults:
.aac
.apng
.atom
.avi
.avif
avifs
.bin
.css
.deb
.dll
.exe
.flac
.gif
.htm
.html
.ico
.img
.iso
.jpeg
.jpg
.js
.json
.md
.mid
.midi
.mp3
.mp4
.mpeg
.mpg
.msi
.ogg
.otf
.pdf
.php
.png
.rpm
.rss
.rst
.svg
.ttf
.txt
.wav
.webm
.webp
.woff2
.woff
.xml
, and
.zip
The
telemetry
option exposes the following settings:
Option
Description
endpoint
(required)
The endpoint for the OpenTelemetry (OTEL) Collector.
It takes a URL to either a gRPC or HTTP(S) endpoint.
protocol
(required)
Determines the protocol used to communicate with the endpoint.
Can be either
http(s)
or
grpc
batch_size
Number of spans to cache before triggering a transaction with the
configured endpoint. This is optional.
This allows the user to cache up to N spans before the OpenTelemetry
(OTEL) background thread sends spans over the network to the
collector.
If specified, it must be a positive integer.
sampling_ratio
Percentage of requests to trace.
This allows the user to only trace anywhere from 0% to 100% of
requests that hit Unit. In high throughput environments this
percentage should be lower. This allows the user to save space in
storing span data, and to collect request metrics like time to decode
headers and whatnot without storing massive amounts of duplicate
superfluous data.
If specified, it must be a positive floating point number.
Example:
"settings"
"telemetry"
"batch_size"
20
"endpoint"
"http://example.com/v1/traces"
"protocol"
"http"
"sampling_ratio"
1.0
},
Access log
To enable basic access logging,
specify the log file path
in the
access_log
option
of the
config
object.
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
"success": "Reconfiguration done."
By default, the log is written in the
Combined Log Format
Example of a CLF 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"
Custom log formatting
The
access_log
option
can be also set to an object
to customize both the log path
and its format:
Option
Description
format
String;
sets the log format.
Besides arbitrary text,
can contain any
variables
Unit supports.
path
String;
pathname of the access log file.
Example:
"access_log"
"path"
"/var/log/unit/access.log"
"format"
$remote_addr
- - [
$time_local
] \"
$request_line
\"
$status
$body_bytes_sent
\"
$header_referer
\" \"
$header_user_agent
\""
By a neat coincidence,
the above
format
is the default setting.
Also, mind that the log entry
is formed
after
the request has been handled.
Besides
built-in variables
you can use
njs
templates
to define the log format:
"access_log"
"path"
"/var/log/unit/basic_access.log"
"format"
${
host
': '
uri
JSON log format
Starting with NGINX Unit 1.34.0,
format
can instead be an object
describing JSON field name/value pairs, for example,
"access_log"
"path"
"/tmp/access.log"
"format"
"remote_addr"
$remote_addr
"time_local"
$time_local
"request_line"
$request_line
"status"
$status
"body_bytes_sent"
$body_bytes_sent
"header_referer"
$header_referer
"header_user_agent"
$header_user_agent
The JSON
values
can be strings, variables and JavaScript.
Conditional access log
The
access_log
can be dynamically turned on and off by using the
if
option:
Option
Description
if
if the value is empty, 0, false, null, or undefined,
the logs will not be recorded.
This feature lets users set conditions to determine whether access logs are
recorded. The
if
option supports a string and JavaScript code.
If its value is empty, 0, false, null, or undefined, the logs will not be
recorded. And the ‘!’ as a prefix inverses the condition.
Example without njs:
"access_log"
"if"
$cookie_session
"path"
"..."
All requests using a session cookie named
session
will be logged.
We can add ! to inverse the condition.
"access_log"
"if"
"!
$cookie_session
"path"
"..."
Now, all requests without a session cookie will be logged.
Example with njs and the use of a template literal:
"access_log"
"if"
${
uri
==
'/health'
false
true
"path"
"..."
Edit this page
Discuss on GitHub
NGINX, Inc.
US