Deployment guide

Nginx Proxy Manager with Authelia

Overview

Nginx Proxy Manager is an open-source web UI that lets you run nginx as a reverse proxy in front of your applications without editing an nginx configuration file by hand. Authelia is an open-source authentication and two-factor single sign-on server that sits in front of proxied applications. Running one behind the other is the standard self-hosted SSO pattern, and Authelia's own documentation includes a dedicated Nginx Proxy Manager integration guide for it.

The work in that pattern is the forward-authentication wiring: the auth_request snippets, the authorisation endpoint, the redirect back to the login portal, and the TLS that the session cookie depends on. This image ships that wiring already done and already proven.

On first boot, the VM creates two proxy hosts for you. One serves the Authelia login portal, and the other serves a protected demo application. The first-boot process does not finish unless it can observe, on that specific VM, that an unauthenticated request is redirected to the portal and that an authenticated session passes through to the backend carrying the identity that Authelia resolved.

What you get:

  • Nginx Proxy Manager 2.15.1 running as a container managed by Docker, pinned by image digest
  • Authelia 4.39.20 installed from the official Debian package, bound to the loopback interface only
  • Authelia pre-wired as Nginx Proxy Manager's forward-authentication provider
  • Two proxy hosts, created on first boot: the login portal and a protected demo application
  • An interim TLS certificate whose private key is generated on your VM at first boot
  • Unique admin credentials for both components, generated on the first boot of every VM
  • Free Let's Encrypt certificate issuance and renewal from the Nginx Proxy Manager UI
  • A dedicated Azure data disk at /srv/npma, which holds both datastores and issued certificates
  • docker.service, authelia.service and the first-boot unit as enabled systemd units
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a reasonable starting point.

Configure the network security group to allow inbound 22/tcp from your management network, 80/tcp and 443/tcp for proxied traffic and Let's Encrypt challenges, and 81/tcp for the admin UI. For production, restrict 81/tcp to trusted source IPs so that the admin console is not exposed to the whole internet.

You also need a domain that you control. Authelia is domain-based. Its session cookie is scoped to a domain, so browser sign-in completes only at a hostname under that domain. The image ships the example domain example.com, so everything works out of the box for inspection. Step 9 replaces it with yours.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Nginx Proxy Manager with Authelia by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22), HTTP (80), HTTPS (443) and 81.

Review the dedicated data disk on the Disks tab. Then select Review + create, followed by Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:nginx-proxy-manager-authelia:default:latest
az vm create \
  --resource-group <your-rg> \
  --name npm-authelia \
  --image sac1647283583153:nginx-proxy-manager-authelia:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name npm-authelia --port 80 --priority 1010
az vm open-port --resource-group <your-rg> --name npm-authelia --port 443 --priority 1020
az vm open-port --resource-group <your-rg> --name npm-authelia --port 81 --priority 1030

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

First boot generates every secret and builds the whole integration, which takes about a minute. If the commands below report anything as inactive, wait a moment and try again.

Step 4 - Confirm both components are running

systemctl is-active docker.service authelia.service nginx-proxy-manager-authelia-firstboot.service

All three report active. Docker runs the Nginx Proxy Manager container, Authelia runs as a native service, and the first-boot unit is a completed one-shot.

sudo docker ps --format '{{.Names}} {{.Image}} {{.Status}}' findmnt -no SOURCE,TARGET,FSTYPE,SIZE /srv/npma

The container runs the pinned jc21/nginx-proxy-manager:2.15.1 image, and your dedicated data disk is mounted at /srv/npma. Both datastores, the generated nginx configuration and all issued certificates live there, so they survive a resize or an OS disk swap.

Step 5 - Retrieve your credentials

Nginx Proxy Manager ships a well-known default login (admin@example.com / changeme). This image never has one. The first boot of every VM creates a unique admin account for both components and writes it to a root-only file:

sudo ls -l /root/nginx-proxy-manager-authelia-credentials.txt

The file is 0600 root:root. Read it to get your credentials:

sudo cat /root/nginx-proxy-manager-authelia-credentials.txt

It contains the Nginx Proxy Manager admin email and password, the Authelia admin username and password, and the three URLs that the appliance serves. Store them in your password manager, then confirm the versions and the health endpoint:

authelia --version sudo docker exec nginx-proxy-manager nginx -t curl -s http://127.0.0.1:81/api/ | python3 -m json.tool

setup reports true, which is Nginx Proxy Manager's way of saying that an administrator already exists. The unauthenticated setup wizard window never opens on your VM.

Step 6 - Watch forward authentication work

This is what the appliance exists to do. The commands below run against the two proxy hosts that the image created for you. The shipped hostnames are examples, so --resolve points them at the VM itself. After Step 9, you use your own DNS instead.

An unauthenticated request to the protected host is redirected to the Authelia login portal:

curl -s -k -o /dev/null -D - -m 15 --resolve app.example.com:443:127.0.0.1 \
  https://app.example.com/ | grep -iE '^HTTP/|^location:'

You get a 302 and a location: header that points at auth.example.com, with an rd= parameter carrying the page you asked for. The protected content itself is never served:

if curl -s -k -m 15 --resolve app.example.com:443:127.0.0.1 https://app.example.com/ \
  | grep -q 'Protected demo application'; then echo 'LEAKED: protected content was served without authentication'; exit 1 else echo 'protected content withheld from the unauthenticated request' fi

Nothing leaked. Next, check that the portal rejects a wrong password:

curl -s -k -o /dev/null -w 'wrong password: HTTP %{http_code}\n' -m 15 \
  --resolve auth.example.com:443:127.0.0.1 \
  -X POST -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"definitely-not-the-password"}' \
  https://auth.example.com/api/firstfactor

The portal returns HTTP 401. Finally, sign in with your real per-VM password and use the resulting session against the same protected URL:

AUTHELIA_PASSWORD=$(sudo grep -m1 '^AUTHELIA_ADMIN_PASSWORD=' /root/nginx-proxy-manager-authelia-credentials.txt | cut -d= -f2-)
JAR=$(mktemp)
curl -s -k -c "$JAR" -m 20 --resolve auth.example.com:443:127.0.0.1 \
  -X POST -H 'Content-Type: application/json' \
  -d "{\"username\":\"admin\",\"password\":\"$AUTHELIA_PASSWORD\"}" \
  https://auth.example.com/api/firstfactor echo
SESSION=$(awk '/authelia_session/ {print $7}' "$JAR" | tail -1)
PAGE=$(curl -s -k -m 20 --resolve app.example.com:443:127.0.0.1 \
  -H "Cookie: authelia_session=$SESSION" https://app.example.com/) rm -f "$JAR" printf '%s' "$PAGE" | grep -oE 'Protected demo application|Signed in as: <code>[a-z]+' \
  || { echo 'the authenticated session did not reach the protected backend'; exit 1; }

The login returns {"status":"OK"}, and the protected page now renders with Signed in as: admin. That last line is the whole point. Authelia resolved your identity, and Nginx Proxy Manager passed it to the backend in a Remote-User header.

Step 7 - Sign in through the browser

Point a browser at the protected demo host. Authelia redirects you to its login portal instead of showing the application:

Sign in as admin with the AUTHELIA_ADMIN_PASSWORD from Step 5. Authelia sends you straight back to the page that you originally asked for, and the protected application renders with your identity:

The shipped hostnames are examples. For this first look, add them to your workstation's hosts file, pointing at your VM's public IP. Alternatively, complete Step 9 first and use your own domain.

Step 8 - Open the Nginx Proxy Manager admin UI

Browse to http://<public-ip>:81/ and sign in with the NPM_ADMIN_EMAIL and NPM_ADMIN_PASSWORD from Step 5.

Open Hosts and then Proxy Hosts. Both hosts that the image created are listed, online, and already bound to the per-VM certificate:

Select the protected host and open its Advanced tab to see the forward-authentication wiring: an include of the Authelia authorisation endpoint and an include of the auth_request snippet. You copy that block onto your own hosts in Step 11.

Step 9 - Point the appliance at your own domain

Authelia scopes its session cookie to a domain, so this step is required before real use. Create DNS A records for auth.<your-domain> and your application hostnames, pointing at your VM's public IP. Then edit the Authelia configuration:

sudo nano /etc/authelia/configuration.yml

Replace every occurrence of the example domain:

  • session.cookies[0].domain becomes <your-domain>
  • session.cookies[0].authelia_url becomes https://auth.<your-domain>
  • The access_control.rules domains become auth.<your-domain> and *.<your-domain>
  • totp.issuer becomes auth.<your-domain>

Check the file parses, then restart Authelia:

sudo authelia validate-config --config /etc/authelia/configuration.yml
sudo systemctl restart authelia

Then, in the Nginx Proxy Manager admin UI, edit both proxy hosts and change their Domain Names to the matching hostnames under your domain.

Step 10 - Request a free Let's Encrypt certificate

The image ships an interim self-signed certificate whose private key was generated on your VM at first boot, so browsers warn until you replace it.

Once your DNS points at the VM and port 80 is reachable from the internet, open SSL Certificates. Choose Add SSL Certificate and then Let's Encrypt, enter your hostnames and email, accept the terms, and save.

Then edit each proxy host, open its SSL tab, select the new certificate, and enable Force SSL and HTTP/2. Nginx Proxy Manager renews the certificate automatically.

Step 11 - Protect your own application

Add a proxy host for your application in the usual way: select Hosts, Proxy Hosts, then Add Proxy Host. Point it at your backend's scheme, hostname and port, and select your certificate on the SSL tab with Force SSL enabled. Then, on the Advanced tab, paste exactly this:

include /data/nginx/custom/authelia-location.conf; location / { include /data/nginx/custom/proxy.conf; include /data/nginx/custom/authelia-authrequest.conf; proxy_pass $forward_scheme://$server:$port; }

Save the host. It is now behind Authelia. Unauthenticated visitors are redirected to your login portal, and authenticated visitors reach your backend with the Remote-User, Remote-Groups, Remote-Email and Remote-Name headers set. Many applications can consume those headers directly for automatic sign-in.

Those three snippet files are already on your VM, and you can read them to see exactly what they do:

ls -l /srv/npma/data/nginx/custom/

Access rules live in Authelia, not in Nginx Proxy Manager. Edit access_control.rules in /etc/authelia/configuration.yml to require two_factor for sensitive hosts, to restrict a host to specific subject groups, or to bypass a public path.

Step 12 - Add more users

Users live in /etc/authelia/users_database.yml. Generate an argon2id hash for the new user's password:

sudo authelia crypto hash generate argon2 --password '<new-password>' --no-confirm

Add an entry alongside admin, using the Digest: value that the command prints, then restart Authelia.

For larger deployments, point Authelia at an LDAP directory instead by replacing the authentication_backend.file section. SecureAnyCloud also publishes an Authelia image bundled with an LLDAP directory for exactly that purpose.

Network ports

  • Port 22 (tcp): SSH management
  • Port 80 (tcp): proxied HTTP, redirect to HTTPS, and the Let's Encrypt http-01 challenge
  • Port 443 (tcp): proxied HTTPS, covering the Authelia login portal and every protected host
  • Port 81 (tcp): the Nginx Proxy Manager admin web UI. Restrict it to trusted source IPs.

Authelia itself listens only on 127.0.0.1:9091 and is never reachable from outside the VM. Nginx Proxy Manager reaches it over the loopback interface. You can confirm this:

sudo ss -ltn | grep -E ':(80|443|81|9091|8099)\b' | awk '{print $1, $4}' | sort -t: -k2 -n

Where things live

  • /srv/npma/data: the Nginx Proxy Manager SQLite database, the generated nginx configs and the Authelia snippets
  • /srv/npma/letsencrypt: issued certificates
  • /srv/npma/authelia: Authelia SQLite storage, notifications and log
  • /etc/authelia/configuration.yml: the Authelia configuration, including domain, access rules and secrets
  • /etc/authelia/users_database.yml: Authelia users and argon2id password hashes
  • /opt/sac/npma/snippets: read-only master copies of the forward-auth snippets
  • /root/nginx-proxy-manager-authelia-credentials.txt: per-VM credentials, 0600 root:root

Troubleshooting

If the login portal is unreachable, check Authelia first:

sudo systemctl status authelia --no-pager | head -20 curl -s http://127.0.0.1:9091/api/health

Authelia refuses to start if authelia_url is not an https URL. If you edited the configuration and Authelia will not start again, check this first. For the proxy layer, run:

sudo docker exec nginx-proxy-manager nginx -t sudo docker logs --tail 40 nginx-proxy-manager

If a browser reaches the login portal but signing in loops back to it, the browser is discarding the session cookie. This means that the hostname you are browsing is not under the session.cookies[0].domain that you configured, or that you are browsing over plain HTTP. Steps 9 and 10 fix both causes.

To see what first boot did, run:

sudo journalctl -u nginx-proxy-manager-authelia-firstboot.service --no-pager | tail -30

Support

SecureAnyCloud publishes this image and provides 24/7 support. Contact sales@secureanycloud.com.

Deploy on Azure

Launch Nginx Proxy Manager with Authelia SSO on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure