Local User Management
NetBird's Management service includes built-in user management, allowing you to create and manage local users directly without requiring an external identity provider. This functionality is powered by an embedded Dex server.
Starting with version 0.62, NetBird no longer requires an external identity provider. The Management service now supports creating and managing local users directly, so you can get started without setting up Zitadel, Keycloak, or any other IdP.
With local user management, you can:
- Create local users directly from the NetBird Dashboard
- Add external identity providers (Google, Microsoft, Okta, etc.) through the Dashboard UI
- Configure multiple IdPs simultaneously, users see all providers as login options
- Simplify your deployment with fewer containers and reduced resource requirements
- Get started faster with no additional IdP setup required
Local user management is powered by an embedded Dex server running within the NetBird Management service, requiring no additional containers or databases.
When to Use Local Users
Local user management is ideal for:
| Use Case | Why Local Users Work |
|---|---|
| Homelabs | Simple setup, minimal resources, no external dependencies |
| Small teams | Easy user management, quick onboarding |
| Proof of concept | Get started in minutes, upgrade path available |
| Air-gapped environments | No external service dependencies |
| Development/testing | Fast iteration, simple reset |
Consider a standalone external IdP if you need:
- SCIM user provisioning (Enterprise feature)
- Complex user lifecycle management
- Integration with existing enterprise SSO infrastructure
- Specific IdP features not available via OIDC connectors
Configuration
Enabling Embedded IdP
The embedded IdP is enabled by default when using the new getting-started.sh quickstart script. For manual configuration, update your management.json:
{
"EmbeddedIdP": {
"Enabled": true,
"DataDir": "/var/lib/netbird/idp"
},
"EncryptionKey": "<auto-generated-base64-key>"
}
Configuration Options
| Option | Description | Default |
|---|---|---|
EmbeddedIdP.Enabled | Enable/disable the embedded IdP | true (quickstart) |
EmbeddedIdP.DataDir | Directory for IdP data storage | /var/lib/netbird/idp |
EncryptionKey | Base64-encoded AES-256 key for encrypting user data | Auto-generated |
Environment Variables
When using docker-compose, you can configure these via environment variables:
environment:
- NETBIRD_EMBEDDED_IDP_ENABLED=true
- NETBIRD_EMBEDDED_IDP_DATA_DIR=/var/lib/netbird/idp
- NETBIRD_ENCRYPTION_KEY=${ENCRYPTION_KEY}
Generating an Encryption Key
If you need to generate an encryption key manually:
openssl rand -base64 32
Store your encryption key securely. If lost, encrypted user data (emails, names) cannot be recovered. Include it in your backup procedures.
User Management
Creating Users via Dashboard
When embedded IdP is enabled, the Dashboard shows a "Create User" button (instead of "Invite User" shown for cloud-hosted NetBird):
- Navigate to Team → Users
- Click Create User
- Fill in the user details:
- Email (required) - User's email address for login
- Name (required) - Display name
- Groups (optional) - Auto-assign to groups
- Click Create
After creation, a modal displays with:
- The generated password with a copy button
- Warning: "This password will only be shown once. Please copy it now."
- Copy & Close button to copy password and dismiss
The generated password is only shown once at creation time. It cannot be retrieved later. Make sure to copy it and share it securely with the user.
User IdP Badges
In the Users table, each user shows a badge indicating their identity provider:
- Users created locally show no badge (or "Local" badge)
- Users who authenticated via an external connector show that provider's icon (Google, Microsoft, etc.)
- The badge links to the
idp_idfield in the user record
Creating Users via API
curl -X POST "https://netbird.example.com/api/users" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"name": "New User",
"auto_groups": ["group-id-1"]
}'
Response includes the generated password:
{
"id": "user-abc123",
"email": "user@example.com",
"name": "New User",
"role": "user",
"status": "active",
"password": "generated-password-here"
}
The password field is only included when creating users with embedded IdP. Store it immediately—it won't be returned in subsequent API calls.
User Roles
Users created through the embedded IdP can be assigned roles:
| Role | Permissions |
|---|---|
| Owner | Full administrative access, cannot be demoted |
| Admin | Manage users, peers, policies, and settings |
| User | Connect devices, view assigned resources |
Instance Setup (First Run)
When NetBird starts with the embedded IdP and no existing accounts, the Dashboard redirects to the /setup route and displays the Instance Setup Wizard:
- The Dashboard checks
GET /instanceforsetup_required: true - If setup is required, users are redirected to
/setup - The wizard collects:
- Email address (required)
- Password (required, minimum 8 characters)
- Name (optional)
- On submit, the owner account is created via
POST /instance/setup - User is redirected to login with the credentials they just created
The /setup route is unauthenticated and only accessible when setup_required is true. Once setup is complete, accessing /setup returns a 412 error and redirects to login.
Setup API
For automated deployments, you can complete setup via API:
# Check if setup is required
curl "https://netbird.example.com/api/instance"
# Response when setup is needed:
{
"setup_required": true
}
# Complete setup
curl -X POST "https://netbird.example.com/api/instance/setup" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "securepassword123",
"name": "Admin User"
}'
# Response:
{
"user_id": "user-abc123",
"account_id": "account-xyz789"
}
Data Encryption
The embedded IdP encrypts sensitive user data at rest:
| Field | Encryption |
|---|---|
| AES-256-GCM | |
| Name | AES-256-GCM |
| Password | bcrypt hash (via Dex) |
The encryption key is configured in management.json and should be:
- Generated using a cryptographically secure random generator
- Stored securely (not in version control)
- Included in backup procedures
- Rotated according to your security policies
Security Considerations
Password Requirements
Default password requirements for local users:
- Minimum 8 characters
- No specific complexity requirements (consider your security policy)
Session Management
- JWT tokens are issued upon successful authentication
- Token expiration follows OIDC best practices
- Device authorization flow available for CLI clients
Audit Logging
User authentication events are logged in the activity log:
- Login attempts (successful and failed)
- User creation/deletion
- Connector configuration changes
Troubleshooting
"Embedded IdP not available" error
Ensure EmbeddedIdP.Enabled is true in management.json and the Management service has been restarted.
Users can't log in
- Check Management service logs:
docker compose logs management - Verify the encryption key hasn't changed
- Confirm the user exists: Check Team → Users in Dashboard
Comparison with External IdP
| Feature | Embedded IdP | External IdP |
|---|---|---|
| Setup complexity | Minimal | Moderate to High |
| Resource requirements | Low (~1GB RAM) | Higher (2-4GB+ RAM) |
| Additional containers | None | IdP + Database |
| User management | Dashboard/API | External IdP console |
| External SSO | Via connectors | Native |
| SCIM provisioning | Not available | Available (Enterprise) |
| MFA | Via external connectors | Native IdP feature |
| Backup complexity | Single database | Multiple databases |
Disabling Embedded IdP
To switch from embedded IdP to an external IdP:
-
Configure your external IdP following the Advanced guide
-
Update
management.json:{ "EmbeddedIdP": { "Enabled": false }, "HttpConfig": { "OIDCConfigEndpoint": "https://your-idp.example.com/.well-known/openid-configuration" } } -
Restart the Management service
-
Users will need to re-authenticate with the new IdP
Disabling the embedded IdP will invalidate all local user accounts. Ensure users have accounts in the external IdP before switching.

