Self-Host with Docker Compose
BeeBuzz publishes the beebuzz-server and beebuzz-web images to the
Codeberg Container Registry. The examples use
the latest tag for convenience; replace it with a specific release tag for
reproducible deployments.
Both recipes use Docker Compose and include Caddy as the example reverse proxy.
The gateway publishes ports 80 and 443 by default. If you already run a
reverse proxy, or use different host ports, adapt or replace the gateway
service and point it at the server and web services.
Self-host with a custom domain
Section titled “Self-host with a custom domain”BeeBuzz needs a stable HTTPS domain for its browser applications. The dashboard and Hive use secure browser origins for authentication, service workers, and Web Push, while the API and email links must point to the same instance. The example uses three subdomains under one domain:
dashboard.example.comfor the account applicationhive.example.comfor receiving notificationsapi.example.comfor the API
For a local test without your own domain, use the local testing recipe below.
Create an empty directory and save these three files in it.
compose.yaml
Section titled “compose.yaml”name: beebuzz
services: server: image: codeberg.org/beebuzz/beebuzz-server:latest restart: unless-stopped env_file: .env environment: BEEBUZZ_ENV: production BEEBUZZ_DEPLOYMENT_MODE: self_hosted BEEBUZZ_DOMAIN: ${BEEBUZZ_DOMAIN:?Set BEEBUZZ_DOMAIN in .env} BEEBUZZ_PROXY_SUBNET: 172.30.0.0/24 BEEBUZZ_DB_DIR: /var/lib/beebuzz/db BEEBUZZ_ATTACHMENTS_DIR: /var/lib/beebuzz/attachments volumes: - database:/var/lib/beebuzz/db - attachments:/var/lib/beebuzz/attachments networks: [beebuzz]
web: image: codeberg.org/beebuzz/beebuzz-web:latest restart: unless-stopped environment: BEEBUZZ_DOMAIN: ${BEEBUZZ_DOMAIN:?Set BEEBUZZ_DOMAIN in .env} networks: [beebuzz]
gateway: image: caddy:2.11-alpine restart: unless-stopped depends_on: [server, web] environment: BEEBUZZ_DOMAIN: ${BEEBUZZ_DOMAIN:?Set BEEBUZZ_DOMAIN in .env} ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config networks: [beebuzz]
networks: beebuzz: ipam: config: - subnet: 172.30.0.0/24
volumes: database: attachments: caddy_data: caddy_config:BEEBUZZ_DOMAIN=example.comBEEBUZZ_BOOTSTRAP_ADMIN_EMAIL=admin@example.comBEEBUZZ_IP_HASH_SALT=BEEBUZZ_VAPID_PRIVATE_KEY=BEEBUZZ_VAPID_PUBLIC_KEY=BEEBUZZ_MAILER_SENDER=noreply@example.comBEEBUZZ_MAILER_REPLY_TO=support@example.comBEEBUZZ_MAILER_SMTP_ADDRESS=smtp.example.com:587BEEBUZZ_MAILER_SMTP_USER=BEEBUZZ_MAILER_SMTP_PASSWORD=# Or use BEEBUZZ_MAILER_RESEND_API_KEY instead of SMTP settings.Caddyfile
Section titled “Caddyfile”dashboard.{$BEEBUZZ_DOMAIN} { reverse_proxy web:80}
hive.{$BEEBUZZ_DOMAIN} { reverse_proxy web:80}
api.{$BEEBUZZ_DOMAIN} { reverse_proxy server:8899}Create DNS records for dashboard, hive, and api pointing to the Docker
host. Generate the keys:
docker compose run --rm server salt generatedocker compose run --rm server vapid generatePaste the generated values into .env, then start the stack:
docker compose up -dCaddy obtains public certificates automatically.
Self-host locally for testing
Section titled “Self-host locally for testing”Local testing still needs HTTPS certificates installed and trusted on every
device that will open BeeBuzz. The steps below use Lancert,
which provides a hostname and publicly trusted certificates for the local
network. Its wildcard certificate also covers mail.<your-lancert-domain>,
which BeeBuzz uses for authenticated SMTP over STARTTLS. Alternatively, create
your own local certificates with a tool such as mkcert
and install its local CA on every test device and in the BeeBuzz container.
Create a separate empty directory and save these three files in it.
Install the Lancert CLI, then register the private IPv4 address of the machine running Docker:
lancert 192.168.1.50Lancert prints a hostname such as quiet-otter.lancert.dev and stores its
certificate files locally.
compose.yaml
Section titled “compose.yaml”name: beebuzz-local
services: server: image: codeberg.org/beebuzz/beebuzz-server:latest restart: unless-stopped depends_on: [mailpit] env_file: .env environment: BEEBUZZ_ENV: production BEEBUZZ_DEPLOYMENT_MODE: self_hosted BEEBUZZ_DOMAIN: ${BEEBUZZ_DOMAIN:?Set BEEBUZZ_DOMAIN in .env} BEEBUZZ_PROXY_SUBNET: 172.30.0.0/24 BEEBUZZ_DB_DIR: /var/lib/beebuzz/db BEEBUZZ_ATTACHMENTS_DIR: /var/lib/beebuzz/attachments BEEBUZZ_MAILER_SMTP_ADDRESS: mail.${BEEBUZZ_DOMAIN}:1025 volumes: - database:/var/lib/beebuzz/db - attachments:/var/lib/beebuzz/attachments networks: [beebuzz]
web: image: codeberg.org/beebuzz/beebuzz-web:latest restart: unless-stopped environment: BEEBUZZ_DOMAIN: ${BEEBUZZ_DOMAIN:?Set BEEBUZZ_DOMAIN in .env} networks: [beebuzz]
gateway: image: caddy:2.11-alpine restart: unless-stopped depends_on: [server, web] environment: BEEBUZZ_DOMAIN: ${BEEBUZZ_DOMAIN:?Set BEEBUZZ_DOMAIN in .env} ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - ${BEEBUZZ_CERTS_DIR:?Set BEEBUZZ_CERTS_DIR in .env}:/certs:ro - caddy_data:/data - caddy_config:/config networks: [beebuzz]
mailpit: image: axllent/mailpit:latest restart: unless-stopped environment: MP_SMTP_AUTH: dev:dev MP_SMTP_REQUIRE_STARTTLS: "true" MP_SMTP_TLS_CERT: /certs/fullchain.pem MP_SMTP_TLS_KEY: /certs/privkey.pem ports: - "127.0.0.1:8025:8025" volumes: - ${BEEBUZZ_CERTS_DIR:?Set BEEBUZZ_CERTS_DIR in .env}:/certs:ro networks: beebuzz: aliases: - mail.${BEEBUZZ_DOMAIN}
networks: beebuzz: ipam: config: - subnet: 172.30.0.0/24
volumes: database: attachments: caddy_data: caddy_config:BEEBUZZ_DOMAIN=quiet-otter.lancert.devBEEBUZZ_CERTS_DIR=/absolute/path/to/lancert/certs/quiet-otter.lancert.devBEEBUZZ_BOOTSTRAP_ADMIN_EMAIL=admin@example.comBEEBUZZ_IP_HASH_SALT=BEEBUZZ_VAPID_PRIVATE_KEY=BEEBUZZ_VAPID_PUBLIC_KEY=BEEBUZZ_MAILER_SMTP_USER=devBEEBUZZ_MAILER_SMTP_PASSWORD=devBEEBUZZ_MAILER_SENDER=noreply@example.comBEEBUZZ_MAILER_REPLY_TO=support@example.comThe Compose file routes BeeBuzz to mail.<BEEBUZZ_DOMAIN> and configures
Mailpit with the Lancert certificate from BEEBUZZ_CERTS_DIR. SMTP
authentication requires STARTTLS; port 1025 is available only on the private
Compose network. The Mailpit web UI is published only on localhost at
http://localhost:8025.
Caddyfile
Section titled “Caddyfile”(lancert_tls) { tls /certs/fullchain.pem /certs/privkey.pem}
dashboard.{$BEEBUZZ_DOMAIN} { import lancert_tls reverse_proxy web:80}
hive.{$BEEBUZZ_DOMAIN} { import lancert_tls reverse_proxy web:80}
api.{$BEEBUZZ_DOMAIN} { import lancert_tls reverse_proxy server:8899}Generate the keys from this directory:
docker compose run --rm server salt generatedocker compose run --rm server vapid generatePaste the generated values into .env, then start the local stack:
docker compose up -dOpen https://quiet-otter.lancert.dev and Mailpit at
http://localhost:8025.
Update your instance
Section titled “Update your instance”From the directory containing the selected recipe, pull the latest images and recreate the containers:
docker compose pulldocker compose up -dBeeBuzz applies schema migrations automatically on startup.
Sending from a self-hosted instance
Section titled “Sending from a self-hosted instance”This minimal configuration omits the optional push and hook hostnames. Use
the API paths instead:
curl https://api.example.com/v1/push \ -H "Authorization: Bearer $TOKEN" \ -F title="Hello from BeeBuzz" \ -F body="Self-hosted delivery test"Set the CLI endpoint before connecting:
export BEEBUZZ_API_URL=https://api.example.combeebuzz connectKeep your data
Section titled “Keep your data”Docker stores BeeBuzz data in the database and attachments volumes. Back up
both volumes and the .env file. Stop the server before taking a file-level
copy of the database volume:
docker compose stop server# copy the volume datadocker compose start server