First Steps#
This walkthrough takes a freshly installed Angie from the package's welcome page to a server that hosts files, forwards requests to an application, serves both over HTTPS with certificates it obtains by itself, and reports its own statistics. If you already run nginx, migrate its configuration instead of rebuilding it by hand.
Each step extends the file the previous one leaves behind, so work through them in order. Only the HTTPS step is optional: it is the one step that needs a public domain name, and nothing after it depends on it.
You need Angie installed from a package on a
systemd-based Linux and an account that can use sudo. On Alpine and
FreeBSD, the installation page gives the service commands to use in
place of systemctl. Confirm the installed version: Start the service and request the default page: The response comes from the welcome-page server that the package ships; the next
step shows where it is defined. Configuration changes are applied with a reload, not a restart: the master
process re-reads the configuration and starts new worker processes, while the
old workers finish the requests they are serving before exiting. For the full
set of start, stop, reload, and log-rotation commands, the signals behind them,
and the command-line parameters, see Runtime Control. The main configuration file is The file is organized into contexts — blocks that group the directives
belonging to one kind of traffic: events — general connection processing http — HTTP traffic mail — mail traffic stream — TCP and UDP traffic Files under The welcome page comes from Inheritance between contexts, the syntax rules, and the size and time units used
by directive parameters are covered in Configuration Files. Start with files on disk. Create two directories with one file each; the
contents name the directory, so a response shows where the file came from: The second file only stands in for an image; a real PNG behaves the same. Replace the contents of Test the configuration and reload: Both files are now reachable, and a missing one gives a 404: Both locations are prefix locations: a request URI matches when it starts with
the given string, and the longest matching prefix wins. The root directive does not name the directory to serve from — it names
the directory the whole request URI is appended to, which is why
Note When a request does not do what you expect, the answer is almost always in
the access and error logs, written to How a request is matched against virtual servers and locations, including regular
expression locations and the order they are tested in, is described in
Connections, Sessions, Requests, Logs. The directives that map URIs onto files —
root, alias, index, try_files — are in the
HTTP modules reference. Angie's second common job is to stand in front of an application and forward
requests to it. Here Angie itself plays the application: a second server,
listening only on loopback port 8080, serves a directory of its own. Replace it
with the real application later. Put that server in a file of its own: In Test the configuration and reload: Requests now reach the application, except the ones for images: The second location starts with proxy_pass has a large set of companion directives — for request headers,
timeouts, buffering, and caching — documented in the Proxy
module. To spread requests across several application servers instead of
one, define an upstream block and proxy to it by name. This step needs a domain name that resolves to this host's public address, and
port 80 reachable from the internet: the certificate authority validates
ownership by fetching a file over HTTP. Without both, skip to the next step;
nothing later depends on this one. Angie obtains and renews certificates itself, over
ACME, with no external client
and no renewal cron job. Add an acme_client above the server block (an
http-level directive) and reference the client from the server, putting your own
names in place of Angie resolves the certificate authority's name through The certificate is issued for the domain names listed in server_name
across the servers that reference the same client; entries that are not domain
names, such as regular expressions and Test the configuration and reload: Angie binds port 443 as soon as the configuration is applied, but it cannot
complete a TLS handshake until the certificate arrives, so requests to that port
fail during the handshake in the meantime. Issuance is not instant; it depends
on the certificate authority. Once the certificate is in place: If it never arrives, look at the client's state under
Note While you are still getting the configuration right, point
acme_client at the certificate authority's staging directory — for
Let's Encrypt, https://acme-staging-v02.api.letsencrypt.org/directory — so
that failed attempts do not count against the production rate limits. Switch
to the production URL once a certificate appears. DNS and TLS-ALPN validation, wildcard certificates, external account binding,
and moving over from Certbot are covered in ACME Configuration; the directives
and variables are in the ACME module. Angie reports its own state through a built-in REST API. Add a location for it,
restricted to local requests, and give the server a status_zone so its
counters are collected: If you skipped the HTTPS step, add only the highlighted lines. The package's
Test the configuration and reload; the API then answers with JSON: The The full set of API sections — upstreams, caches, resolvers, shared memory
zones, ACME clients, and, in Angie PRO, dynamic configuration — is documented in
the API module. If you would rather look at it than curl it,
Console Light renders the same data as a web panel. Step-by-step guides for specific tasks: SSL, OIDC, clustering,
monitoring dashboards, and custom metrics. The reference for every directive and variable, grouped by module. Short links that jump straight to a directive's documentation. If you also run nginx elsewhere, move those configurations across.Checking the Installation#
$ angie -v
Angie version: Angie/1.12.1
$ sudo systemctl start angie
$ curl -I localhost
HTTP/1.1 200 OK
Server: Angie/1.12.1
...
Configuration Layout#
angie.conf; its location is compiled into
the binary:$ angie -V 2>&1 | tr ' ' '\n' | grep conf-path
--conf-path=/etc/angie/angie.conf
/etc/angie/http.d/ are included inside http, so they
hold server blocks and http-level directives. A server block
describes one virtual server, and a location block inside it describes
how to handle one set of request URIs./etc/angie/http.d/default.conf, the only
server the package defines. This walkthrough replaces that file and leaves
angie.conf as installed.Serving Static Files#
$ sudo mkdir -p /data/www /data/images
$ echo 'Hello from /data/www' | sudo tee /data/www/index.html
Hello from /data/www
$ echo 'Hello from /data/images' | sudo tee /data/images/example.png
Hello from /data/images
/etc/angie/http.d/default.conf with:server {
listen 80;
location / {
root /data/www;
}
location /images/ {
root /data;
}
}
$ sudo angie -t && sudo systemctl reload angie
$ curl localhost/index.html
Hello from /data/www
$ curl localhost/images/example.png
Hello from /data/images
$ curl -o /dev/null -w '%{http_code}\n' localhost/images/missing.png
404
/index.html
matched location /, the shortest possible prefix, which catches
everything the other locations do not.location /images/ needs root /data, not /data/images:
the URI /images/example.png appended to /data gives
/data/images/example.png./var/log/angie/.Proxying to an Application#
$ sudo mkdir -p /data/app
$ echo 'Hello from the application' | sudo tee /data/app/index.html
Hello from the application
server {
listen 127.0.0.1:8080;
root /data/app;
}
default.conf, replace root in location / with
proxy_pass, and match images by extension instead of by prefix:server {
listen 80;
location / {
proxy_pass http://127.0.0.1:8080;
}
location ~ \.(gif|jpg|png)$ {
root /data;
}
}
$ sudo angie -t && sudo systemctl reload angie
$ curl localhost/
Hello from the application
$ curl localhost/images/example.png
Hello from /data/images
~, which makes it a regular expression
location instead of a prefix. Angie checks prefix locations first and remembers
the longest match, then tries the regular expressions in the order they appear;
if one of them matches, it wins. That is what lets a single short pattern carve
the image requests out of the catch-all above it, so Angie answers them from
disk without involving the application.Automatic HTTPS#
example.com and www.example.com:acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80;
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
location / {
proxy_pass http://127.0.0.1:8080;
}
location ~ \.(gif|jpg|png)$ {
root /data;
}
}
/etc/resolv.conf
by default, so no resolver directive is needed. On a host with no IPv6
connectivity, add resolver conf ipv6=off; above the server block so it
stops asking for AAAA records it cannot use._, are skipped with a warning in
the error log. The certificate reaches ssl_certificate through a variable
rather than a file path, so there is nothing to install and nothing to rotate by
hand.$ sudo angie -t && sudo systemctl reload angie
$ curl -I https://www.example.com/
HTTP/1.1 200 OK
...
/status/http/acme_clients/example in the API of the next step and at the
ACME messages in the error log; if that is not enough, turn
on the debug log.Server Statistics#
acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80;
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
status_zone site;
location / {
proxy_pass http://127.0.0.1:8080;
}
location ~ \.(gif|jpg|png)$ {
root /data;
}
location /status/ {
api /status/;
allow 127.0.0.1;
deny all;
}
}
default.conf shipped this same /status/ location; it went away
when you replaced that file, so put it back.$ sudo angie -t && sudo systemctl reload angie
$ curl localhost/status/angie/
{
"version": "1.12.1",
"build_time": "2026-07-17T06:58:49Z",
"address": "192.0.2.10",
"generation": 1,
"load_time": "2026-07-17T10:23:06.011Z"
}
/status/connections reports accepted, dropped, active, and idle
connections. Per-server and per-location counters are opt-in: a server
appears under /status/http/server_zones/ and a location under
/status/http/location_zones/, each only once it carries a
status_zone of its own. The server above carries one, its locations do
not:$ curl localhost/status/http/server_zones/site
{
"ssl": {
"handshaked": 3,
"reuses": 0,
"timedout": 0,
"failed": 0
},
"requests": {
"total": 5,
"processing": 1,
"discarded": 0
},
"responses": {
"200": 3,
"404": 1
},
"data": {
"received": 412,
"sent": 1418
}
}
ssl object is there because the server listens with ssl;
without it the zone starts at requests.Where to Go Next#