Deploy With Coolify
Deploy Buzz as a Docker Compose application while Coolify manages the host and Traefik proxy. The repository’s Coolify file connects Buzz to the external coolify network and stores data in buzz_buzz-data.
Before You Start
Section titled “Before You Start”You need:
- A working Coolify installation with its proxy enabled.
- A repository connection that can read the Buzz repository.
- DNS control for
buzz.example.com. - A Cloudflare API token with permission to edit DNS records for the zone.
- A configured GitHub OAuth app.
- A Coolify server whose Traefik proxy is dedicated to Cloudflare-managed domains.
- A backup of the current proxy configuration and a maintenance window for restarting it.
The procedure below changes Coolify’s server-wide letsencrypt resolver from HTTP validation to Cloudflare DNS validation. This affects certificate issuance and renewal for every application using that resolver. A mistake can interrupt TLS for all applications behind the proxy.
Proceed only when this Coolify proxy is dedicated to Cloudflare-managed domains and all applications using letsencrypt can use the Cloudflare DNS challenge. If the proxy serves any other DNS provider or must retain HTTP validation, stop here. Deploy Buzz with standalone Docker Compose or design and test a separate resolver outside this guide. The repository doesn’t provide a verified shared-proxy procedure.
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 Docker Compose application from the Buzz repository.
-
Set the Compose file to
docker-compose.coolify.yml. -
Enable Raw Docker Compose Deployment in the application settings. The repository 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-secret
Do not deploy the application until the proxy is configured. This avoids exposing Buzz with an incomplete TLS setup.
Configure Coolify’s Proxy
Section titled “Configure Coolify’s Proxy”Open Servers > Proxy and edit the Traefik Compose configuration there. Do not edit /data/coolify/proxy/docker-compose.yml directly because Coolify can replace direct changes during proxy actions or upgrades.
Copy the complete current configuration to secure off-host storage before changing it. The copy is the rollback source for the server-wide proxy.
-
Add the Cloudflare token to the Traefik service:
environment:- CF_DNS_API_TOKEN=your-cloudflare-api-token -
Remove the existing
letsencryptHTTP challenge arguments and add these DNS challenge arguments to the Traefik service’scommandlist:--certificatesresolvers.letsencrypt.acme.dnschallenge=true--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53Keep Coolify’s existing
--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.jsonargument. It preserves the resolver’s existing ACME state. -
Add these labels to the Traefik service:
traefik.http.routers.wildcard-certs.tls.certresolver=letsencrypttraefik.http.routers.wildcard-certs.tls.domains[0].main=buzz.example.comtraefik.http.routers.wildcard-certs.tls.domains[0].sans=*.buzz.example.com -
Save the proxy configuration through Servers > Proxy, then restart the proxy from that page.
The Buzz application labels intentionally set tls=true without tls.certresolver. Keep certificate issuance on the single proxy-level wildcard-certs router. Adding a resolver to the Buzz routers can create duplicate ACME challenges for the same DNS record.
Traefik requires DNS-01 validation for wildcard certificates and derives certificate names from a router’s TLS domains. See Traefik’s ACME certificate resolver documentation.
Prepare The Optional Custom Domain Control Plane
Section titled “Prepare The Optional Custom Domain Control Plane”Custom domains are disabled by default. Skip this section when the operator does not want Buzz to manage custom domains. The existing dashboard and wildcard site hosting do not require these changes.
-
Generate a high-entropy control token and keep it out of source control:
Terminal window python -c 'import secrets; print(secrets.token_urlsafe(48))' -
Add these variables to the Buzz application and redeploy it:
BUZZ_CUSTOM_DOMAINS_ENABLED=trueBUZZ_TRAEFIK_CONTROL_TOKEN=replace-with-the-generated-tokenThe application starts a private listener on port
8081with the network aliasbuzz-traefik-control. The port is not published or attached to a public Traefik router. -
Return to Servers > Proxy and add these arguments to the Traefik service’s
commandlist. Replace both token placeholders with the same generated token and replace the email placeholder:--providers.http=true--providers.http.endpoint=http://buzz-traefik-control:8081/traefik--providers.http.headers.Authorization=Bearer replace-with-the-generated-token--providers.http.pollInterval=5s--providers.http.pollTimeout=2s--providers.http.maxResponseBodySize=1048576--api=true--entrypoints.buzz-admin.address=:8082--certificatesresolvers.buzz-custom.acme.email=admin@example.com--certificatesresolvers.buzz-custom.acme.storage=/traefik/acme.json--certificatesresolvers.buzz-custom.acme.httpchallenge.entrypoint=http--certificatesresolvers.buzz-custom.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directoryKeep the existing Cloudflare DNS-01
letsencryptresolver. The newbuzz-customresolver is separate and uses Coolify’shttpentrypoint for individual customer hostnames. -
Add these labels to the Traefik service, replacing the token placeholder again:
traefik.enable=truetraefik.http.routers.buzz-runtime-api.rule=PathPrefix(`/api`) && Header(`Authorization`, `Bearer replace-with-the-generated-token`)traefik.http.routers.buzz-runtime-api.entrypoints=buzz-admintraefik.http.routers.buzz-runtime-api.service=api@internalThe
buzz-adminentrypoint is not published on the host. Its authorization rule still prevents other containers on the shared Docker network from reading Traefik runtime state. -
Save and restart the proxy through Servers > Proxy. Do not use Reset proxy configuration, which replaces custom proxy settings with Coolify defaults.
-
Open a terminal for the Buzz container and inspect the private readiness response:
Terminal window uv run python -c 'import json,os,urllib.request; token=os.environ["BUZZ_TRAEFIK_CONTROL_TOKEN"]; request=urllib.request.Request("http://localhost:8081/ready",headers={"Authorization":f"Bearer {token}"}); print(json.dumps(json.load(urllib.request.urlopen(request)),indent=2))'
The automated checks cover provider polling, protected runtime API access, entrypoint https, and service buzz@docker. They do not claim that the unused resolver or ACME storage has issued a certificate. Exercise those later with a staging hostname before production custom domains are admitted.
Back up the modified proxy configuration and /data/coolify/proxy. A proxy restart loses the HTTP provider’s in-memory snapshot until it polls Buzz again. A failed poll on a running proxy leaves the previous valid snapshot active.
Exercise Staging Routing
Section titled “Exercise Staging Routing”Keep the buzz-custom resolver on Let’s Encrypt’s staging directory during this step. Staging certificates are not trusted by browsers and must not be presented as production custom-domain support.
-
Add this variable to the Buzz application and redeploy it:
BUZZ_CUSTOM_DOMAIN_ADMISSION_ENABLED=trueBUZZ_CUSTOM_DOMAIN_INGRESS_IPS=your-public-ingress-ipBUZZ_CUSTOM_DOMAIN_ROUTING_ENABLED=true -
Open a site’s detail page, add a custom hostname, publish the displayed DNS records, and select Check ownership.
-
After ownership verifies, Buzz publishes one exact generation-qualified
Host()router through the HTTP provider. -
Wait for the dashboard to report that Traefik acknowledged the router.
-
Open the staging verification URL displayed by Buzz. The request records that the public hostname reached the expected Buzz site. No other path on that custom hostname is served during this stage.
After creating the controlled test claim, set BUZZ_CUSTOM_DOMAIN_ADMISSION_ENABLED=false and redeploy Buzz to close admission while validation continues.
To stop staging routing, set BUZZ_CUSTOM_DOMAIN_ROUTING_ENABLED=false and redeploy Buzz. Buzz immediately emits an empty custom-domain snapshot and stops serving challenge paths. Keep the provider configured until every claim reports that its router was withdrawn. Turning off BUZZ_CUSTOM_DOMAINS_ENABLED or deleting the provider first is not a withdrawal mechanism because a running Traefik instance retains its last valid snapshot after polling failures.
Enable Direct Production Domains
Section titled “Enable Direct Production Domains”Complete the staging publication, challenge, withdrawal, reuse, and proxy-restart checks before enabling production certificates.
-
Add a separate production resolver to Servers > Proxy so staging certificate history remains isolated:
--certificatesresolvers.buzz-production.acme.email=admin@example.com--certificatesresolvers.buzz-production.acme.storage=/traefik/acme-production.json--certificatesresolvers.buzz-production.acme.httpchallenge.entrypoint=http -
Save and restart the proxy, then configure Buzz and redeploy it:
BUZZ_TRAEFIK_CERT_RESOLVER=buzz-productionBUZZ_CUSTOM_DOMAIN_INGRESS_IPS=your-public-ingress-ipBUZZ_CUSTOM_DOMAIN_ORIGIN_HOST=coolify-proxyBUZZ_CUSTOM_DOMAIN_ROUTING_ENABLED=trueBUZZ_MAX_CUSTOM_DOMAINS_PER_SITE=5BUZZ_MAX_CUSTOM_DOMAINS_PER_USER=20BUZZ_MAX_CUSTOM_DOMAINS_SERVER_WIDE=1000Replace the example ingress address with every public IPv4 or IPv6 address that reaches this proxy. Every DNS answer for a direct custom hostname must be public and present in this allowlist. Keep staging and production resolvers in separate ACME storage files; an existing staging certificate in Traefik’s global TLS store can suppress production issuance for the same hostname.
-
Keep
BUZZ_CUSTOM_DOMAIN_ADMISSION_ENABLED=falseduring controlled rollout. Set it totrueonly when site owners should be able to create new claims. The three positive quota settings limit pending and verified aliases per site, per user, and across the server. An alias awaiting acknowledged withdrawal continues to consume quota.
Buzz marks each direct routed hostname active only after its DNS answers match the ingress allowlist and a trusted TLS request through coolify-proxy:443 returns the exact generation challenge. Multiple aliases can serve one deployment, but each retains independent ownership, routing, TLS, diagnostics, and removal state. Custom-host requests and same-site alias referrers resolve through the canonical Buzz site identity.
Enable Cloudflare Proxy Domains
Section titled “Enable Cloudflare Proxy Domains”Cloudflare proxy claims and activation are independently disabled by default. First enable diagnostic admission for a controlled claim:
BUZZ_CLOUDFLARE_DIAGNOSTICS_ENABLED=trueKeep general custom-domain admission and routing enabled. Users must retain 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 bundled Cloudflare ranges and pins probes to a validated address. Mixed Cloudflare and non-Cloudflare answers fail before connection. Edge TLS, edge challenge, origin, and errors 1014, 525, and 526 remain separate diagnostics. Other CDNs are unsupported.
The port-80 check sends Buzz’s generation challenge without following redirects. It does not validate Traefik ACME HTTP-01. Before activation, use a controlled zone to prove a fresh /.well-known/acme-challenge/* request in Traefik and origin logs. Then add BUZZ_CLOUDFLARE_ACTIVATION_ENABLED=true and redeploy.
Automatic connection-path detection defaults off. It is available only when direct routing and Cloudflare diagnostics and activation are all ready, including current Cloudflare ranges and the transition runtime. After validating observations for existing direct and Cloudflare claims, add BUZZ_AUTOMATIC_DOMAIN_TRANSITION_ADMISSION_ENABLED=true and redeploy. Buzz then validates supported path changes without replacing the domain claim. Closing admission stops new transitions while active transitions continue to completion, cancellation, or their deadline.
Set BUZZ_CUSTOM_DOMAIN_OPERATOR_TOKEN to one or more dedicated bearer tokens separated by commas. On the private control port 8081, GET /operator/domain-transitions reports active transitions with current generation-qualified DNS and path evidence. POST /operator/domain-transitions/{claim_id}/cancel validates the current effective path before retaining it and returns 404 for an unknown claim or 409 when cancellation is unsafe or loses a race. Operator routes authenticate before route or method handling and return no-store JSON errors. Do not reuse BUZZ_TRAEFIK_CONTROL_TOKEN, expose these endpoints through the public app, or log/send the token outside the authorization header.
Activation requires current TXT ownership, Cloudflare-only DNS, edge TLS, the exact public generation challenge, trusted origin TLS, and the acknowledged router generation. Ownership loss, DNS leaving Cloudflare, or a challenge identity mismatch stops serving immediately. Edge and origin transport failures receive three consecutive one-minute attempts before Buzz stops serving. A healthy pass automatically activates or recovers the hostname.
The bundled range snapshot fails closed after 180 days. Update it from Cloudflare’s official ips-v4 and ips-v6 endpoints with uv run python scripts/update_cloudflare_ranges.py, review the diff, run the diagnostic tests, and deploy a new server build. Do not fetch range policy dynamically at runtime.
Setting BUZZ_CLOUDFLARE_DIAGNOSTICS_ENABLED=false blocks new claims but preserves existing routes. To disable activation, first close general and Cloudflare admission, remove every Cloudflare claim, and wait for acknowledged router withdrawal. Buzz refuses to start with BUZZ_CLOUDFLARE_ACTIVATION_ENABLED=false while an activated Cloudflare router remains.
If the proxy fails to restart, Buzz has no valid certificate, or another application loses TLS, paste the complete saved configuration back into Servers > Proxy, save it, and restart the proxy. Do not keep retrying certificate issuance against the production Let’s Encrypt endpoint while the same error persists.
Deploy And Verify Buzz
Section titled “Deploy And Verify Buzz”-
Deploy the application through Coolify.
-
Confirm that the application is running. 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 and check that the server didn’t report missing GitHub credentials.
-
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. -
Open one existing application that also uses the
letsencryptresolver and confirm that its certificate and route still work.
If Coolify replaces the proxy settings, return to Servers > Proxy, restore the saved configuration there, and restart the proxy. See Troubleshoot Self-Hosting for routing and certificate failures.