Describe the problem
I'm having a little trouble figuring out how I'm supposed to serve Netbird with PocketID on NixOS. I have it running inside a VM, and have all the enableNginx options enabled. Then, in another VM (acts like a nameserver/proxy) which is running Caddy, I forward all Netbird requests to my Netbird VM, ie; reverse_proxy 192.168.100.5:80.
What happens is that when I access netbird.domain, I see the dashboard load, it forwards to PocketID which asks me to authenticate, then it goes to this URL: https://netbird.domain.tld/auth?code=k21CjtEE4PxYhPgyXcmRlgMgMKmpRPrk&state=8ZWS2F3KCd7eP9Cq&iss=https%3A%2F%2Fpocketid.domain.tld, which leads to a 404 from nginx.
I've tried doing it with enableNginx disabled and serving all the routes myself, but I cannot for the life of me get it to work - how do I even serve the frontend? AFAIK, it's served with the mgmt server, but for some reason, I can't access anything.
In any case, I am 99% sure my Netbird configuration is fine, and that it's just something to do with how I'm forwarding requests. In my Caddyfile, all I do is literally just forward all requests to the Netbird VM:
"netbird.${domain}".extraConfig = ''
reverse_proxy 192.168.100.5:80
'';
Again, my best guess is that ^^^ is forwarding the request to /auth to the wrong place. I've tried more complex ones, like I saw some on closed issues where they forward /api/* to one place, or gRPC requests to another, but (obviously) none of those helped here.
I've also attached my generated management.json file below.
management.json
{
"DataStoreEncryptionKey": "REDACTED_DATASTORE_KEY",
"Datadir": "/var/lib/netbird-mgmt/data",
"DeviceAuthorizationFlow": {
"Provider": "none",
"ProviderConfig": {
"Audience": "netbird",
"ClientID": "netbird",
"DeviceAuthEndpoint": "",
"Domain": null,
"Scope": "openid profile email groups",
"TokenEndpoint": null,
"UseIDToken": true
}
},
"HttpConfig": {
"Address": "127.0.0.1:8011",
"AuthAudience": "REDACTED_AUTH_AUDIENCE",
"IdpSignKeyRefreshEnabled": true,
"OIDCConfigEndpoint": "https://pocketid.domain.tld/.well-known/openid-configuration"
},
"IdpManagerConfig": {
"Auth0ClientCredentials": null,
"AzureClientCredentials": null,
"ClientConfig": {
"ClientID": "netbird",
"ClientSecret": "",
"GrantType": "client_credentials",
"Issuer": "",
"TokenEndpoint": ""
},
"ExtraConfig": {
"ApiToken": "REDACTED_API_TOKEN",
"ManagementEndpoint": "https://pocketid.domain.tld"
},
"KeycloakClientCredentials": null,
"ManagerType": "pocketid",
"ZitadelClientCredentials": null
},
"PKCEAuthorizationFlow": {
"ProviderConfig": {
"Audience": "REDACTED_PKCE_AUDIENCE",
"AuthorizationEndpoint": "",
"ClientID": "REDACTED_PKCE_CLIENT_ID",
"ClientSecret": "",
"RedirectURLs": [
"http://localhost:53000"
],
"Scope": "openid profile email",
"TokenEndpoint": "",
"UseIDToken": false
}
},
"Relay": {
"Addresses": [
"rels://netbird.domain.tld:33080"
],
"CredentialsTTL": "24h",
"Secret": "REDACTED_RELAY_SECRET"
},
"ReverseProxy": {
"TrustedHTTPProxies": [],
"TrustedHTTPProxiesCount": 0,
"TrustedPeers": [
"0.0.0.0/0"
]
},
"Signal": {
"Password": null,
"Proto": "https",
"URI": "netbird.domain.tld:443",
"Username": ""
},
"StoreConfig": {
"Engine": "sqlite"
},
"Stuns": [
{
"Password": null,
"Proto": "udp",
"URI": "stun:netbird.domain.tld:3478",
"Username": ""
}
],
"TURNConfig": {
"CredentialsTTL": "12h",
"Secret": "REDACTED_TURN_SECRET",
"TimeBasedCredentials": false,
"Turns": [
{
"Password": "REDACTED_TURN_PASSWORD",
"Proto": "udp",
"URI": "turn:netbird.domain.tld:3478",
"Username": "netbird"
}
]
}
}
Any help at all is greatly appreciated!
UPDATE: Apparently, no route other than the base route works, is this expected?
Some more logs below:
Details
[default@vpn:~]$ curl -v https://netbird.domain.tld/auth
* Host netbird.domain.tld:443 was resolved.
* IPv6: 2606:4700:3033::6815:3a75, 2606:4700:3036::ac43:9f69
* IPv4: 192.168.100.4
* Trying [2606:4700:3033::6815:3a75]:443...
* Immediate connect fail for 2606:4700:3033::6815:3a75: Network is unreachable
* Trying 192.168.100.4:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
* OpenSSL default paths (fallback)
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
* subject: CN=*.domain.tld
* start date: Dec 5 10:38:30 2025 GMT
* expire date: Mar 5 10:38:29 2026 GMT
* issuer: C=US; O=Let's Encrypt; CN=E8
* Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
* Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption
* Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* subjectAltName: "netbird.domain.tld" matches cert's "*.domain.tld"
* SSL certificate verified via OpenSSL.
* Established connection to netbird.domain.tld (192.168.100.4 port 443) from 192.168.100.5 port 43832
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://netbird.domain.tld/auth
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: netbird.domain.tld]
* [HTTP/2] [1] [:path: /auth]
* [HTTP/2] [1] [user-agent: curl/8.17.0]
* [HTTP/2] [1] [accept: */*]
> GET /auth HTTP/2
> Host: netbird.domain.tld
> User-Agent: curl/8.17.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Request completely sent off
< HTTP/2 404
< alt-svc: h3=":443"; ma=2592000
< content-type: text/html
< date: Mon, 22 Dec 2025 01:44:30 GMT
< server: nginx
< via: 1.1 Caddy
< content-length: 146
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host netbird.domain.tld:443 left intact
[default@vpn:~]$ curl -v http://192.168.100.5:80/auth
* Trying 192.168.100.5:80...
* Established connection to 192.168.100.5 (192.168.100.5 port 80) from 192.168.100.5 port 58326
* using HTTP/1.x
> GET /auth HTTP/1.1
> Host: 192.168.100.5
> User-Agent: curl/8.17.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 404 Not Found
< Server: nginx
< Date: Mon, 22 Dec 2025 01:44:50 GMT
< Content-Type: text/html
< Content-Length: 146
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 192.168.100.5:80 left intact
Expected behavior
I assume this should redirect to the Netbird dashboard?
Are you using NetBird Cloud?
No, I'm self hosting
NetBird version
I'm using an overlay while I wait for my PR.
Is any other VPN software installed?
Nope.
Have you tried these troubleshooting steps?
Describe the problem
I'm having a little trouble figuring out how I'm supposed to serve Netbird with PocketID on NixOS. I have it running inside a VM, and have all the
enableNginxoptions enabled. Then, in another VM (acts like a nameserver/proxy) which is running Caddy, I forward all Netbird requests to my Netbird VM, ie;reverse_proxy 192.168.100.5:80.What happens is that when I access
netbird.domain, I see the dashboard load, it forwards to PocketID which asks me to authenticate, then it goes to this URL:https://netbird.domain.tld/auth?code=k21CjtEE4PxYhPgyXcmRlgMgMKmpRPrk&state=8ZWS2F3KCd7eP9Cq&iss=https%3A%2F%2Fpocketid.domain.tld, which leads to a 404 from nginx.I've tried doing it with
enableNginxdisabled and serving all the routes myself, but I cannot for the life of me get it to work - how do I even serve the frontend? AFAIK, it's served with the mgmt server, but for some reason, I can't access anything.In any case, I am 99% sure my Netbird configuration is fine, and that it's just something to do with how I'm forwarding requests. In my Caddyfile, all I do is literally just forward all requests to the Netbird VM:
Again, my best guess is that ^^^ is forwarding the request to
/authto the wrong place. I've tried more complex ones, like I saw some on closed issues where they forward/api/*to one place, or gRPC requests to another, but (obviously) none of those helped here.I've also attached my generated
management.jsonfile below.management.json
{ "DataStoreEncryptionKey": "REDACTED_DATASTORE_KEY", "Datadir": "/var/lib/netbird-mgmt/data", "DeviceAuthorizationFlow": { "Provider": "none", "ProviderConfig": { "Audience": "netbird", "ClientID": "netbird", "DeviceAuthEndpoint": "", "Domain": null, "Scope": "openid profile email groups", "TokenEndpoint": null, "UseIDToken": true } }, "HttpConfig": { "Address": "127.0.0.1:8011", "AuthAudience": "REDACTED_AUTH_AUDIENCE", "IdpSignKeyRefreshEnabled": true, "OIDCConfigEndpoint": "https://pocketid.domain.tld/.well-known/openid-configuration" }, "IdpManagerConfig": { "Auth0ClientCredentials": null, "AzureClientCredentials": null, "ClientConfig": { "ClientID": "netbird", "ClientSecret": "", "GrantType": "client_credentials", "Issuer": "", "TokenEndpoint": "" }, "ExtraConfig": { "ApiToken": "REDACTED_API_TOKEN", "ManagementEndpoint": "https://pocketid.domain.tld" }, "KeycloakClientCredentials": null, "ManagerType": "pocketid", "ZitadelClientCredentials": null }, "PKCEAuthorizationFlow": { "ProviderConfig": { "Audience": "REDACTED_PKCE_AUDIENCE", "AuthorizationEndpoint": "", "ClientID": "REDACTED_PKCE_CLIENT_ID", "ClientSecret": "", "RedirectURLs": [ "http://localhost:53000" ], "Scope": "openid profile email", "TokenEndpoint": "", "UseIDToken": false } }, "Relay": { "Addresses": [ "rels://netbird.domain.tld:33080" ], "CredentialsTTL": "24h", "Secret": "REDACTED_RELAY_SECRET" }, "ReverseProxy": { "TrustedHTTPProxies": [], "TrustedHTTPProxiesCount": 0, "TrustedPeers": [ "0.0.0.0/0" ] }, "Signal": { "Password": null, "Proto": "https", "URI": "netbird.domain.tld:443", "Username": "" }, "StoreConfig": { "Engine": "sqlite" }, "Stuns": [ { "Password": null, "Proto": "udp", "URI": "stun:netbird.domain.tld:3478", "Username": "" } ], "TURNConfig": { "CredentialsTTL": "12h", "Secret": "REDACTED_TURN_SECRET", "TimeBasedCredentials": false, "Turns": [ { "Password": "REDACTED_TURN_PASSWORD", "Proto": "udp", "URI": "turn:netbird.domain.tld:3478", "Username": "netbird" } ] } }Any help at all is greatly appreciated!
UPDATE: Apparently, no route other than the base route works, is this expected?
Some more logs below:
Details
Expected behavior
I assume this should redirect to the Netbird dashboard?
Are you using NetBird Cloud?
No, I'm self hosting
NetBird version
I'm using an overlay while I wait for my PR.
Is any other VPN software installed?
Nope.
Have you tried these troubleshooting steps?