Nginx Proxy Manager with Authelia SSO on Ubuntu 24.04 on Azure User Guide
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 designed to sit in front of proxied applications. Running one behind the other is the standard self-hosted SSO pattern, and Authelia's own documentation ships a dedicated Nginx Proxy Manager integration guide for it.
The work in that pattern is the forward authentication wiring: the auth_request snippets, the internal authorisation endpoint, the redirect back to the login portal, the TLS 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 serving the Authelia login portal, one a protected demo application - and refuses to 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 Authelia resolved.
What is included:
- 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/npmaholding both datastores and issued certificates docker.service,authelia.serviceand the first-boot unit as enabled systemd units- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a reasonable starting point. Network security group inbound rules: allow 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 the admin console is not exposed to the whole internet.
You will also want a domain you control. Authelia is domain based - its session cookie is scoped to a domain, so browser sign-in only completes at a hostname under that domain. The image ships the placeholder 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 cloudimg, and select Create. On Basics 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 Review + create and Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name npm-authelia \
--image <marketplace-image-urn> \
--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 retry.
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. A unique admin account is created for both components on the first boot of every VM and written 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 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 an administrator already exists - the unauthenticated setup wizard window never opens on your VM.

Step 6 - Watch the forward authentication working
This is what the appliance exists to do. The commands below run against the two proxy hosts the image created for you. Because the shipped hostnames are placeholders, --resolve points them at the VM itself; after Step 9 you will 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 pointing 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. Now 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
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. You will be redirected to the Authelia login portal instead of the application:

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

Because the shipped hostnames are placeholders, add them to your workstation's hosts file pointing at your VM's public IP for this first look, or simply 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 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. That block is what you copy 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 placeholder domain:
session.cookies[0].domainbecomes<your-domain>session.cookies[0].authelia_urlbecomeshttps://auth.<your-domain>- the
access_control.rulesdomains becomeauth.<your-domain>and*.<your-domain> totp.issuerbecomesauth.<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 will 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 it automatically.
Step 11 - Protect your own application
Add a proxy host for your application in the usual way - Hosts, Proxy Hosts, Add Proxy Host - pointing 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. That host is now behind Authelia: unauthenticated visitors are redirected to your login portal, and authenticated ones reach your backend with 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 are readable if you want 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, restrict a host to specific subject groups, or 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 it prints, then restart Authelia. For larger deployments, point Authelia at an LDAP directory instead by replacing the authentication_backend.file section - cloudimg also publishes an Authelia image bundled with an LLDAP directory for exactly that.
Network ports
| Port | Protocol | Purpose |
|---|---|---|
| 22 | tcp | SSH management |
| 80 | tcp | Proxied HTTP, redirect to HTTPS, Let's Encrypt http-01 challenge |
| 443 | tcp | Proxied HTTPS: the Authelia login portal and every protected host |
| 81 | tcp | Nginx Proxy Manager admin web UI - restrict 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 that:
sudo ss -ltn | grep -E ':(80|443|81|9091|8099)\b' | awk '{print $1, $4}' | sort -t: -k2 -n

Where things live
| Path | Contents |
|---|---|
/srv/npma/data |
Nginx Proxy Manager SQLite database, generated nginx configs, the Authelia snippets |
/srv/npma/letsencrypt |
Issued certificates |
/srv/npma/authelia |
Authelia SQLite storage, notifications and log |
/etc/authelia/configuration.yml |
Authelia configuration - domain, access rules, secrets |
/etc/authelia/users_database.yml |
Authelia users and argon2id password hashes |
/opt/cloudimg/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, so if you edited the configuration and it will not come back, that is the first thing to check. For the proxy layer:
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 session cookie is being discarded - that means the hostname you are browsing is not under the session.cookies[0].domain you configured, or you are browsing over plain HTTP. Both are fixed in Steps 9 and 10.
To review what first boot did:
sudo journalctl -u nginx-proxy-manager-authelia-firstboot.service --no-pager | tail -30
Support
This image is published and supported by cloudimg with 24/7 support. Contact support@cloudimg.co.uk.