Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/mdx.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function Note({ children }) {
return (
<div className="my-6 flex gap-2.5 rounded-l border border-orange-500/20 bg-orange-50/50 p-4 leading-6 text-orange-900 dark:border-orange-500/30 dark:bg-orange-500/5 dark:text-orange-200 dark:[--tw-prose-links-hover:theme(colors.orange.300)] dark:[--tw-prose-links:theme(colors.white)]">
<InfoIcon className="mt-1 h-4 w-4 flex-none fill-orange-500 stroke-white dark:fill-orange-200/20 dark:stroke-orange-200" />
<div className="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
<div className="min-w-0 flex-1 [&>:first-child]:mt-0 [&>:last-child]:mb-0">
{children}
</div>
</div>
Expand All @@ -94,7 +94,7 @@ export function Warning({ children }) {
return (
<div className="my-6 flex gap-2.5 rounded-l border border-red-500/20 bg-red-50/50 p-4 leading-6 text-red-900 dark:border-red-500/30 dark:bg-red-500/5 dark:text-red-200 dark:[--tw-prose-links-hover:theme(colors.red.300)] dark:[--tw-prose-links:theme(colors.white)]">
<WarningIcon className="mt-1 h-4 w-4 flex-none fill-red-500 stroke-white dark:fill-red-200/20 dark:stroke-red-200" />
<div className="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
<div className="min-w-0 flex-1 [&>:first-child]:mt-0 [&>:last-child]:mb-0">
{children}
</div>
</div>
Expand All @@ -105,7 +105,7 @@ export function Success({ children }) {
return (
<div className="my-6 flex gap-2.5 rounded-l border border-green-500/20 bg-green-50/50 p-4 leading-6 text-green-900 dark:border-green-500/30 dark:bg-green-500/5 dark:text-green-200 dark:[--tw-prose-links-hover:theme(colors.green.300)] dark:[--tw-prose-links:theme(colors.white)]">
<SuccessIcon className="mt-1 h-4 w-4 flex-none fill-green-500 stroke-white dark:fill-green-200/20 dark:stroke-green-200" />
<div className="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
<div className="min-w-0 flex-1 [&>:first-child]:mt-0 [&>:last-child]:mb-0">
{children}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,48 @@ Before deploying additional proxy instances, make sure you have:
- The ability to update DNS records to point to multiple servers
- Access to the management server CLI to generate proxy tokens

### Prepare the management server for cross-host proxies

Additional proxy instances run on different hosts than the management server, so they cannot reach the management container over the Docker network. Each remote proxy connects to management via gRPC over public TLS (`https://netbird.example.com:443`), which means the management server's Traefik must be configured to route the gRPC streams used by the proxy.

If you have not already done so as part of [Enable Reverse Proxy](/selfhosted/migration/enable-reverse-proxy#connecting-through-traefik-instead-of-docker-network), update the management server now:

1. **Add the `ProxyService` gRPC path** to the existing gRPC router rule on the management host. In a standard deployment this is the `traefik.http.routers.netbird-grpc` label:

```
traefik.http.routers.netbird-grpc.rule=Host(`netbird.example.com`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`) || PathPrefix(`/management.ProxyService/`))
```

<Warning>
Replace the entire rule — don't just append the new `ProxyService` clause to the end of your existing line. The original rule closes its path group with `))`, so pasting `|| PathPrefix(...)` after it pushes the new path *outside* the OR group:

```
...ManagementService/`)) || PathPrefix(`/management.ProxyService/`))
^ this `)` ends the OR group too early
```

The symptom is the same `transport: received unexpected content-type "text/html"` error, which makes it easy to think the fix didn't work. The `ProxyService` path must sit inside the same parentheses as the other two paths.
</Warning>

Without `/management.ProxyService/` in this rule, Traefik falls back to the dashboard router and returns the dashboard HTML. The proxy logs this as `code = Unimplemented ... 404 (Not Found); transport: received unexpected content-type "text/html"`.

2. **Disable the Traefik idle timeout** on the `websecure` entrypoint so long-lived gRPC streams between the proxy and management server are not cut off:

```yaml
# docker-compose.yml on the management host
services:
traefik:
command:
# ...existing args...
- "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0"
```

3. Restart Traefik on the management host: `docker compose up -d traefik`.

<Warning>
Both changes are made on the **management server**, not on the proxy hosts. Skipping them is the most common cause of `management connection failed ... 404 (Not Found)` errors when adding a remote proxy instance.
</Warning>

## Token management

Each proxy instance authenticates with the management server using an access token. You can either generate a unique token per instance or share a single token across instances.
Expand Down Expand Up @@ -114,7 +156,7 @@ Create a `docker-compose.yml` on the new server:
```yaml
services:
traefik:
image: traefik:v3.4
image: traefik:v3.6
container_name: traefik
restart: unless-stopped
command:
Expand Down
Loading