Skip to content

fix: update self-hosted docs for the Traefik reverse proxy - #911

Merged
SunsetDrifter merged 8 commits into
mainfrom
cc/enterprise-traefik-tls
Aug 10, 2026
Merged

fix: update self-hosted docs for the Traefik reverse proxy#911
SunsetDrifter merged 8 commits into
mainfrom
cc/enterprise-traefik-tls

Conversation

@SunsetDrifter

@SunsetDrifter SunsetDrifter commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Why

getting-started-enterprise.sh deploys Traefik instead of Caddy, but the enterprise getting-started page still describes the Caddy stack. The script served from pkgs.netbird.io has no Caddy references at all, so operators following that page today already have Traefik in front of them.

The worst of it is the custom TLS certificate appendix: every step edits a Caddyfile the installer no longer generates, so it cannot be followed.

What changed

Custom TLS certificate appendix — rewritten for Traefik. The certificate is supplied through Traefik's file provider, which the generated stack does not enable, so this needs a new traefik-dynamic.yaml plus edits to docker-compose.yml.

The part worth reviewing closely: removing the four router certresolver labels is not sufficient. While the --certificatesresolvers.letsencrypt.acme.* flags remain, Traefik keeps loading the certificate already stored in acme.json and serves it for the deployment's own hostname in preference to the supplied one, so the stack looks like it is ignoring the operator's certificate. Both the flags and the labels have to go. The netbird_traefik_letsencrypt volume can stay as-is — with no resolver configured its contents are inert.

Renewal. Traefik watches the dynamic configuration file, not the certificate files it references, so replacing the certificates alone has no effect. The appendix now says to touch traefik-dynamic.yaml, which reloads the certificate without restarting any container. The previous Caddy instruction (docker compose restart caddy) has no Traefik equivalent.

Certificate issuance guidance corrected, on the enterprise page and in troubleshooting/certificates. Both claimed the ACME HTTP-01 challenge on port 80 matters. The generated stack sets acme.tlschallenge=true and nothing else, so TCP/443 is what has to be reachable; port 80 only serves the HTTP→HTTPS redirect. getting-started.sh sets the same flag, so the troubleshooting page was giving this advice to community deployments too — it now notes that pre-Traefik deployments still on Caddy are the exception.

Stale references. Dropped the Caddyfile row from the generated-files table (the installer writes .env, docker-compose.yml and config.yaml only), swapped caddy for traefik in the stack components table, log commands and cleanup instructions, and fixed selfhosted-guide calling the bundled proxy Caddy in one place while calling it Traefik in another.

Consequences that were previously unstated: a peer that does not trust a private issuing CA fails to connect outright rather than warning; config.yaml's empty server.tls block is intentional because netbird-server listens on plain HTTP behind Traefik; and dropping the 80:80 mapping is fine for certificates but costs the HTTP→HTTPS redirect.

Out of scope

external-reverse-proxy and the installer's "External Caddy" menu option are unchanged — those correctly describe an operator's own Caddy. maintenance/backup and migration/external-to-embedded-idp still reference the pre-v0.65 five-container layout, which is a separate cleanup.

Summary by CodeRabbit

  • Documentation
    • Updated self-hosting guides to use Traefik as the default reverse proxy.
    • Added setup and validation guidance for HTTPS certificates using TLS-ALPN-01 on port 443.
    • Expanded instructions for custom TLS configuration, certificate mounting, renewal, and verification.
    • Updated troubleshooting steps for Traefik and included guidance for legacy Caddy deployments.

`getting-started-enterprise.sh` deploys Traefik instead of Caddy, but the
enterprise getting-started page still described the Caddy stack. The worst of it
was the custom-TLS appendix, which edits a `Caddyfile` the installer no longer
generates, so it could not be followed at all.

- Rewrite the custom TLS certificate appendix for Traefik: supply the
  certificate through the file provider, then remove both the ACME resolver
  flags and the four router `certresolver` labels. Removing the labels alone is
  not enough, because the certificate already stored in `acme.json` continues to
  be served in preference to the supplied one.
- Document renewal correctly. Traefik watches the dynamic configuration file
  rather than the certificate files it references, so replacing the certificates
  has no effect until that file is touched. No container restart is needed.
- Correct the certificate issuance guidance on the enterprise page and in
  certificate troubleshooting. The generated stack uses the TLS-ALPN-01
  challenge only, so TCP/443 is what must be reachable; port 80 serves the
  HTTP→HTTPS redirect and is never used for validation. The community installer
  sets the same flag, so this applies to both.
- Drop the `Caddyfile` row from the generated-files table. The installer writes
  `.env`, `docker-compose.yml` and `config.yaml` only, and configures routing
  and TLS through Traefik labels and command flags.
- Replace `caddy` with `traefik` in the stack components table, the log
  commands, and the cleanup instructions, and fix `selfhosted-guide` still
  calling the bundled proxy Caddy where the same page elsewhere says Traefik.
- Add the consequences that were previously unstated: a peer that does not trust
  a private issuing CA fails to connect, `config.yaml`'s empty `server.tls`
  block is intentional, and dropping the `80:80` mapping costs the HTTP→HTTPS
  redirect.
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 10, 2026 12:10pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@SunsetDrifter, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 13f4318e-6056-4f45-9d22-43faadaf6949

📥 Commits

Reviewing files that changed from the base of the PR and between 60c7057 and d46204a.

📒 Files selected for processing (1)
  • src/pages/selfhosted/enterprise/getting-started.mdx
📝 Walkthrough

Walkthrough

The self-hosted documentation now uses Traefik instead of Caddy as the default reverse proxy. It updates setup, HTTPS validation, certificate troubleshooting, custom certificates, deployment commands, and legacy Caddy guidance.

Changes

Traefik self-hosted documentation

Layer / File(s) Summary
Traefik setup and deployment flow
src/pages/selfhosted/enterprise/getting-started.mdx, src/pages/selfhosted/selfhosted-guide.mdx
The guides describe Traefik prerequisites, Compose labels, validation commands, stack behavior, reset steps, and reverse-proxy setup.
Traefik certificate renewal troubleshooting
src/pages/selfhosted/enterprise/getting-started.mdx, src/pages/selfhosted/troubleshooting/certificates.mdx
The troubleshooting guidance uses TLS-ALPN-01 on TCP/443 and includes DNS, firewall, log, restart, and legacy Caddy instructions.
Traefik custom certificate configuration
src/pages/selfhosted/enterprise/getting-started.mdx
The custom certificate procedure documents file-provider configuration, certificate mounts, router labels, deployment, verification, and renewal behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Poem

A rabbit checks the proxy bright,
Traefik guides the TLS flight.
Port four-four-three guards the way,
Certificates renew each day.
Caddy’s old notes rest nearby—
Hop, hop, secure the sky!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: updating self-hosted documentation for the Traefik reverse proxy.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cc/enterprise-traefik-tls

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/selfhosted/enterprise/getting-started.mdx`:
- Around line 297-324: Update the Traefik setup instructions to use a
bind-mounted ./traefik directory at /etc/traefik/dynamic instead of mounting
traefik-dynamic.yaml directly. Replace the file provider filename command with
--providers.file.directory=/etc/traefik/dynamic, and adjust the dynamic
configuration file location and volume example accordingly while preserving the
certificate mount.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5f81a71-f816-4fb6-a92e-bfba14146703

📥 Commits

Reviewing files that changed from the base of the PR and between e2c42dd and bc6cffc.

📒 Files selected for processing (3)
  • src/pages/selfhosted/enterprise/getting-started.mdx
  • src/pages/selfhosted/selfhosted-guide.mdx
  • src/pages/selfhosted/troubleshooting/certificates.mdx

Comment thread src/pages/selfhosted/enterprise/getting-started.mdx
A bind-mounted single file is pinned to one inode, so any tool that replaces the
file rather than editing it in place — most editors, `sed -i`, many
configuration-management tools — leaves the container reading the old content
indefinitely, with no error and no way to recover by touching the new file.
Traefik's own documentation recommends `directory` over `filename` for this
reason.

Switch to `--providers.file.directory=/etc/traefik/dynamic` with `./traefik`
bind-mounted, and move the dynamic configuration to `traefik/dynamic.yaml`. The
renewal instruction is otherwise unchanged: the certificates are referenced
rather than watched, so they are picked up by touching the dynamic configuration,
without restarting any container.

Also drop the `tls.certificates` list from the example. It is redundant next to
`stores.default.defaultCertificate`, which already covers every connection
including clients that send no SNI.
The appendix sits on a page that also documents `migrate-to-enterprise.sh`, and
the two paths differ. A migrated deployment is built on the community
`getting-started.sh` render, which already configures a file provider when the
reverse proxy is enabled. `providers.file.filename` and
`providers.file.directory` are mutually exclusive, so following these steps
verbatim there would declare a conflicting second provider.

Note that deployments which already have a file provider should extend its
dynamic configuration instead, and adjust router names to match their own
Compose file.
Replace the em dashes added by the previous commits with commas, colons and
parentheses. House convention is to reach for an em dash deliberately or not at
all, and the appendix had accumulated eleven of them.

Also state when not to follow the appendix at all: the default Let's Encrypt
path renews itself, and everything in the appendix makes renewal the operator's
responsibility. Expand SNI on first use.
Running the appendix against a real migrated deployment (community install with
the built-in Traefik, then migrate-to-enterprise.sh with Postgres and traffic
flow) showed the previous note pointed at the wrong difference. The four router
names are identical to the fresh install, so there is nothing to adjust there.

What actually differs is which file holds each label. `netbird-dashboard`,
`netbird-grpc` and `netbird-backend` are in `docker-compose.yml`, while
`netbird-flow` is on the `flow-receiver` service in
`docker-compose.override.yml`, so the deletions span two files. The `traefik`
service and the ACME flags stay in `docker-compose.yml`.

Also state why a deployment that already has a file provider must extend it
rather than add a second one: `providers.file.directory` and
`providers.file.filename` are mutually exclusive.
The appendix had grown to four stacked callouts, two of them before the reader
reaches the first step. Cut it from 690 to 554 words and from four callouts to
two, without dropping anything load-bearing.

Move the migrate-path and existing-file-provider caveats out of a 106 word
preamble block and into the two steps they actually affect. Drop a decorative
sentence about all routes passing through Traefik, shorten the inode note, and
trim the SNI gloss to expanding the acronym.
The appendix is the supported way to serve a custom certificate, not a stopgap,
so it should not hard-code "four labels, one on dashboard, two on
netbird-server, one on receiver". That count goes stale the moment a router is
added, and silently.

Replace the enumeration with a grep over `docker-compose*.yml`. It is shorter,
survives new routers, and spans `docker-compose.override.yml` on a migrated
deployment, which removes the need for a separate note about where the
`netbird-flow` label lives.

Also reword the intro so the file provider reads as the mechanism you enable
rather than something the installer failed to configure.
- The `traefik` directory was never created. "Create `traefik/dynamic.yaml`"
  fails in any editor that will not create a missing parent, so add the
  `mkdir -p traefik` the steps assumed.
- `/certs` appeared in the dynamic configuration one step before the mount that
  defines it. Say that it is a container path and where it comes from.
- Point the file-provider caveat at the named edit it refers to instead of "the
  next step's first edit", and reword the grep sentence.
@SunsetDrifter
SunsetDrifter merged commit 4061897 into main Aug 10, 2026
5 checks passed
@SunsetDrifter
SunsetDrifter deleted the cc/enterprise-traefik-tls branch August 10, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants