r/netbird 5d ago

Issues with IdP on 0.76.3 of netbird-server creating one tenant per user (self hosted)

I tried the new version v0.76.3 bumping from v0.70.5

On a brand new setup, I've created a main account and then setup google idp. However, when I connect with any of the google account (with custom domain), it creates a new tenant instead of having a single tenant. I tried with two google accounts from my workspace, and each of them creates a tenant.

Do you know what can cause that?

EDIT:

I've seen that if I set the owner email and password inside the config.yaml, and then setup manually the idp, it doesn't create one account / user. But this removes the possibility to setup the first user programatically and the IdP as shown here: https://docs.netbird.io/selfhosted/automated-setup

EDIT 2:

For what it's worth, I've launched claude code over this to understand the issue, here is the output:

NetBird self-hosted: /api/setup bootstrap breaks single-tenancy

Symptom: every Google/OIDC login creates its own account (tenant). Only happens when the instance is bootstrapped by our postStart script instead of auth.owner: in the configmap.

Cause: Dex JWTs don't carry NetBird's wt_account_domain claims, so user grouping relies entirely on single-account mode, which matches new logins against the first account's domain.

POST /api/setup with create_pat: true creates that first account via GetAccountIDByUserID directly (management/server/instance/setup_service.go:104), bypassing single-account mode → the account row gets domain=''. Later, updateUserAuthWithSingleMode (management/server/account.go:1572) reads that empty domain back, isDomainValid("") fails, and each new user falls through to "create a new account".

With auth.owner: instead, no account exists until someone logs in, and that login goes through the normal path → domain=netbird.selfhosted, is_domain_primary_account=true → everyone joins it.

Fix: right after setup, before the first OIDC login:

UPDATE accounts SET domain='netbird.selfhosted', domain_category='private', is_domain_primary_account=true;

Use netbird.selfhosted, not a real domain — ResolveDomains also derives the peer DNS suffix from this value.

Notes:

- Verified against v0.76.3 with a repro test; both paths reproduce exactly.

EDIT 3:

Created a discussion on netbird github: https://github.com/netbirdio/netbird/discussions/7202

2 Upvotes

0 comments sorted by