DockerSelf-Hosting

BunkerWeb with Docker: an open-source web application firewall for your services (Linux server, Windows and Mac)

Before you start

  • 20 min read

Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#

BunkerWeb is a free, open-source web application firewall (WAF) built on NGINX: it sits in front of your web apps as a reverse proxy, checks every request with ModSecurity and the OWASP Core Rule Set, and blocks attacks before they reach the app.

BunkerWeb official logo

Logo: the official BunkerWeb project (github.com/bunkerity/bunkerweb)

It suits a home lab or small server where an app faces the internet and bots probe it all day. It adds HTTPS with Let's Encrypt, rate limits, bot challenges, bad-IP lists and a web UI, and one container is enough. The project is alive: v1.6.17 is from October 2026, and it is not archived or renamed.

WhatValue (official docs, read November 2026)
Current versionBunkerWeb v1.6.17, released October 2026 (latest stable)
Image to pinbunkerity/bunkerweb-all-in-one:1.6.17 (tag checked on Docker Hub)
Time neededAbout 30 minutes for the first protected app (our estimate, not measured)
Size and RAMDocs recommend at least 2 vCPU and 8 GB RAM for testing, 4 vCPU and 16 GB for production
Ports80 and 443 (TCP) and 443 (UDP, QUIC) on the host; the web UI on 7000 stays inside the container
LicenceAGPLv3 (LICENSE.md at the release tag). A paid PRO edition exists; the features in this guide are the free ones
Official docsdocs.bunkerweb.io and the repository

At a glance:

  • What it is: NGINX plus ModSecurity, the OWASP rules, HTTPS, limits and a web UI, packaged as one all-in-one image.
  • Where it sits: between the internet and your app, as the reverse proxy. It can replace Traefik or Caddy for those apps, or sit behind them.
  • First thing to know: the WAF will block some legitimate requests (false positives). This guide has a section on fixing them without switching the WAF off.
  • Platforms: the server runs on Linux. The docs name no Windows or macOS host. Windows and Mac are clients here.
  • Privacy: BunkerNet (on by default) reports blocked IPs anonymously to the project. See "Security and testing".

How it fits together#

Visitors reach BunkerWeb on ports 80 and 443. It checks each request, then forwards the clean ones to your app over a private Docker network. You manage it through its own web UI, which also sits behind BunkerWeb.

Two-lane diagram: visitors send HTTPS requests to BunkerWeb on ports 80 and 443, which applies bans, rate limits and bot challenges, then ModSecurity with the OWASP rules, and forwards clean requests to your app over plain HTTP; on the right you open the web UI through BunkerWeb, and the UI and scheduler store their data in a folder on the host

Drawn for this guide from the official docs. Your app has no port open to the internet; only BunkerWeb does.

Where it sits compared with Traefik and Caddy#

Traefik and Caddy route traffic and get certificates; they do not inspect requests for attacks. BunkerWeb does both jobs, so there are three layouts:

LayoutUse it whenTrade-off
BunkerWeb alone on 80 and 443You have no proxy yet, or only a few appsSimplest. Real client IPs and Let's Encrypt work out of the box. This is the layout in the steps
Proxy in front, BunkerWeb behind itTraefik or Caddy already owns 80 and 443You must set Real IP so BunkerWeb sees visitors, not the proxy. The docs describe those settings but give no Traefik or Caddy recipe, so treat this as untested
Proxy for most apps, BunkerWeb for a fewMany apps, few of them publicThe proxy sends only chosen host names to BunkerWeb. Same Real IP rule

Before you start#

You need a Linux server you manage, Docker with Compose, and a name or IP address your browser can reach.

Linux server: Docker Engine and Docker Compose on x86_64 or arm64 (the docs list x64, x86, armv7 and arm64 images), plus curl. Free ports 80 and 443.

Run in terminalbash
docker --version
docker compose version

Windows: a client only. Check curl.exe (it ships with Windows 10 and 11):

Run in terminalpowershell
curl.exe --version

Mac: a client only. Check curl:

Run in terminalbash
curl --version

Checklist:

  • [ ] Docker and Docker Compose on the Linux server
  • [ ] Ports 80 and 443 free (sudo ss -ltn 'sport = :80 or sport = :443' prints nothing)
  • [ ] At least 2 vCPU and 8 GB RAM for a test (the docs' own figure)
  • [ ] Your current config backed up if the server already runs a proxy
  • [ ] For the public HTTPS route only: a domain, DNS A records for the UI and each app, and ports 80 and 443 forwarded to the server

Tested by DIY Lab Hub: not yet. The commands come from the official docs and release files read in November 2026. We did not run them.

Step 1: Create the data folder on the Linux server#

BunkerWeb keeps its database, cache, backups and the web UI's secret keys in one folder. We use ~/docker_volumes/bunkerweb, as all DIY Lab Hub guides do. The container runs as an unprivileged user with UID and GID 101, so the folder needs matching rights (this is the docs' own recipe).

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes/bunkerweb/data
cd ~/docker_volumes/bunkerweb
sudo chown root:101 data
sudo chmod 770 data

Windows (PowerShell)#

Not supported as the server. The docs name Linux distributions and Docker images, but no Windows host. Run this step on a Linux machine.

Mac (Terminal)#

Not supported as the server. The docs do not list macOS, and we did not test it. Run this step on a Linux machine.

Check it worked. The group is 101:

Run in terminalbash
ls -ld ~/docker_volumes/bunkerweb/data

Tip: If you skip the chown, the container cannot write to the folder and the web UI fails with permission errors. Fix the rights, do not run the container as root.

Step 2: Start BunkerWeb and a test app#

This Compose file starts the official all-in-one image and the docs' own demo app, bunkerweb-hello, on a private network. The demo app publishes no port, so the only way in is through BunkerWeb.

Linux#

Run in terminalbash
cd ~/docker_volumes/bunkerweb
cat > docker-compose.yml << 'EOF'
services:
  bunkerweb:
    image: bunkerity/bunkerweb-all-in-one:1.6.17
    container_name: bunkerweb-aio
    restart: unless-stopped
    ports:
      - "80:8080/tcp"
      - "443:8443/tcp"
      - "443:8443/udp"
    volumes:
      - ./data:/data
    networks:
      - bw-services

  hello:
    image: bunkerity/bunkerweb-hello:v1.0
    container_name: hello
    restart: unless-stopped
    networks:
      - bw-services

networks:
  bw-services:
    name: bw-services
EOF
docker compose up -d
docker compose ps
docker compose logs --tail=50 bunkerweb

Windows (PowerShell)#

Not supported as the server. Run this step on the Linux server.

Mac (Terminal)#

Not supported as the server. Docker Desktop on a Mac or PC may run the image for a throwaway test, but the docs do not cover it and visitors' addresses can be hidden by Docker's network (issue 2739). Use Linux.

Check it worked. docker compose ps shows both containers running. The docs publish no expected log text, so read the log for errors.

Tip: Environment changes only apply when the container is recreated. After editing the Compose file run docker compose up -d again, not restart.

Step 3: Finish the setup wizard#

The first visit to the server opens a setup wizard. Use the server's IP address; the certificate is self-signed at this point, so the browser warns once.

Linux#

On a Linux desktop, open https://<server-ip>/setup in the browser and accept the warning. From a terminal you can confirm the wizard answers:

Run in terminalbash
curl -k -I https://<server-ip>/setup

Windows (PowerShell)#

Open https://<server-ip>/setup in your browser. The command-line check is:

Run in terminalpowershell
curl.exe -k -I https://<server-ip>/setup

Mac (Terminal)#

Open https://<server-ip>/setup in your browser. The command-line check is:

Run in terminalbash
curl -k -I https://<server-ip>/setup

In the wizard:

  1. Enter an administrator username, email and a strong password.
  2. Enter the server name for the web UI, for example bunker.example.com on a public domain, or bunker.home.arpa at home. Tick Let's Encrypt only if the name is public and points at this server.
  3. Skip the PRO step and click Setup.

For a home name, tell each computer where it lives. Add one line to its hosts file (an operating-system step, not from the BunkerWeb docs).

Linux and Mac:

Run in terminalbash
echo "<server-ip> bunker.home.arpa hello.home.arpa" | sudo tee -a /etc/hosts

Windows, in PowerShell opened as Administrator:

Codepowershell
Add-Content -Path "$env:SystemRoot\System32\drivers\etc\hosts" -Value "<server-ip> bunker.home.arpa hello.home.arpa"

Check it worked. https://bunker.home.arpa/ (or your public name) shows the login page, and you can sign in with the account you just made.

Tip: Turn on two-factor authentication (TOTP) for the admin account right away. The UI can change every setting, so it is the most valuable target on the box.

Step 4: Protect the test app#

A "service" is one site BunkerWeb protects. You create it in the web UI.

Linux#

  1. In the UI open Services, then Create new service.
  2. Set the server name to hello.home.arpa (or hello.<your-domain>).
  3. Turn on the reverse proxy and set the host to http://hello:8080 and the URL to /. The name hello works because both containers share the bw-services network.
  4. Save. BunkerWeb applies the change within a short time.

The docs also show a variable form (SERVER_NAME, <server>_USE_REVERSE_PROXY=yes, <server>_REVERSE_PROXY_HOST=http://hello:8080) for people who prefer environment variables.

Test it from any machine. --resolve points the name at your server without a hosts entry, and -k accepts the self-signed certificate:

Run in terminalbash
curl -k -I --resolve hello.home.arpa:443:<server-ip> https://hello.home.arpa/

Windows (PowerShell)#

Do the UI steps in your browser, then test:

Run in terminalpowershell
curl.exe -k -I --resolve hello.home.arpa:443:<server-ip> https://hello.home.arpa/

Mac (Terminal)#

Do the UI steps in your browser, then test:

Run in terminalbash
curl -k -I --resolve hello.home.arpa:443:<server-ip> https://hello.home.arpa/

Check it worked. The command prints an HTTP status line with 200 and the demo app's page opens in the browser. The docs show no sample output for this command, so compare with your own result.

Tip: Do not publish the app's own port. With a ports: line, visitors can bypass BunkerWeb.

Step 5: Watch it block an attack#

The docs' own false-positive example uses a request for a system file. Send it and see the WAF answer:

Linux#

Run in terminalbash
curl -k -s -o /dev/null -w "%{http_code}\n" --resolve hello.home.arpa:443:<server-ip> "https://hello.home.arpa/?id=/etc/passwd"
docker logs bunkerweb-aio --tail 30

Windows (PowerShell)#

Run in terminalpowershell
curl.exe -k -s -o NUL -w "%{http_code}`n" --resolve hello.home.arpa:443:<server-ip> "https://hello.home.arpa/?id=/etc/passwd"

Mac (Terminal)#

Run in terminalbash
curl -k -s -o /dev/null -w "%{http_code}\n" --resolve hello.home.arpa:443:<server-ip> "https://hello.home.arpa/?id=/etc/passwd"

Check it worked. The status is 403. On the server, the log shows ModSecurity matching rules such as 930120 (OS file access) and then rule 949110, "Inbound Anomaly Score Exceeded". That is the pattern the docs show for this exact request (labelled: from the docs, we did not run it). In the web UI, the Reports page lists the blocked request.

Tip: Repeated 403s from one IP can trigger "bad behavior" and ban that address, including yours. Unban with docker exec bunkerweb-aio bwcli unban <your-ip>.

Put it behind HTTPS (with or without Traefik)#

BunkerWeb does its own HTTPS, so most readers need only the first route. The other two are for servers where Traefik or Caddy already owns ports 80 and 443.

BunkerWeb alone: Let's Encrypt built in#

Choose this if you have a public domain and no other proxy on the server.

In the service's Let's Encrypt plugin settings, set:

SettingValue
AUTO_LETS_ENCRYPTyes
EMAIL_LETS_ENCRYPTyour email, for expiry reminders
LETS_ENCRYPT_CHALLENGEhttp (default) or dns if port 80 is unreachable
USE_LETS_ENCRYPT_STAGINGyes while testing, then no

DNS A records must point each name at the server's public IP. At home with no public name, BunkerWeb serves an auto-generated certificate, and USE_CUSTOM_SSL=yes lets you load your own.

Check it worked. From any system:

Run in terminalbash
curl -I https://hello.example.com/

You get a status line over HTTPS with no certificate error. On Windows use curl.exe.

With Traefik#

Use this if Traefik already owns ports 80 and 443. Traefik ends the HTTPS connection and passes plain HTTP to BunkerWeb, which keeps scanning the traffic. The BunkerWeb docs describe the Real IP settings used here, but not a Traefik recipe, so this layout is untested by us.

Create the shared network once, finish the setup wizard first (Step 3), then change the BunkerWeb part of the Compose file. Remove the ports: lines and join the proxy network. The entrypoint name https is an example; use yours:

File contentyaml
services:
  bunkerweb:
    image: bunkerity/bunkerweb-all-in-one:1.6.17
    container_name: bunkerweb-aio
    restart: unless-stopped
    environment:
      USE_REAL_IP: "yes"
      REAL_IP_FROM: "<your-proxy-network-subnet>"
      REAL_IP_HEADER: "X-Forwarded-For"
    volumes:
      - ./data:/data
    networks:
      - bw-services
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.bunkerweb.rule=Host(`hello.<your-domain>`) || Host(`bunker.<your-domain>`)"
      - "traefik.http.routers.bunkerweb.entrypoints=https"
      - "traefik.http.routers.bunkerweb.tls=true"
      - "traefik.http.services.bunkerweb.loadbalancer.server.port=8080"

networks:
  bw-services:
    name: bw-services
  proxy:
    external: true

Create the network with docker network create proxy if it is missing, and find its subnet for REAL_IP_FROM with docker network inspect proxy. Then run docker compose up -d. Turn off BunkerWeb's own Let's Encrypt for these names, since Traefik handles the certificate. If your browser loops on redirects, check the service's HTTP-to-HTTPS redirect settings; this is our guess, not a documented fix.

Check it worked. curl -I https://hello.<your-domain>/ returns a status line over HTTPS. Send the attack request from Step 5 through Traefik and confirm a 403, then look at the Reports page: the client address must be yours, not the Traefik container's.

Without Traefik: Caddy#

Choose this if you have no Traefik and want Caddy's certificates in front of BunkerWeb. Caddy's docs describe automatic Let's Encrypt for a public domain and tls internal for a local certificate at home. This layout is untested here too.

Use the same BunkerWeb Compose change as the Traefik route, without the Traefik labels. Then create the Caddy folders. We mount the conf folder, not a single file, because Caddy's Docker docs warn against single-file mounts:

Run in terminalbash
mkdir -p ~/docker_volumes/caddy/{conf,data,config}
cd ~/docker_volumes/caddy

Save as ~/docker_volumes/caddy/docker-compose.yml (caddy:2.11.8 is the newest tag on Docker Hub when checked; release 2.11.9 exists on GitHub but its image tag was not there yet):

File contentyaml
services:
  caddy:
    image: caddy:2.11.8
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80/tcp"
      - "443:443/tcp"
      - "443:443/udp"
    volumes:
      - ./conf:/etc/caddy
      - ./data:/data
      - ./config:/config
    networks:
      - proxy

networks:
  proxy:
    external: true

Save as ~/docker_volumes/caddy/conf/Caddyfile for public domains:

File contenttext
hello.example.com, bunker.example.com {
    reverse_proxy bunkerweb-aio:8080
}

For a home-only name use tls internal:

File contenttext
hello.home.arpa, bunker.home.arpa {
    tls internal
    reverse_proxy bunkerweb-aio:8080
}

Start it with docker compose up -d. For a home certificate, install Caddy's root certificate on each device. Locate it first with docker exec caddy find /data -name root.crt, copy it out with docker cp caddy:<path-from-find> ./caddy-root.crt, and import it: Windows "Trusted Root Certification Authorities", macOS Keychain Access with "Always Trust", Linux your distribution's CA store (these OS steps are not from the Caddy docs).

Check it worked. curl -I https://hello.example.com/ returns a status line over HTTPS. For a home name the browser shows a padlock only after the root is trusted.

Tip: Never put two proxies on ports 80 and 443 at once. The second one fails to start with "address already in use". Run sudo ss -ltn 'sport = :443' to see who owns the port.

Security and testing#

BunkerWeb is itself a high-value target: it holds your certificates and can change every rule. These controls matter most.

ControlHow
Web UIKeep it behind BunkerWeb, never publish port 7000. Strong admin password and TOTP. The docs also advise a hard-to-guess URL path and source IP limits
Data folder~/docker_volumes/bunkerweb/data holds the database and UI secret keys. Readable by you and group 101 only (chmod 770)
App networkPut apps on bw-services with no ports: line, so BunkerWeb is the only way in
Switch off sharingBunkerNet is on by default and reports blocked IPs anonymously. Set USE_BUNKERNET=no if you do not want that
CrowdSecOff by default. USE_CROWDSEC=yes registers with CrowdSec's Central API and sends alert signals as well as receiving the community list

Quick tests on Linux after each change:

Run in terminalbash
cd ~/docker_volumes/bunkerweb
docker compose ps
docker compose logs --tail=100 bunkerweb
curl -k -I --resolve hello.home.arpa:443:<server-ip> https://hello.home.arpa/

Add CrowdSec#

BunkerWeb's all-in-one image has CrowdSec built in: add USE_CROWDSEC: "yes" under environment: and recreate the container. If you already run CrowdSec for Traefik, see our CrowdSec with Docker guide.

Fix false positives without turning the WAF off#

A false positive is a legitimate request the WAF blocks, often from rich-text editors, file sync and APIs. The OWASP rules add up a score per request; past the threshold (5 for requests by default, per the docs) the request gets a 403. The fix is to remove the one rule for the one place it misfires.

  1. Switch to detect mode while you look. Set SECURITY_MODE to detect for the service. It logs matches without blocking. Switch back to block (the default) when done.
  2. Find the rule. The Reports page and the ModSecurity log show rule IDs. A 403 page shows a request ID you can search in the access log. Ignore rule 949110: it is only the "score too high" rule, and the docs say never to remove it.
  3. Exclude narrowly. Open Configs, create a custom config of type modsec-crs for that service, and add a rule that matches one path and removes one rule ID:
File contentconf
SecRule REQUEST_URI "@beginsWith /your-app/save" "id:1002200,phase:1,pass,nolog,ctl:ruleRemoveById=<rule-id>"

This follows the docs and the fix confirmed in issue 3682; we did not run it. Pick a unique id. For an app the OWASP project covers, enable its ready-made exclusions instead, for example MODSECURITY_CRS_PLUGINS=nextcloud-rule-exclusions, which the maintainers recommended for Nextcloud. 4. Keep the paranoia level at 1. It is the default. Higher levels catch more but block far more normal traffic; the docs say to set it through a modsec-crs custom config (the OWASP variable is tx.blocking_paranoia_level). 5. Check the result. Repeat the failing action. It should work, and the attack request from Step 5 must still return 403.

In the all-in-one image the same config can be an environment variable named <SITE>_CUSTOM_CONF_MODSEC_CRS_<NAME>. PRO has an "Easy Resolve" button that writes exclusions for you; the steps above need no licence.

Tip: If one client is banned for "bad behavior" after your app returned its own 403 or 404 several times, that is not ModSecurity. Raise the threshold or edit BAD_BEHAVIOR_STATUS_CODES (issue 4060).

Common mistakes#

These come from the official docs' warnings, GitHub issues and common AI assistant answers.

MistakeWhy it happensDo this instead
Skipping chown root:101 on the data folderThe container runs as UID/GID 101; a bind-mounted folder owned by you is read-only for itRun the Step 1 commands before docker compose up
Blocking a rule by removing 949110It looks like the rule that blocks the app. The docs say it only adds up the scoreFind the real rule IDs (for example 941100) and exclude those for one path
Excluding a rule globally for a whole siteIt is quick, and it worksMatch one path and one rule, as in issue 4025's scoped fix. Wide exclusions leave the app unprotected
Treating an app's own 403 as a WAF blockBad Behavior counts any 403, even from your app, and bans the IP. Issue 4060 says this is by designTune BAD_BEHAVIOR_STATUS_CODES or the threshold for that service
Rich-text or file-sync apps blocked after installNextcloud calendar sync (issue 3682) was blocked by rule 200002 on XML bodiesEnable the CRS exclusions plugin (nextcloud-rule-exclusions) or exclude that rule for the DAV path
BunkerWeb logs the Docker gateway IP, not the visitorPort-forwarded Docker setups can hide the client address (issue 2739, no fix in the thread). Behind a proxy it is the proxy's addressRun on a Linux host with direct ports, or set USE_REAL_IP, REAL_IP_FROM and REAL_IP_HEADER for your proxy
Finishing the setup wizard behind a proxy that does not do HTTPSThe UI enforces HTTPS, so the wizard can fail (issue 2774)Finish the wizard on BunkerWeb directly, then add the proxy
Using HAProxy send-proxy-v2 without the matching settingsThe PROXY protocol must be on both sides or HTTPS breaks with ERR_SSL_PROTOCOL_ERROR (issue 3634)Set USE_PROXY_PROTOCOL=yes and REAL_IP_HEADER=proxy_protocol, and trust only the proxy's address
AI assistants: inventing settings such as WAF=on or PARANOIA_LEVEL=2Assistants guess variable names from other tools. BunkerWeb's real names are USE_MODSECURITY, MODSECURITY_SEC_RULE_ENGINE, SECURITY_MODECheck every name in the official features page for your version before pasting
AI assistants: putting server_name or NGINX blocks into a Compose fileThey treat it like plain NGINX. Settings live in SERVER_NAME, <server>_USE_REVERSE_PROXY and custom configsUse the settings and custom config types the docs list

Tip: Run the Step 5 attack request after every change. If it stops returning 403, you have switched the WAF off by accident.

Tip: Change one setting at a time and watch the Reports page, where each blocked request shows its rule ID.

Troubleshooting#

ProblemSolution
Wizard page does not openCheck docker compose ps and docker compose logs --tail=100 bunkerweb. Make sure ports 80 and 443 are free and use https://<server-ip>/setup
Container logs show permission errors on /dataRedo sudo chown -R root:101 data and sudo chmod -R 770 data, then docker compose up -d
403 on a normal pageTurn on SECURITY_MODE=detect, find the rule ID in Reports, and exclude it for that path (see the false-positive section)
Your own IP is banneddocker exec bunkerweb-aio bwcli unban <ip>
Let's Encrypt failsCheck DNS A records, that port 80 reaches the server, and test with USE_LETS_ENCRYPT_STAGING=yes first
Web UI loops back to the login pageReported with several UI workers (issue 3079, not reproducible later). Update to the latest 1.6.x and clear cookies
Config breaks with USE_MODSECURITY_GLOBAL_CRS=yes, or new sites get no certificateBoth were fixed in 1.6.14 (issues 3761 and 3772). Use 1.6.17
Env change has no effectRecreate the container: docker compose up -d

Known bugs#

Check the open issues before blaming your setup. We did not review the open list beyond the issues named in this guide.

Diagnostics to run first:

Run in terminalbash
docker compose ps
docker compose logs --tail=200 bunkerweb
docker inspect bunkerweb-aio --format '{{.Config.Image}}'

Updates and backups#

Update. Read the release notes, change the tag in docker-compose.yml, and back up first:

Run in terminalbash
cd ~/docker_volumes/bunkerweb
docker compose pull
docker compose up -d
docker compose logs --tail=100 bunkerweb

Back up. BunkerWeb has a built-in daily backup (USE_BACKUP, 7 kept, stored under /var/lib/bunkerweb/backups, which the web UI docs say is a link into /data). For a full copy, stop it, archive the folder and start again:

Run in terminalbash
cd ~/docker_volumes/bunkerweb
docker compose down
mkdir -p backups
tar -czf "backups/bunkerweb-$(date +%F).tar.gz" data
chmod 600 backups/bunkerweb-*.tar.gz
docker compose up -d

To run it nightly at 03:00 on Linux, add a crontab entry with the same commands. Keep a copy on another machine; the archive holds your UI secrets.

Restore test. Unpack into a new empty folder, never over the live one:

Run in terminalbash
mkdir -p /tmp/restore-test && tar -xzf backups/bunkerweb-<date>.tar.gz -C /tmp/restore-test
ls /tmp/restore-test/data

Windows (PowerShell)#

Not for the server. Copy the archive off the Linux server from your PC:

Run in terminalpowershell
scp <your-user>@<your-server-address>:~/docker_volumes/bunkerweb/backups/bunkerweb-<date>.tar.gz .

Mac (Terminal)#

Use the same scp command from Terminal.

Performance and resources#

The docs recommend 2 vCPU and 8 GB RAM for testing, and 4 vCPU and 16 GB for production. They also say to enable USE_MODSECURITY_GLOBAL_CRS=yes when RAM is short or you run many services, at the cost of per-service modsec-crs rules. We give no measured figures; measure your own server:

  • Live use: docker stats bunkerweb-aio shows CPU and memory.
  • Logs: files rotate daily or past 100 MB, 14 kept (docs).

FAQ#

Is BunkerWeb free? Yes, under AGPLv3. A paid PRO edition exists with extras such as Easy Resolve and Load Balancer. If you modify BunkerWeb and offer it to others over a network, the AGPL asks you to share your changes.

Is the project still maintained? Yes. The latest release read in November 2026 is v1.6.17, from October 2026. It is not archived and has not been renamed.

Should it replace Traefik or Caddy? For the apps it protects it can: it ends HTTPS and proxies requests itself.

Can I run it on Windows or macOS? The docs name Linux distributions and Docker images, and no Windows or macOS host. Use Windows and Mac as clients.

Does it send data to the project? BunkerNet is on by default and anonymously reports blocked IPs. CrowdSec is off by default; if you enable it, it also sends alert signals. Set USE_BUNKERNET=no to stop the first.

Sources#

Checked against the official docs on 2026-11-05.