Skip to content

API

Overview

The BunkerWeb API is the control plane used to manage BunkerWeb instances programmatically: list and manage instances, reload/stop, handle bans, plugins, jobs, configs, and more. It exposes a documented FastAPI application with strong authentication, authorization and rate limiting.

Open the interactive documentation at /docs (or <root_path>/docs if you set API_ROOT_PATH). The OpenAPI schema is available at /openapi.json.

Security

The API is a privileged control plane. Do not expose it on the public Internet without additional protections.

At a minimum, restrict source IPs (API_WHITELIST_IPS), enable authentication (API_TOKEN or API users + Biscuit), and consider putting it behind BunkerWeb with an unguessable path and extra access controls.

Prerequisites

The API service requires access to the BunkerWeb database (DATABASE_URI). It is usually deployed alongside the Scheduler and optionally the Web UI. The recommended setup is to run BunkerWeb in front as a reverse proxy and isolate the API on an internal network.

See the quickstart wizard and architecture guidance in the quickstart guide.

For production, deploy the API as its own container next to the BunkerWeb data plane and scheduler. Keep the API bound to the internal control-plane network and publish it only through BunkerWeb as a reverse proxy. This layout matches the Docker integration reference and ensures the scheduler, BunkerWeb, and API share the same settings.

x-bw-env: &bw-env
  # We use an anchor to avoid repeating the same settings for both services
  API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24" # Make sure to set the correct IP range so the scheduler can send the configuration to the instance
  DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db" # Remember to set a stronger password for the database

services:
  bunkerweb:
    # This is the name that will be used to identify the instance in the Scheduler
    image: bunkerity/bunkerweb:1.6.6-rc2
    ports:
      - "80:8080/tcp"
      - "443:8443/tcp"
      - "443:8443/udp" # For QUIC / HTTP3 support
    environment:
      <<: *bw-env # We use the anchor to avoid repeating the same settings for all services
    restart: "unless-stopped"
    networks:
      - bw-universe
      - bw-services

  bw-scheduler:
    image: bunkerity/bunkerweb-scheduler:1.6.6-rc2
    environment:
      <<: *bw-env
      BUNKERWEB_INSTANCES: "bunkerweb" # Make sure to set the correct instance name
      SERVER_NAME: "api.example.com" # Change it if needed
      MULTISITE: "yes"
      USE_REDIS: "yes"
      REDIS_HOST: "redis"
      api.example.com_USE_TEMPLATE: "bw-api"
      api.example.com_GENERATE_SELF_SIGNED_SSL: "yes"
      api.example.com_USE_REVERSE_PROXY: "yes"
      api.example.com_REVERSE_PROXY_URL: "/"
      api.example.com_REVERSE_PROXY_HOST: "http://bw-api:8888"
    volumes:
      - bw-storage:/data # This is used to persist the cache and other data like the backups
    restart: "unless-stopped"
    networks:
      - bw-universe
      - bw-db

  bw-api:
    image: bunkerity/bunkerweb-api:1.6.6-rc2
    environment:
      <<: *bw-env
      API_USERNAME: "admin"
      API_PASSWORD: "Str0ng&P@ss!" # Remember to set a stronger password for the admin user
      DEBUG: "1"
    restart: "unless-stopped"
    networks:
      bw-universe:
        aliases:
          - bw-api
      bw-db:
        aliases:
          - bw-api

  bw-db:
    image: mariadb:11
    # We set the max allowed packet size to avoid issues with large queries
    command: --max-allowed-packet=67108864
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
      MYSQL_DATABASE: "db"
      MYSQL_USER: "bunkerweb"
      MYSQL_PASSWORD: "changeme" # Remember to set a stronger password for the database
    volumes:
      - bw-data:/var/lib/mysql
    restart: "unless-stopped"
    networks:
      - bw-db

  redis: # Redis service for the persistence of reports/bans/stats
    image: redis:7-alpine
    command: >
      redis-server
      --maxmemory 256mb
      --maxmemory-policy allkeys-lru
      --save 60 1000
      --appendonly yes
    volumes:
      - redis-data:/data
    restart: "unless-stopped"
    networks:
      - bw-universe

volumes:
  bw-data:
  bw-storage:
  redis-data:

networks:
  bw-universe:
    name: bw-universe
    ipam:
      driver: default
      config:
        - subnet: 10.20.30.0/24 # Make sure to set the correct IP range so the scheduler can send the configuration to the instance
  bw-services:
    name: bw-services
  bw-db:
    name: bw-db

This isolates the API behind BunkerWeb, keeps traffic on trusted networks, and lets you enforce authentication, allowlists, and rate limits at both the control plane and the exposed hostname.

Highlights

  • Instance‑aware: broadcasts operational actions to discovered instances.
  • Strong auth: Basic for admins, Bearer admin override, or Biscuit ACL for fine‑grained permissions.
  • IP allowlist and flexible per‑route rate limiting.
  • Standard health/readiness signals and startup safety checks.

Compose boilerplates

Reverse proxy the API under /api with BunkerWeb.

x-bw-env: &bw-env
  # Shared instance control-plane allowlist for BunkerWeb/Scheduler
  API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"

services:
  bunkerweb:
    image: bunkerity/bunkerweb:1.6.6-rc2
    ports:
      - "80:8080/tcp"
      - "443:8443/tcp"
      - "443:8443/udp"  # QUIC
    environment:
      <<: *bw-env
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-services

  bw-scheduler:
    image: bunkerity/bunkerweb-scheduler:1.6.6-rc2
    environment:
      <<: *bw-env
      BUNKERWEB_INSTANCES: "bunkerweb"  # Match the instance service name
      SERVER_NAME: "www.example.com"
      MULTISITE: "yes"
      DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db"
      DISABLE_DEFAULT_SERVER: "yes"
      # Reverse-proxy the API on /api
      www.example.com_USE_REVERSE_PROXY: "yes"
      www.example.com_REVERSE_PROXY_URL: "/api"
      www.example.com_REVERSE_PROXY_HOST: "http://bw-api:8888"
    volumes:
      - bw-storage:/data
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-api:
    image: bunkerity/bunkerweb-api:1.6.6-rc2
    environment:
      DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db"  # Use a strong password
      API_WHITELIST_IPS: "127.0.0.0/8 10.20.30.0/24"                      # API allowlist
      API_TOKEN: "secret"                                                 # Optional admin override token
      API_ROOT_PATH: "/api"                                               # Match reverse-proxy path
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-db:
    image: mariadb:11
    # Avoid issues with large queries
    command: --max-allowed-packet=67108864
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
      MYSQL_DATABASE: "db"
      MYSQL_USER: "bunkerweb"
      MYSQL_PASSWORD: "changeme"  # Use a strong password
    volumes:
      - bw-data:/var/lib/mysql
    restart: unless-stopped
    networks:
      - bw-db

volumes:
  bw-data:
  bw-storage:

networks:
  bw-universe:
    name: bw-universe
    ipam:
      driver: default
      config:
        - subnet: 10.20.30.0/24
  bw-services:
    name: bw-services
  bw-db:
    name: bw-db

Same as above but leveraging the Autoconf service to discover and configure services automatically. The API is exposed under /api using labels on the API container.

x-api-env: &api-env
  AUTOCONF_MODE: "yes"
  DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db"  # Use a strong password

services:
  bunkerweb:
    image: bunkerity/bunkerweb:1.6.6-rc2
    ports:
      - "80:8080/tcp"
      - "443:8443/tcp"
      - "443:8443/udp"  # QUIC
    environment:
      AUTOCONF_MODE: "yes"
      API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-services

  bw-scheduler:
    image: bunkerity/bunkerweb-scheduler:1.6.6-rc2
    environment:
      <<: *api-env
      BUNKERWEB_INSTANCES: ""    # Discovered by Autoconf
      SERVER_NAME: ""            # Filled via labels
      MULTISITE: "yes"           # Mandatory with Autoconf
      API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
    volumes:
      - bw-storage:/data
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-autoconf:
    image: bunkerity/bunkerweb-autoconf:1.6.6-rc2
    depends_on:
      - bunkerweb
      - bw-docker
    environment:
      <<: *api-env
      DOCKER_HOST: "tcp://bw-docker:2375"
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-docker
      - bw-db

  bw-api:
    image: bunkerity/bunkerweb-api:1.6.6-rc2
    environment:
      <<: *api-env
      API_WHITELIST_IPS: "127.0.0.0/8 10.20.30.0/24"
      API_TOKEN: "secret"
      API_ROOT_PATH: "/api"
    labels:
      - "bunkerweb.SERVER_NAME=www.example.com"
      - "bunkerweb.USE_REVERSE_PROXY=yes"
      - "bunkerweb.REVERSE_PROXY_URL=/api"
      - "bunkerweb.REVERSE_PROXY_HOST=http://bw-api:8888"
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-db:
    image: mariadb:11
    command: --max-allowed-packet=67108864
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
      MYSQL_DATABASE: "db"
      MYSQL_USER: "bunkerweb"
      MYSQL_PASSWORD: "changeme"
    volumes:
      - bw-data:/var/lib/mysql
    restart: unless-stopped
    networks:
      - bw-db

  bw-docker:
    image: tecnativa/docker-socket-proxy:nightly
    environment:
      CONTAINERS: "1"
      LOG_LEVEL: "warning"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped
    networks:
      - bw-docker

volumes:
  bw-data:
  bw-storage:

networks:
  bw-universe:
    name: bw-universe
    ipam:
      driver: default
      config:
        - subnet: 10.20.30.0/24
  bw-services:
    name: bw-services
  bw-db:
    name: bw-db
  bw-docker:
    name: bw-docker

Reverse proxy path

Keep the API path unguessable and combine with the API allowlist and authentication.

If you already expose another app on the same server name with a template (e.g. USE_TEMPLATE), prefer a separate hostname for the API to avoid conflicts.

All‑In‑One

If you use the All‑In‑One image, the API can be enabled by setting SERVICE_API=yes:

docker run -d \
  --name bunkerweb-aio \
  -e SERVICE_API=yes \
  -e API_WHITELIST_IPS="127.0.0.0/8" \
  -p 80:8080/tcp -p 443:8443/tcp -p 443:8443/udp \
  bunkerity/bunkerweb-all-in-one:1.6.6-rc2

Authentication

Supported ways to authenticate requests:

  • Basic admin: When credentials belong to an admin API user, protected endpoints accept Authorization: Basic <base64(username:password)>.
  • Admin Bearer override: If API_TOKEN is configured, Authorization: Bearer <API_TOKEN> grants full access.
  • Biscuit token (recommended): Obtain a token from POST /auth using Basic credentials or a JSON/form body containing username and password. Use the returned token as Authorization: Bearer <token> on subsequent calls.

Example: get a Biscuit, list instances, then reload all instances.

# 1) Get a Biscuit token with admin credentials
TOKEN=$(curl -s -X POST -u admin:changeme http://api.example.com/auth | jq -r .token)

# 2) List instances
curl -H "Authorization: Bearer $TOKEN" http://api.example.com/instances

# 3) Reload configuration across all instances (no test)
curl -X POST -H "Authorization: Bearer $TOKEN" \
     "http://api.example.com/instances/reload?test=no"

Biscuit facts and checks

Tokens embed facts like user(<username>), client_ip(<ip>), domain(<host>), and a coarse role role("api_user", ["read", "write"]) derived from DB permissions. Admins include admin(true) while non‑admins carry fine‑grained facts such as api_perm(<resource_type>, <resource_id|*>, <permission>).

Authorization maps the route/method to required permissions; admin(true) always passes. When fine‑grained facts are absent, the guard falls back to the coarse role: GET/HEAD/OPTIONS require read; write verbs require write.

Keys are stored at /var/lib/bunkerweb/.api_biscuit_private_key and /var/lib/bunkerweb/.api_biscuit_public_key. You can also provide BISCUIT_PUBLIC_KEY/BISCUIT_PRIVATE_KEY via environment variables; if neither files nor env are set, the API generates a key pair at startup and persists it securely.

Permissions (ACL)

This API supports two authorization layers:

  • Coarse role: Tokens carry role("api_user", ["read"[, "write"]]) for endpoints without a fine‑grained mapping. Read maps to GET/HEAD/OPTIONS; write maps to POST/PUT/PATCH/DELETE.
  • Fine‑grained ACL: Tokens embed api_perm(<resource_type>, <resource_id|*>, <permission>) and routes declare what they require. admin(true) bypasses all checks.

Supported resource types: instances, global_config, services, configs, plugins, cache, bans, jobs.

Permission names by resource type:

  • instances: instances_read, instances_update, instances_delete, instances_create, instances_execute
  • global_config: global_config_read, global_config_update
  • services: service_read, service_create, service_update, service_delete, service_convert, service_export
  • configs: configs_read, config_read, config_create, config_update, config_delete
  • plugins: plugin_read, plugin_create, plugin_delete
  • cache: cache_read, cache_delete
  • bans: ban_read, ban_update, ban_delete, ban_created
  • jobs: job_read, job_run

Resource IDs: For fine‑grained checks, the second path segment is treated as resource_id when meaningful. Examples: /services/{service} -> {service}; /configs/{service}/... -> {service}. Use "*" (or omit) to grant globally for a resource type.

User and ACL configuration:

  • Admin user: Set API_USERNAME and API_PASSWORD to create the first admin at startup. To rotate creds later, set OVERRIDE_API_CREDS=yes (or ensure the admin was created with method manual). Only one admin exists; additional attempts fall back to non‑admin creation.
  • Non‑admin users and grants: Provide API_ACL_BOOTSTRAP_FILE pointing to a JSON file, or mount /var/lib/bunkerweb/api_acl_bootstrap.json. The API reads it at startup to create/update users and permissions.
  • ACL cache file: A read‑only summary is written at /var/lib/bunkerweb/api_acl.json at startup for introspection; authorization evaluates DB‑backed grants baked into the Biscuit token.

Bootstrap JSON examples (both forms supported):

{
  "users": {
    "ci": {
      "admin": false,
      "password": "Str0ng&P@ss!",
      "permissions": {
        "services": {
          "*": { "service_read": true },
          "app-frontend": { "service_update": true, "service_delete": false }
        },
        "configs": {
          "app-frontend": { "config_read": true, "config_update": true }
        }
      }
    },
    "ops": {
      "admin": false,
      "password_hash": "$2b$13$...bcrypt-hash...",
      "permissions": {
        "instances": { "*": { "instances_execute": true } },
        "jobs": { "*": { "job_run": true } }
      }
    }
  }
}

Or list form:

{
  "users": [
    {
      "username": "ci",
      "password": "Str0ng&P@ss!",
      "permissions": [
        { "resource_type": "services", "resource_id": "*", "permission": "service_read" },
        { "resource_type": "services", "resource_id": "app-frontend", "permission": "service_update" }
      ]
    }
  ]
}

Notes:

  • Passwords may be plaintext (password) or bcrypt (password_hash / password_bcrypt). Weak plaintext passwords are rejected in non‑debug builds; if missing, a random one is generated and a warning is logged.
  • resource_id: "*" (or null/empty) grants globally on that resource type.
  • Existing users can have passwords updated and additional grants applied via bootstrap.

Feature reference

The API is organised by resource-focused routers. Use the sections below as a capability map; the interactive schema at /docs documents request/response models in detail.

Core and authentication

  • GET /ping, GET /health: lightweight liveness probes for the API service itself.
  • POST /auth: exchange Basic credentials (or the admin override token) for a Biscuit. Accepts JSON, form, or Authorization headers. Admins may also continue using HTTP Basic directly on protected routes when desired.

Instances control plane

  • GET /instances: list registered instances, including creation/last-seen timestamps, registration method, and metadata.
  • POST /instances: register a new API-managed instance (hostname, optional port, server name, friendly name, method).
  • GET /instances/{hostname} / PATCH /instances/{hostname} / DELETE /instances/{hostname}: inspect, update mutable fields, or remove API-managed instances.
  • DELETE /instances: bulk removal; skips non-API instances and reports them in skipped.
  • GET /instances/ping and GET /instances/{hostname}/ping: health checks across all or individual instances.
  • POST /instances/reload?test=yes|no, POST /instances/{hostname}/reload: trigger configuration reloads (test mode performs dry-run validation).
  • POST /instances/stop, POST /instances/{hostname}/stop: relay stop commands to instances.

Global configuration

  • GET /global-config: fetch non-default settings (use full=true for the entire config, methods=true to include provenance).
  • PATCH /global-config: upsert API-owned (method="api") global settings; validation errors call out unknown or read-only keys.

Service lifecycle

  • GET /services: enumerate services with metadata, including draft status and timestamps.
  • GET /services/{service}: retrieve non-default overlays (full=false) or the full config snapshot (full=true) for a service.
  • POST /services: create services, optionally as draft, and seed prefixed variables ({service}_{KEY}). Updates the SERVER_NAME roster atomically.
  • PATCH /services/{service}: rename services, toggle draft flags, and update prefixed variables. Ignores direct edits to SERVER_NAME within variables for safety.
  • DELETE /services/{service}: remove a service and its derived configuration keys.
  • POST /services/{service}/convert?convert_to=online|draft: quickly switch draft/online state without altering other variables.

Custom configuration snippets

  • GET /configs: list custom config fragments (HTTP/server/stream/ModSecurity/CRS hooks) for a service (service=global by default). with_data=true embeds UTF-8 content when printable.
  • POST /configs and POST /configs/upload: create new snippets from JSON payloads or uploaded files. Accepted types include http, server_http, default_server_http, modsec, modsec_crs, stream, server_stream, and CRS plugin hooks. Names must match ^[\w_-]{1,255}$.
  • GET /configs/{service}/{type}/{name}: retrieve a snippet with optional content (with_data=true).
  • PATCH /configs/{service}/{type}/{name} and PATCH .../upload: update or move API-managed snippets; template- or file-managed entries stay read-only.
  • DELETE /configs and DELETE /configs/{service}/{type}/{name}: prune API-managed snippets while preserving template-managed ones, returning a skipped list for ignored entries.

Ban orchestration

  • GET /bans: aggregate active bans reported by all instances.
  • POST /bans or POST /bans/ban: apply one or multiple bans. Payloads may be JSON objects, arrays, or stringified JSON. service is optional; when omitted the ban is global.
  • POST /bans/unban or DELETE /bans: remove bans globally or per service using the same flexible payloads.

Plugin management

  • GET /plugins?type=all|external|ui|pro: list plugins with metadata; with_data=true includes packaged bytes when available.
  • POST /plugins/upload: install UI plugins from .zip, .tar.gz, or .tar.xz archives. Archives may bundle multiple plugins as long as each contains a plugin.json.
  • DELETE /plugins/{id}: remove a UI plugin by ID (^[\w.-]{4,64}$).

Job cache and execution

  • GET /cache: list cached artifacts produced by scheduler jobs, filtered by service, plugin ID, or job name. with_data=true includes printable file content.
  • GET /cache/{service}/{plugin}/{job}/{file}: fetch a specific cache file (download=true streams an attachment).
  • DELETE /cache or DELETE /cache/{service}/{plugin}/{job}/{file}: delete cache files and notify the scheduler about affected plugins.
  • GET /jobs: inspect known jobs, their schedule metadata, and cache summaries.
  • POST /jobs/run: request job execution by flagging the associated plugin(s) as changed.

Operational notes

  • Write endpoints persist to the shared database; instances pick up changes via scheduler sync or after a /instances/reload.
  • Errors are normalised to { "status": "error", "message": "..." } with appropriate HTTP status codes (422 validation, 404 not found, 403 ACL, 5xx upstream failures).

Rate limiting

Per‑client rate limiting is handled by SlowAPI. Enable/disable it and shape limits via environment variables or /etc/bunkerweb/api.yml.

  • API_RATE_LIMIT_ENABLED (default: yes)
  • Default limit: API_RATE_LIMIT_TIMES per API_RATE_LIMIT_SECONDS (e.g. 100 per 60)
  • API_RATE_LIMIT_RULES: inline JSON/CSV, or a path to a YAML/JSON file with per‑route rules
  • Storage backend: in‑memory or Redis/Valkey when USE_REDIS=yes and REDIS_* variables are provided (Sentinel supported)
  • Headers: API_RATE_LIMIT_HEADERS_ENABLED (default: yes)

Example YAML (mounted at /etc/bunkerweb/api.yml):

API_RATE_LIMIT_ENABLED: yes
API_RATE_LIMIT_DEFAULTS: ["200/minute"]
API_RATE_LIMIT_RULES:
  - path: "/auth"
    methods: "POST"
    times: 10
    seconds: 60
  - path: "/instances*"
    methods: "GET|POST"
    times: 100
    seconds: 60

Configuration

You can configure the API via environment variables, Docker secrets, and the optional /etc/bunkerweb/api.yml or /etc/bunkerweb/api.env files. Key settings:

  • Docs & schema: API_DOCS_URL, API_REDOC_URL, API_OPENAPI_URL, API_ROOT_PATH.
  • Auth basics: API_TOKEN (admin override Bearer), API_USERNAME/API_PASSWORD (create/update admin), OVERRIDE_API_CREDS.
  • ACL and users: API_ACL_BOOTSTRAP_FILE (JSON path).
  • Biscuit policy: API_BISCUIT_TTL_SECONDS (0/off disables TTL), CHECK_PRIVATE_IP (bind token to client IP unless private).
  • IP allowlist: API_WHITELIST_ENABLED, API_WHITELIST_IPS.
  • Rate limiting (core): API_RATE_LIMIT_ENABLED, API_RATE_LIMIT_TIMES, API_RATE_LIMIT_SECONDS, API_RATE_LIMIT_HEADERS_ENABLED.
  • Rate limiting (advanced): API_RATE_LIMIT_AUTH_TIMES, API_RATE_LIMIT_AUTH_SECONDS, API_RATE_LIMIT_RULES, API_RATE_LIMIT_DEFAULTS, API_RATE_LIMIT_APPLICATION_LIMITS, API_RATE_LIMIT_STRATEGY, API_RATE_LIMIT_KEY, API_RATE_LIMIT_EXEMPT_IPS, API_RATE_LIMIT_STORAGE_OPTIONS.
  • Rate limiting storage: in‑memory or Redis/Valkey when USE_REDIS=yes and Redis settings like REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_DATABASE, REDIS_SSL, or Sentinel variables are set. See the Redis settings table in docs/features.md.
  • Network/TLS: API_LISTEN_ADDR, API_LISTEN_PORT, API_FORWARDED_ALLOW_IPS, API_SSL_ENABLED, API_SSL_CERTFILE, API_SSL_KEYFILE, API_SSL_CA_CERTS.

How configuration is loaded

Precedence from highest to lowest:

  • Environment variables (e.g. container environment: or exported shell vars)
  • Secrets files under /run/secrets (Docker/K8s secrets; filenames match variable names)
  • YAML file at /etc/bunkerweb/api.yml
  • Env file at /etc/bunkerweb/api.env (key=value lines)
  • Built‑in defaults

Notes:

  • YAML supports inlining secret files with <file:relative/path>; the path is resolved against /run/secrets.
  • Set doc URLs to off/disabled/none to disable endpoints (e.g. API_DOCS_URL=off).
  • If API_SSL_ENABLED=yes, you must also set API_SSL_CERTFILE and API_SSL_KEYFILE.
  • If Redis is enabled (USE_REDIS=yes), provide Redis details; see Redis section in docs/features.md.

Authentication and users

  • Admin bootstrap: set API_USERNAME and API_PASSWORD to create the first admin. To re‑apply later, set OVERRIDE_API_CREDS=yes.
  • Non‑admins and permissions: provide API_ACL_BOOTSTRAP_FILE with a JSON path (or mount to /var/lib/bunkerweb/api_acl_bootstrap.json). The file may list users and fine‑grained grants.
  • Biscuit keys: either set BISCUIT_PUBLIC_KEY/BISCUIT_PRIVATE_KEY or mount files at /var/lib/bunkerweb/.api_biscuit_public_key and /var/lib/bunkerweb/.api_biscuit_private_key. If none are provided, the API generates and persists a key pair at startup.

TLS and networking

  • Bind address/port: API_LISTEN_ADDR (default 0.0.0.0), API_LISTEN_PORT (default 8888).
  • Reverse proxies: set API_FORWARDED_ALLOW_IPS to the proxy IPs so Gunicorn trusts X‑Forwarded‑* headers.
  • TLS termination in the API: API_SSL_ENABLED=yes plus API_SSL_CERTFILE and API_SSL_KEYFILE; optional API_SSL_CA_CERTS

Rate limiting quick recipes

  • Disable globally: API_RATE_LIMIT_ENABLED=no
  • Set a simple global limit: API_RATE_LIMIT_TIMES=100, API_RATE_LIMIT_SECONDS=60
  • Per‑route rules: set API_RATE_LIMIT_RULES to a JSON/YAML file path or inline YAML in /etc/bunkerweb/api.yml.

Startup safety

The API exits if there is no authentication path configured (no Biscuit keys, no admin user, and no API_TOKEN). Ensure at least one method is set before starting.

Startup safety: The API exits if no authentication path is available (no Biscuit keys, no admin API user, and no API_TOKEN). Ensure at least one is configured.

Root path and proxies

If you deploy the API behind BunkerWeb on a sub‑path, set API_ROOT_PATH to that path so /docs and relative routes work correctly when proxied.

Operations

  • Health: GET /health returns {"status":"ok"} when the service is up.
  • Linux service: a systemd unit named bunkerweb-api.service is packaged. Customize via /etc/bunkerweb/api.env and manage with systemctl.
  • Startup safety: the API fails fast when no authentication path is available (no Biscuit keys, no admin user, no API_TOKEN). Errors are written to /var/tmp/bunkerweb/api.error.