[GH-ISSUE #1953] Caddy Reverse Proxy Config #3838

Closed
opened 2026-08-05 00:54:35 -04:00 by saavagebueno · 9 comments
Owner

Originally created by @pomology on GitHub (May 9, 2024).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/1953

Is there a Caddy reverse-proxy template available? It would be extremely helpful to have one. Thank you!

Originally created by @pomology on GitHub (May 9, 2024). Original GitHub issue: https://github.com/netbirdio/netbird/issues/1953 Is there a Caddy reverse-proxy template available? It would be extremely helpful to have one. Thank you!
saavagebueno added the waiting-feedbacktriage-needed labels 2026-08-05 00:54:35 -04:00
Author
Owner

@CrimsonFez commented on GitHub (May 10, 2024):

there isn't. However you can look in infrastructure_files/getting-started-with-zitadel.sh for an example.

I've modified it for my own usage:

netbird.example.net {
	reverse_proxy /* netbird-dash:80
	reverse_proxy /signalexchange.SignalExchange/* h2c://netbird-signal
	reverse_proxy /api/* netbird-mgmt
	reverse_proxy /management.ManagementService/* h2c://netbird-mgmt
	header * {
		Strict-Transport-Security "max-age=3600; includeSubDomains; preload"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "DENY"
		X-XSS-Protection "1; mode=block"
		-Server
		Referrer-Policy strict-origin-when-cross-origin
	}
}
<!-- gh-comment-id:2103852119 --> @CrimsonFez commented on GitHub (May 10, 2024): there isn't. However you can look in [infrastructure_files/getting-started-with-zitadel.sh](https://github.com/netbirdio/netbird/blob/main/infrastructure_files/getting-started-with-zitadel.sh) for an example. I've modified it for my own usage: ``` netbird.example.net { reverse_proxy /* netbird-dash:80 reverse_proxy /signalexchange.SignalExchange/* h2c://netbird-signal reverse_proxy /api/* netbird-mgmt reverse_proxy /management.ManagementService/* h2c://netbird-mgmt header * { Strict-Transport-Security "max-age=3600; includeSubDomains; preload" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" X-XSS-Protection "1; mode=block" -Server Referrer-Policy strict-origin-when-cross-origin } } ```
Author
Owner

@pomology commented on GitHub (May 10, 2024):

Thank you so much for pointing out that helpful source!

So then, the docker-compose.yml file would need this section added under "services":

  caddy:
    image: caddy
    restart: unless-stopped
    networks: [ netbird ]
    ports:
      - '443:443'
      - '80:80'
    volumes:
      - netbird_caddy_data:/data
      - ./Caddyfile:/etc/caddy/Caddyfile

And under "volumes" add:

netbird_caddy_data:

Besides that, are there any other config changes you made for caddy to work?

Thank you so much for your help! I really appreciate your input.

<!-- gh-comment-id:2104455153 --> @pomology commented on GitHub (May 10, 2024): Thank you so much for pointing out that helpful source! So then, the docker-compose.yml file would need this section added under "services": ``` caddy: image: caddy restart: unless-stopped networks: [ netbird ] ports: - '443:443' - '80:80' volumes: - netbird_caddy_data:/data - ./Caddyfile:/etc/caddy/Caddyfile ``` And under "volumes" add: ` netbird_caddy_data:` Besides that, are there any other config changes you made for caddy to work? Thank you so much for your help! I really appreciate your input.
Author
Owner

@sfnemis commented on GitHub (May 12, 2024):

Thank you so much for pointing out that helpful source!

So then, the docker-compose.yml file would need this section added under "services":

  caddy:
    image: caddy
    restart: unless-stopped
    networks: [ netbird ]
    ports:
      - '443:443'
      - '80:80'
    volumes:
      - netbird_caddy_data:/data
      - ./Caddyfile:/etc/caddy/Caddyfile

And under "volumes" add:

netbird_caddy_data:

Besides that, are there any other config changes you made for caddy to work?

Thank you so much for your help! I really appreciate your input.

Is this conf working with caddy ?

<!-- gh-comment-id:2106402431 --> @sfnemis commented on GitHub (May 12, 2024): > Thank you so much for pointing out that helpful source! > > So then, the docker-compose.yml file would need this section added under "services": > > ``` > caddy: > image: caddy > restart: unless-stopped > networks: [ netbird ] > ports: > - '443:443' > - '80:80' > volumes: > - netbird_caddy_data:/data > - ./Caddyfile:/etc/caddy/Caddyfile > ``` > > And under "volumes" add: > > ` netbird_caddy_data:` > > Besides that, are there any other config changes you made for caddy to work? > > Thank you so much for your help! I really appreciate your input. Is this conf working with caddy ?
Author
Owner

@pomology commented on GitHub (May 12, 2024):

@sfnemis As far as the Compose file, yes that works with Caddy. I've pasted my sanitized Compose file below. I also pasted the CaddyFile config I'm using. Everything is working with this setup, except that all my peers are getting an identical private IP set as their "Public IP" (like 10.89.1.53). I'm not sure why, and I have an open bug submission here on it. Anyway, here's what I've got so far! I'll post again when we get the IP issue figured out.

Here is my Caddyfile, from NetBird's default configs:

{
  debug
	servers :80,:443 {
    protocols h1 h2c
  }
}

(security_headers) {
    header * {
     
        Strict-Transport-Security "max-age=3600; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        X-XSS-Protection "1; mode=block"
        -Server
        Referrer-Policy strict-origin-when-cross-origin
    }
}

:80, REDACTED.REDACTED.com:443 {
    import security_headers
    reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
    reverse_proxy /api/* management:80
    reverse_proxy /management.ManagementService/* h2c://management:80
    reverse_proxy /* dashboard:80
}


And here's my compose.yml, again just built on NetBird's default:

version: "3"
services:
 # Caddy reverse proxy
  caddy:
    image: caddy:latest
    restart: unless-stopped
    networks:
      - netbird
    ports:
      - '443:443'
      - '80:80'
    volumes:
      - netbird_caddy_data:/data
      - ./Caddyfile:/etc/caddy/Caddyfile

  #UI dashboard
  dashboard:
    image: netbirdio/dashboard:latest
    restart: unless-stopped
    networks:
      - netbird
    environment:
      # Endpoints
      - NETBIRD_MGMT_API_ENDPOINT=https://REDACTED.REDACTED.com:443
      - NETBIRD_MGMT_GRPC_API_ENDPOINT=https://REDACTED.REDACTED.com:443
      # OIDC
      - AUTH_AUDIENCE=REDACTED
      - AUTH_CLIENT_ID=REDACTED
      - AUTH_CLIENT_SECRET=
      - AUTH_AUTHORITY=https://REDACTED.okta.com
      - USE_AUTH0=false
      - AUTH_SUPPORTED_SCOPES=openid profile email
      - AUTH_REDIRECT_URI=/auth
      - AUTH_SILENT_REDIRECT_URI=/silent-auth
      - NETBIRD_TOKEN_SOURCE=idToken

  # Signal
  signal:
    image: netbirdio/signal:latest
    restart: unless-stopped
    networks:
      - netbird
   

  # Management
  management:
    image: netbirdio/management:latest
    restart: unless-stopped
    networks:
      - netbird
    depends_on:
      - dashboard
    volumes:
      - netbird-mgmt:/var/lib/netbird
      - ./management.json:/etc/netbird/management.json
    command: [
      "--port", "80",
      "--log-file", "console",
      "--log-level", "info",
      "--disable-anonymous-metrics=false",
      "--single-account-mode-domain=REDACTED.REDACTED.com",
      "--dns-domain=netbird.selfhosted",
      "--idp-sign-key-refresh-enabled"
]

  # Coturn
  coturn:
    image: coturn/coturn:latest
    restart: unless-stopped
    domainname: REDACTED.REDACTED.com
    volumes:
      - ./turnserver.conf:/etc/turnserver.conf:ro
    network_mode: host
    command:
      - -c /etc/turnserver.conf

volumes:
  netbird-mgmt:
  netbird_caddy_data:

networks:
  netbird:

<!-- gh-comment-id:2106551100 --> @pomology commented on GitHub (May 12, 2024): @sfnemis As far as the Compose file, yes that works with Caddy. I've pasted my sanitized Compose file below. I also pasted the CaddyFile config I'm using. Everything is working with this setup, except that all my peers are getting an identical private IP set as their "Public IP" (like 10.89.1.53). I'm not sure why, and I have an open bug submission here on it. Anyway, here's what I've got so far! I'll post again when we get the IP issue figured out. Here is my Caddyfile, from NetBird's default configs: ``` { debug servers :80,:443 { protocols h1 h2c } } (security_headers) { header * { Strict-Transport-Security "max-age=3600; includeSubDomains; preload" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" X-XSS-Protection "1; mode=block" -Server Referrer-Policy strict-origin-when-cross-origin } } :80, REDACTED.REDACTED.com:443 { import security_headers reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000 reverse_proxy /api/* management:80 reverse_proxy /management.ManagementService/* h2c://management:80 reverse_proxy /* dashboard:80 } ``` And here's my compose.yml, again just built on NetBird's default: ``` version: "3" services: # Caddy reverse proxy caddy: image: caddy:latest restart: unless-stopped networks: - netbird ports: - '443:443' - '80:80' volumes: - netbird_caddy_data:/data - ./Caddyfile:/etc/caddy/Caddyfile #UI dashboard dashboard: image: netbirdio/dashboard:latest restart: unless-stopped networks: - netbird environment: # Endpoints - NETBIRD_MGMT_API_ENDPOINT=https://REDACTED.REDACTED.com:443 - NETBIRD_MGMT_GRPC_API_ENDPOINT=https://REDACTED.REDACTED.com:443 # OIDC - AUTH_AUDIENCE=REDACTED - AUTH_CLIENT_ID=REDACTED - AUTH_CLIENT_SECRET= - AUTH_AUTHORITY=https://REDACTED.okta.com - USE_AUTH0=false - AUTH_SUPPORTED_SCOPES=openid profile email - AUTH_REDIRECT_URI=/auth - AUTH_SILENT_REDIRECT_URI=/silent-auth - NETBIRD_TOKEN_SOURCE=idToken # Signal signal: image: netbirdio/signal:latest restart: unless-stopped networks: - netbird # Management management: image: netbirdio/management:latest restart: unless-stopped networks: - netbird depends_on: - dashboard volumes: - netbird-mgmt:/var/lib/netbird - ./management.json:/etc/netbird/management.json command: [ "--port", "80", "--log-file", "console", "--log-level", "info", "--disable-anonymous-metrics=false", "--single-account-mode-domain=REDACTED.REDACTED.com", "--dns-domain=netbird.selfhosted", "--idp-sign-key-refresh-enabled" ] # Coturn coturn: image: coturn/coturn:latest restart: unless-stopped domainname: REDACTED.REDACTED.com volumes: - ./turnserver.conf:/etc/turnserver.conf:ro network_mode: host command: - -c /etc/turnserver.conf volumes: netbird-mgmt: netbird_caddy_data: networks: netbird: ```
Author
Owner

@ndziuba commented on GitHub (May 15, 2024):

Maybe it helps somebody
Using Azure AD as IdP and the Safari Browser i got the error
AADSTS50011: The reply url specified in the request does not match the reply
because Safari calles /auth using http.
I added a rule catching all http requests and redirecting /auth to https manually and
also needed to do the same for the dashboard otherwise the screen would just be blank.

http://<YOUR_DOMAIN> {
	@http protocol http

	handle_path /auth {
		redir @http https://{host}{uri} html
	}

	handle_path / {
		redir @http https://{host}{uri}
	}
}

:80, :443 {
	import security_headers
	reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
	reverse_proxy /api/* management:80
	reverse_proxy /management.ManagementService/* h2c://management:80
	reverse_proxy /* dashboard:80
}
<!-- gh-comment-id:2112348733 --> @ndziuba commented on GitHub (May 15, 2024): Maybe it helps somebody Using Azure AD as IdP and the Safari Browser i got the error AADSTS50011: The reply url specified in the request does not match the reply because Safari calles /auth using http. I added a rule catching all http requests and redirecting /auth to https manually and also needed to do the same for the dashboard otherwise the screen would just be blank. ``` http://<YOUR_DOMAIN> { @http protocol http handle_path /auth { redir @http https://{host}{uri} html } handle_path / { redir @http https://{host}{uri} } } :80, :443 { import security_headers reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000 reverse_proxy /api/* management:80 reverse_proxy /management.ManagementService/* h2c://management:80 reverse_proxy /* dashboard:80 } ```
Author
Owner

@PapaZigE commented on GitHub (Oct 5, 2024):

@pomology, I've been banging my head against this for some time now. I tried to replicate what you did but to no avail. I get either a blank screen or the dashboard is hung on loading... Did you have to change any ports in the setup.env or base.setup.env files? I have a feeling the settings in there are conflicting with the docker-compose.yml and Caddyfile. Also, I don't see anything in your files referencing the relay service... are you not using it ?

Any help is greatly appreciated, thanks !!!

<!-- gh-comment-id:2395252742 --> @PapaZigE commented on GitHub (Oct 5, 2024): @pomology, I've been banging my head against this for some time now. I tried to replicate what you did but to no avail. I get either a blank screen or the dashboard is hung on loading... Did you have to change any ports in the setup.env or base.setup.env files? I have a feeling the settings in there are conflicting with the docker-compose.yml and Caddyfile. Also, I don't see anything in your files referencing the relay service... are you not using it ? Any help is greatly appreciated, thanks !!!
Author
Owner

@pomology commented on GitHub (Oct 5, 2024):

@PapaZigE
The issue I was having was that I was trying to run this in rootless docker which requires a lot of extra config to make work. As long as you run SELinux, there is dubious benefit to rootless docker in this case, as long as you don't have other containers running other services on the machine.

What IDP are you using? This setup above uses Okta, if you are using Zitadel, the install works out of the box on rootfull Docker. For an Okta setup, there are also additional changes needed in the Management.json file, beyond anything in the setup.env config.

You can SANITIZE your code and upload it to ChatGPT for feedback as well. Just upload your sanitized management.json, caddyfile, and compose.yml and ask for feedback. It works amazingly well.

<!-- gh-comment-id:2395280344 --> @pomology commented on GitHub (Oct 5, 2024): @PapaZigE The issue I was having was that I was trying to run this in rootless docker which requires a lot of extra config to make work. As long as you run SELinux, there is dubious benefit to rootless docker in this case, as long as you don't have other containers running other services on the machine. What IDP are you using? This setup above uses Okta, if you are using Zitadel, the install works out of the box on rootfull Docker. For an Okta setup, there are also additional changes needed in the Management.json file, beyond anything in the setup.env config. You can SANITIZE your code and upload it to ChatGPT for feedback as well. Just upload your sanitized management.json, caddyfile, and compose.yml and ask for feedback. It works amazingly well.
Author
Owner

@nazarewk commented on GitHub (Apr 28, 2025):

Hello @pomology,

We're currently reviewing our open issues and would like to verify if this problem still exists in the latest NetBird version.

Could you please confirm if the issue is still there?

We may close this issue temporarily if we don't hear back from you within 2 weeks, but feel free to reopen it with updated information.

Thanks for your contribution to improving the project!

<!-- gh-comment-id:2835663239 --> @nazarewk commented on GitHub (Apr 28, 2025): Hello @pomology, We're currently reviewing our open issues and would like to verify if this problem still exists in the [latest NetBird version](https://github.com/netbirdio/netbird/releases). Could you please confirm if the issue is still there? We may close this issue temporarily if we don't hear back from you within **2 weeks**, but feel free to reopen it with updated information. Thanks for your contribution to improving the project!
Author
Owner

@mlsmaycon commented on GitHub (Jun 1, 2025):

closing issue due to no recent feedback. Feel free to open a new one if the issue persist or reopen if this was a feature request.

<!-- gh-comment-id:2927892779 --> @mlsmaycon commented on GitHub (Jun 1, 2025): closing issue due to no recent feedback. Feel free to open a new one if the issue persist or reopen if this was a feature request.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#3838