BunkerWeb Web Application Firewall on Ubuntu 24.04 on Azure User Guide
Overview
BunkerWeb is an open source Web Application Firewall and reverse proxy built on top of nginx. It sits in front of your web applications and APIs and applies a broad set of security defences out of the box — a ModSecurity engine with the OWASP Core Rule Set, automatic HTTPS, bad behaviour and rate limiting, bot and country filtering, request and header hardening — all driven from a clean web dashboard. The cloudimg image deploys BunkerWeb v1.6.13 as the supported single node All-In-One container (nginx, the scheduler, the web UI, a bundled database and Redis), pinned by image digest, so a production grade WAF is online within a minute or two of launch.
What is included:
- BunkerWeb v1.6.13 (AGPL-3.0) deployed from the official
bunkerity/bunkerweb-all-in-oneimage, pinned by digest so the image never silently changes at build time - Docker Engine (Docker CE) and the Docker Compose plugin, installed from the official Docker repository as the container runtime
- Two systemd services —
docker.serviceandbunkerweb.service(which runs the All-In-One container) — plus a one-shotbunkerweb-firstboot.service - The web dashboard reached over HTTPS through BunkerWeb itself at a per-VM secret path — the raw UI port (7000) is bound to localhost only and never exposed
- A unique administrator password, Flask session secret and 2FA encryption key generated on the first boot of every VM and written to a root only file — no default login ships in the image
- A fully patched Ubuntu 24.04 LTS base with unattended security updates enabled
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key, and a VNet + subnet. Standard_B2ms (2 vCPU / 8 GB) is the recommended size — BunkerWeb's All-In-One stack needs at least 2 vCPU and 8 GB RAM; choose a larger size (for example Standard_D4s_v3, 16 GB) if you will protect high traffic sites. NSG inbound rules: allow 22/tcp from your management CIDR for SSH, and 80/tcp and 443/tcp from the clients that need the protected sites and the dashboard. Do not open port 7000 — the raw web UI is intentionally bound to localhost only.
Step 1: Connect over SSH
Replace <vm-ip> with the public IP of your VM. The default login user is azureuser.
ssh azureuser@<vm-ip>
Step 2: Confirm the services are active
sudo systemctl is-active docker bunkerweb
test -f /var/lib/cloudimg/bunkerweb-firstboot.done && echo "first boot: complete"
docker ps --format '{{.Names}} {{.Status}}'
You should see active printed twice, first boot: complete, and the bunkerweb-aio container reporting Up ... (healthy). bunkerweb.service only starts once first boot has written your per-VM secrets, so a fresh VM is never left running with a default login.

Step 3: Retrieve your unique dashboard credentials
A unique administrator password, Flask session secret, 2FA encryption key and a hard-to-guess dashboard path are generated for your instance on its first boot and written to a root only file (0600 root:root). The full login URL — including the secret path — is in the file.
sudo ls -l /root/bunkerweb-credentials.txt
sudo cat /root/bunkerweb-credentials.txt
The file holds your BUNKERWEB_URL (replace <VM-PUBLIC-IP> with this VM's public IP address from the Azure portal), bunkerweb.username, bunkerweb.password and the secret bunkerweb.ui_path. Note the BUNKERWEB_URL value — that is exactly where you sign in.

Step 4: Verify the administrator login works
This confirms the per-VM administrator credentials authenticate against the BunkerWeb web UI over the same CSRF-protected login flow a browser uses. It reads the generated username, password and secret path from the credentials file and signs in.
CREDS=/root/bunkerweb-credentials.txt
USER=$(sudo grep '^bunkerweb.username=' "$CREDS" | cut -d= -f2-)
PASS=$(sudo grep '^bunkerweb.password=' "$CREDS" | cut -d= -f2-)
UIPATH=$(sudo grep '^bunkerweb.ui_path=' "$CREDS" | cut -d= -f2-)
sudo /usr/local/sbin/bunkerweb-login-check.sh "$USER" "$PASS" "$UIPATH"
An OK: line confirms the per-VM administrator credentials authenticate and the dashboard is live.
Step 5: Confirm the security posture
The WAF listens on ports 80 and 443. HTTP is redirected to HTTPS, and the dashboard is served over HTTPS at your secret path. The raw web UI (port 7000) is bound to localhost only, and the container image is pinned by digest.
UIPATH=$(sudo grep '^bunkerweb.ui_path=' /root/bunkerweb-credentials.txt | cut -d= -f2-)
curl -ks -o /dev/null -w 'HTTP -> HTTPS redirect: %{http_code}\n' http://127.0.0.1/
curl -ks -o /dev/null -w 'dashboard through the WAF: %{http_code}\n' "https://127.0.0.1${UIPATH}"
sudo ss -tlnH 'sport = :7000' | awk '{print "raw UI 7000 bound to:", $4}'
sudo docker inspect --format 'image pinned: {{.Config.Image}}' bunkerweb-aio
A 301 redirect to HTTPS and a 303/200 from the dashboard path confirm the WAF is serving; the raw UI shows 127.0.0.1:7000 (localhost only).

Step 6: Confirm first boot and patching
systemctl is-enabled bunkerweb-firstboot.service 2>/dev/null || echo "disabled (already ran on first boot)"
uname -r
apt-mark showhold
The bunkerweb-firstboot.service reports disabled — it runs once on first boot to generate your unique credentials, then disables itself. apt-mark showhold prints nothing, so no security updates are held back.

Step 7: Sign in to the BunkerWeb dashboard
Browse to the BUNKERWEB_URL from Step 3 — https://<VM-PUBLIC-IP>/<secret-path>/ — accepting the self-signed certificate warning, and sign in with your generated username and password. The dashboard answers on whatever address you use, so the same secret path also works over the VNet private IP or an SSH tunnel.

Step 8: The dashboard
After signing in you land on the Home dashboard — your single pane of glass. It shows the running BunkerWeb instance and version, counts of your instances, services and plugins, and live traffic metrics: total requests, blocked requests, blocked unique IPs, and a request status distribution chart. The left sidebar gives you Instances, Global Settings, Services, Configs, Templates, Plugins, Cache, Reports, Bans, Jobs and Logs.

Step 9: Tune the security settings
Open Global Settings to configure BunkerWeb's protections. Each security feature is a plugin — General, ACME (automatic Let's Encrypt certificates), Antibot, Anti DDoS, Auth basic, rate limiting, the ModSecurity WAF with the OWASP Core Rule Set, bad behaviour, reverse proxy and more. Settings applied here become the defaults for every protected service; individual services can override them.

Step 10: Put your own site behind BunkerWeb
Open Services → Create new service to protect a backend. Give the service a server name (a domain that resolves to this VM), point it at your upstream application with the reverse proxy settings, and choose its security mode (Block actively blocks malicious traffic; Detect only logs). BunkerWeb then proxies and protects that site on ports 80/443, requesting a Let's Encrypt certificate automatically when the domain resolves publicly.

Step 11: Day-2 operations
Manage the WAF with systemd and Docker:
sudo systemctl status bunkerweb --no-pager | head -5
docker logs --tail 20 bunkerweb-aio
BunkerWeb's configuration, database and Redis state live in the bw-data Docker volume on the OS disk. Keep the OS patched (unattended security upgrades are enabled by default). To move to a newer BunkerWeb release, review the upstream release notes first, then update the pinned image reference in /etc/systemd/system/bunkerweb.service and restart the service.
Support
cloudimg provides 24/7 technical support by email and chat: deployment, the dashboard and secret path, putting sites behind the WAF, tuning the OWASP Core Rule Set and other protections, Let's Encrypt certificates, reverse proxy configuration, rate limiting and bot protection, and runtime tuning.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. BunkerWeb is a trademark of its respective owner.