Deploy With Coolify
Deploy Buzz as a Docker Compose application while Coolify manages the host and Traefik proxy. A deployment repository you control holds the Compose file, which connects Buzz to the external coolify network and stores data in buzz_buzz-data.
Buzz needs no proxy changes. It obtains the certificate for buzz.example.com and *.buzz.example.com itself and publishes it, together with any custom-domain routers, to /data/coolify/proxy/dynamic, which Coolify’s default proxy already watches.
Before You Start
Section titled “Before You Start”You need:
- A working Coolify installation with its default proxy enabled.
- A git repository you control for the deployment, readable by Coolify.
- The DNS zone of
buzz.example.comhosted on Cloudflare, with the records from Configure DNS And TLS. - A Cloudflare API token with Zone:Read and DNS:Edit, scoped to that zone.
- A configured GitHub OAuth app.
Complete the access decision in the Self-Hosting Overview before deployment. Configure upstream controls first if Buzz is for a closed group.
Create The Application
Section titled “Create The Application”-
Create a deployment repository containing this Compose file as
docker-compose.yml. It runsghcr.io/infomiho/buzzstaticpinned to the current release:docker-compose.yml services:server:image: ghcr.io/infomiho/buzzstatic:0.7.1 # x-release-please-versionrestart: unless-stoppedenvironment:- BUZZ_DATA_DIR=/data- BUZZ_DOMAIN=${BUZZ_DOMAIN}- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}- BUZZ_ANALYTICS_SECRET=${BUZZ_ANALYTICS_SECRET:-}- BUZZ_MAX_ARCHIVE_BYTES=${BUZZ_MAX_ARCHIVE_BYTES:-524288000}- BUZZ_MAX_SITE_BYTES=${BUZZ_MAX_SITE_BYTES:-524288000}- BUZZ_MAX_SITE_FILES=${BUZZ_MAX_SITE_FILES:-10000}- BUZZ_MAX_ARCHIVE_PATH_BYTES=${BUZZ_MAX_ARCHIVE_PATH_BYTES:-1024}- BUZZ_ALLOW_REGISTRATION=${BUZZ_ALLOW_REGISTRATION:-true}- BUZZ_ALLOWED_GITHUB_USERS=${BUZZ_ALLOWED_GITHUB_USERS:-}- BUZZ_TRAEFIK_DYNAMIC_DIR=/traefik-dynamic- BUZZ_CLOUDFLARE_DNS_API_TOKEN=${BUZZ_CLOUDFLARE_DNS_API_TOKEN}- BUZZ_ACME_DIRECTORY_URL=${BUZZ_ACME_DIRECTORY_URL:-}- BUZZ_CUSTOM_DOMAINS_ENABLED=${BUZZ_CUSTOM_DOMAINS_ENABLED:-false}- BUZZ_TRAEFIK_HTTPS_ENTRYPOINT=https- BUZZ_CUSTOM_DOMAIN_OPERATOR_TOKEN=${BUZZ_CUSTOM_DOMAIN_OPERATOR_TOKEN:-}- BUZZ_MAX_CUSTOM_DOMAINS_PER_SITE=${BUZZ_MAX_CUSTOM_DOMAINS_PER_SITE:-5}- BUZZ_MAX_CUSTOM_DOMAINS_PER_USER=${BUZZ_MAX_CUSTOM_DOMAINS_PER_USER:-20}- BUZZ_MAX_CUSTOM_DOMAINS_SERVER_WIDE=${BUZZ_MAX_CUSTOM_DOMAINS_SERVER_WIDE:-1000}- BUZZ_CUSTOM_DOMAIN_INGRESS_IPS=${BUZZ_CUSTOM_DOMAIN_INGRESS_IPS:-}- BUZZ_CUSTOM_DOMAIN_ORIGIN_HOST=${BUZZ_CUSTOM_DOMAIN_ORIGIN_HOST:-coolify-proxy}- BUZZ_TRAEFIK_CERT_RESOLVER=${BUZZ_TRAEFIK_CERT_RESOLVER:-letsencrypt}- BUZZ_CUSTOM_DOMAIN_RECONCILE_SECONDS=${BUZZ_CUSTOM_DOMAIN_RECONCILE_SECONDS:-5}volumes:- buzz-data:/data- /data/coolify/proxy/dynamic:/traefik-dynamicexpose:- "8080"- "8081"healthcheck:test: ["CMD", "/app/.venv/bin/python", "-m", "server.healthcheck"]interval: 30stimeout: 5sstart_period: 10sretries: 3networks:- coolifylabels:- traefik.enable=true- "traefik.http.routers.buzz.rule=Host(`${BUZZ_DOMAIN}`)"- traefik.http.routers.buzz.entrypoints=https- traefik.http.routers.buzz.tls=true- "traefik.http.routers.buzz-wildcard.rule=HostRegexp(`.+\\.${BUZZ_DOMAIN}`)"- traefik.http.routers.buzz-wildcard.entrypoints=https- traefik.http.routers.buzz-wildcard.tls=true- traefik.http.services.buzz.loadbalancer.server.port=8080networks:coolify:external: truevolumes:buzz-data:name: buzz_buzz-data -
Create a Docker Compose application from your deployment repository with the Compose file set to
docker-compose.yml. -
Enable Raw Docker Compose Deployment in the application settings. The Compose file supplies the routing labels directly. Coolify documents this mode in Raw Docker Compose Deployment.
-
Leave FQDN/Domains empty. The Compose labels define both the base and wildcard routes.
-
Add these environment variables:
BUZZ_DOMAIN=buzz.example.comGITHUB_CLIENT_ID=your-github-client-idGITHUB_CLIENT_SECRET=your-github-client-secretBUZZ_CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-api-tokenBUZZ_DOMAINmust be a bare hostname. Buzz exits at startup when it is missing or contains a scheme, port, or path. The Compose file passes the other settings it lists from the application’s environment, as described in the configuration reference.
Deploy And Verify Buzz
Section titled “Deploy And Verify Buzz”-
Deploy the application through Coolify.
-
Confirm that the application is running and healthy. The Compose file expects Coolify’s external Docker network to be named
coolify. If deployment reports that this network doesn’t exist, confirm the server’s network name before changing the Compose file. -
Open the application logs. The server must report no
ERROR:line, and after the first issuance it logsWildcard certificate issued, valid untilwith the expiry date. -
Request the health endpoint:
Terminal window curl --fail --show-error https://buzz.example.com/health -
Run the certificate inspection command in Verify DNS, Routing, And TLS and confirm that the certificate covers
*.buzz.example.com. The first issuance takes up to a few minutes. Until then the proxy serves its default certificate and/healthreports"status": "degraded". Buzz checks the served certificate again a few seconds after it publishes one.
See Troubleshoot Self-Hosting for routing and certificate failures.
How Buzz Publishes Its Certificate
Section titled “How Buzz Publishes Its Certificate”The Compose file bind-mounts /data/coolify/proxy/dynamic at /traefik-dynamic and sets BUZZ_TRAEFIK_DYNAMIC_DIR to it. Buzz keeps the certificate, its key, and the ACME account key in /data/tls. It writes the certificate to buzz-certificate.yaml in the dynamic directory and checks that file every five minutes, rewriting it when it is missing or changed without placing a new order. Buzz renews when a third of the certificate’s lifetime remains. The Buzz routers set tls=true without a certificate resolver.
Buzz also requests a random site hostname through coolify-proxy:443 every 5 minutes. When that certificate is invalid or expires within 21 days, /health reports "status": "degraded" and the dashboard shows a warning. /health is also degraded while Buzz cannot obtain, save, or publish the certificate. wildcard_certificate_error then holds a short code, such as not_configured when BUZZ_CLOUDFLARE_DNS_API_TOKEN is missing or issuance_failed, and the application logs hold the detail. After a failure Buzz retries after 2, 5, 15, and 30 minutes, then every 30 minutes. /health keeps returning HTTP 200, and sites keep being served. Monitor the status field rather than the exact response body.
Keep Buzz the only source of this certificate. A proxy-issued wildcard for the same names makes Traefik pick either certificate at random after a restart, and nothing renews the proxy’s copy.
Run one Buzz application per Coolify server. The file names in the dynamic directory, the Traefik router names, and the buzz_buzz-data volume name are fixed, so a second instance on the same proxy overwrites the first one’s certificate and routes.
The certificate’s private key is part of buzz-certificate.yaml, so Coolify users who can manage the server see it under Servers > Proxy > Dynamic Configurations. Other team members see only the file names. Users who can manage the server can also read the proxy’s acme.json and run commands there, so the key adds no new exposure. Treat that access as access to the certificate.
Keep The Proxy Default
Section titled “Keep The Proxy Default”Buzz works with Coolify’s default proxy configuration, so Reset configuration to default in Servers > Proxy is always safe for Buzz. When the proxy fails to restart or another application loses TLS, reset it and restart the proxy.
Do not add Traefik dashboard or API labels, such as a router with service=api@internal, to the proxy. Buzz doesn’t need them, and on the shared coolify network they expose Traefik’s runtime state.
Buzz writes only buzz-certificate.yaml and buzz-routes.yaml, each validated and replaced atomically, and never touches other files in /data/coolify/proxy/dynamic. One malformed file anywhere in that directory still stops Traefik from loading changes to every file there, and a proxy restart then drops all of their routes and certificates, including Coolify’s own, until the file is fixed or removed. Coolify lists these files under Servers > Proxy > Dynamic Configurations. Do not edit Buzz’s files there.
Enable Custom Domains
Section titled “Enable Custom Domains”Custom domains are disabled by default and need no proxy changes. Skip this section when the operator does not want Buzz to manage custom domains.
-
Add these variables to the Buzz application and redeploy it:
BUZZ_CUSTOM_DOMAINS_ENABLED=trueBUZZ_CUSTOM_DOMAIN_INGRESS_IPS=your-public-ingress-ipList every public IPv4 or IPv6 address that reaches this proxy, separated by commas. Every DNS answer for a direct custom hostname must be public and present in this list.
-
Open the site detail page of any site. The custom-domain section reports whether the control plane is ready.
-
Add a custom hostname, publish the displayed DNS records, and select Check ownership.
After ownership verifies, Buzz adds one generation-qualified Host() router to buzz-routes.yaml. Each version of that file carries a revision router on the reserved hostname buzz-config-revision.invalid, and Buzz treats a change as applied only once a probe through coolify-proxy:443 shows Traefik serving that revision. Buzz needs no access to Traefik’s API.
Traefik requests each custom hostname’s certificate over HTTP-01 through letsencrypt, Coolify’s default resolver. If your letsencrypt resolver uses DNS-01, add a separate HTTP-01 resolver with its own ACME storage file in Servers > Proxy and set BUZZ_TRAEFIK_CERT_RESOLVER to its name. A DNS-01 resolver cannot issue certificates for customer zones.
BUZZ_MAX_CUSTOM_DOMAINS_PER_SITE, BUZZ_MAX_CUSTOM_DOMAINS_PER_USER, and BUZZ_MAX_CUSTOM_DOMAINS_SERVER_WIDE limit pending and verified hostnames. A hostname awaiting withdrawal still counts.
Cloudflare Proxy Domains
Section titled “Cloudflare Proxy Domains”Onboarding is automatic. Buzz detects whether a hostname points directly at the ingress addresses or through Cloudflare and switches when DNS changes. Site owners must keep the TXT ownership record, enable orange-cloud proxying, and select Cloudflare Full (strict). Bypass cache, redirects, WAF, Workers, Access, and managed challenges for /.well-known/buzz-domain-check/* and /.well-known/acme-challenge/*.
Buzz uses no customer Cloudflare credentials. It validates resolved addresses against a bundled snapshot of Cloudflare’s ranges. The snapshot fails closed after 180 days. Upgrade Buzz to refresh it.
Operator Endpoints
Section titled “Operator Endpoints”Set BUZZ_CUSTOM_DOMAIN_OPERATOR_TOKEN to one or more bearer tokens separated by commas. Buzz then listens on the private port 8081:
GET /operator/domain-transitionsreports active DNS path transitions with their current evidence.POST /operator/domain-transitions/{claim_id}/cancelcancels a transition. It returns404for an unknown claim and409when cancelling is unsafe.
Do not route this port through the proxy.
Disable Custom Domains
Section titled “Disable Custom Domains”Set BUZZ_CUSTOM_DOMAINS_ENABLED=false and redeploy. Custom hostnames stop serving immediately. Buzz rewrites buzz-routes.yaml without their routers and logs All custom-domain routers are withdrawn once Traefik serves that revision. Keep BUZZ_TRAEFIK_DYNAMIC_DIR and the bind mount: without them Buzz cannot withdraw the routers, so it serves no custom hostname and /health reports custom_domains_error: routers_unmanaged.
Update Or Roll Back
Section titled “Update Or Roll Back”To update, create a cold backup and read the release notes for the versions you skip past. Then replace docker-compose.yml with the file from this page for the new release, push, and redeploy the application in Coolify.
Replace the whole file rather than only the image: tag, because the image, healthcheck, mounts, and environment change together. A 0.6 Compose file with only its tag bumped leaves the container unhealthy, because its healthcheck calls uv, which the image no longer ships.
Every redeploy interrupts sites for a few seconds. When a custom hostname’s router or certificate changes, it can answer 421 for up to about 90 seconds while Buzz checks it again.
Database migrations run automatically when the new server starts and are forward-only: an older image refuses to start against the migrated database. Before migrating, Buzz saves the database to /data/backups. To roll back, set docker-compose.yml back to the previous release, restore the pre-migration backup named in the refusal message, and redeploy.
Upgrade From 0.6
Section titled “Upgrade From 0.6”Earlier releases asked you to edit the proxy: a wildcard resolver, and for custom domains an HTTP provider, the Traefik API, and extra resolvers. This release replaces all of that. Do these steps in one maintenance window, in order. Sites are down for about 20 seconds in total, and the wildcard certificate stays valid throughout.
Prepare
Section titled “Prepare”- Create a cold backup and copy the complete configuration shown under Servers > Proxy to secure off-host storage. That copy is your proxy rollback. Coolify also saves a copy in
/data/coolify/proxy/backupson every save and reset. - Add
BUZZ_CLOUDFLARE_DNS_API_TOKENto the application’s environment variables. Without the token Buzz doesn’t issue a certificate,/healthreportsnot_configured, and you must stop after step 5. - Keep
BUZZ_TRAEFIK_CERT_RESOLVERas it is until step 8. The new default,letsencrypt, may still be DNS-01 on your proxy, and a DNS-01 resolver cannot issue certificates for custom hostnames. - Confirm that
BUZZ_DOMAINis a bare hostname such asbuzz.example.com. Otherwise the new server exits at startup and every site goes down.
Deploy
Section titled “Deploy”-
Replace
docker-compose.ymlin your deployment repository with the file from this page, keeping any local additions, then push and redeploy. Sites are down for about 7 seconds, and the proxy switches to Buzz’s certificate without a TLS gap.Then confirm that
/data/coolify/proxy/dynamic/buzz-certificate.yamlexists on the Coolify host and that/healthreports"wildcard_certificate_error": null. Do not continue otherwise: resetting the proxy without Buzz’s certificate drops TLS on every Buzz host.Until step 9, Traefik logs a provider error for
buzz-traefik-controlevery few seconds. That is expected.
Clean Up The Proxy
Section titled “Clean Up The Proxy”-
Check that every other application on this server is reachable on port
80. Reset returns theletsencryptresolver to HTTP-01, which those applications then renew with. -
Open Servers > Proxy and select Reset configuration to default. Coolify saves the default configuration immediately and keeps running the old one until the proxy restarts. The default has no
cloudflare,buzz-custom, orbuzz-productionresolver, noCF_DNS_API_TOKEN, no--api=true,buzz-admin, or--providers.httpflags, and nowildcard-certsorapi@internallabels. Check the saved configuration for them.If another application cannot use HTTP-01, skip Reset and delete only those Buzz items by hand, keeping your DNS-01
letsencryptand its token. Then add a separate HTTP-01 resolver for custom domains as described in Enable Custom Domains. -
Delete the old wildcard and the old resolver setting:
- Under Servers > Proxy > TLS Certificates, delete the entry for
buzz.example.comand*.buzz.example.com, if there is one. - Leave
/data/coolify/proxy/acme-cloudflare.jsonin place if you used the separatecloudflareresolver. Traefik ignores it without that resolver, and keeping it makes a rollback free of TLS gaps. Delete it once you no longer need to roll back. - Delete
BUZZ_TRAEFIK_CERT_RESOLVERfrom the application, unless it names an HTTP-01 resolver that still exists.
- Under Servers > Proxy > TLS Certificates, delete the entry for
-
Restart the proxy, then immediately redeploy the application. Sites are down for about 3 seconds and then about 7 seconds. Custom hostnames show the default certificate and then answer
421for up to about 90 seconds whileletsencryptissues their certificates and Buzz checks them again. -
Remove these application environment variables. No redeploy is needed. Coolify adds
BUZZ_TRAEFIK_CERT_RESOLVER=letsencryptback from the Compose file, which is correct.BUZZ_TRAEFIK_CONTROL_TOKEN,BUZZ_TRAEFIK_CONTROL_PORT,BUZZ_TRAEFIK_API_URL, andBUZZ_TRAEFIK_API_AUTHORIZATIONBUZZ_CUSTOM_DOMAIN_ACME_CA_SERVER,BUZZ_CUSTOM_DOMAIN_ROUTING_ENABLED,BUZZ_CUSTOM_DOMAIN_ADMISSION_ENABLED,BUZZ_CLOUDFLARE_DIAGNOSTICS_ENABLED,BUZZ_CLOUDFLARE_ACTIVATION_ENABLED, andBUZZ_AUTOMATIC_DOMAIN_TRANSITION_ADMISSION_ENABLED
Save
BUZZ_TRAEFIK_CONTROL_TOKENwith the proxy copy first if you may roll back.
Verify
Section titled “Verify”-
On the Coolify host, compare the serial the proxy serves with the one Buzz stored:
Terminal window openssl s_client -connect buzz.example.com:443 -servername check.buzz.example.com </dev/null 2>/dev/null | openssl x509 -noout -serialdocker run --rm -v buzz_buzz-data:/data:ro python:3.12-slim python -c "import json; print(json.load(open('/data/tls/wildcard-certificate.json'))['chain'])" | openssl x509 -noout -serialBoth lines must match. With custom domains enabled, also open a site detail page and confirm that the control plane is ready.
Roll Back
Section titled “Roll Back”- Add back the variables the old release needs:
BUZZ_TRAEFIK_CONTROL_TOKENwith the value from the saved proxy configuration, andBUZZ_TRAEFIK_CERT_RESOLVERwith its previous value. - Paste the saved proxy configuration into Servers > Proxy, save it, and restart the proxy. Buzz’s certificate keeps being served.
- Restore the previous
docker-compose.yml, push, and redeploy. - Delete
buzz-certificate.yamlandbuzz-routes.yamlfrom/data/coolify/proxy/dynamic. Otherwise stale routes and the random certificate pick return.
Traefik orders its own wildcard only once Buzz’s file is gone, so sites show the default certificate until that order completes, a few seconds in rehearsal and longer with Let’s Encrypt and Cloudflare. With the separate cloudflare resolver and acme-cloudflare.json kept, Traefik serves the old certificate again without a gap.