Expose from CLI

The netbird expose command lets peers expose local HTTP services to the public internet through the NetBird reverse proxy without using the dashboard. Services created this way are ephemeral — they exist only while the command is running and are automatically removed when you stop the command or the session expires.

This is useful for quick demos, temporary access to development servers, webhook receivers, or sharing local work with teammates.

Prerequisites

Before using netbird expose, make sure:

  • The NetBird client is connected — run netbird up first.
  • The Peer Expose feature is enabled by your account administrator (see Enable peer expose below).
  • If peer group restrictions are configured, your peer must be in one of the allowed groups.

Enable peer expose

An account administrator must enable peer expose before any peer can use the netbird expose command.

From the dashboard

  1. Navigate to Settings > Clients in the NetBird dashboard.
  2. Scroll to the Peer Expose section.
  3. Toggle Enable Peer Expose on.
  4. Select specific peer groups that are allowed to expose services. Groups without proper permission with get a PermissionDenied error.
  5. Click Save Changes.

From the API

Set peer_expose_enabled to true in the account settings:

curl -X PUT "https://api.netbird.io/api/accounts/{account_id}" \
  -H "Authorization: Token {api_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "peer_expose_enabled": true,
      "peer_expose_groups": ["ch8i4ug6lnn4g9hqv7m0"]
    }
  }'

Set peer_expose_groups to an array of peer group IDs to restrict which peers can expose services.

Basic usage

Expose a local HTTP server running on a specific port:

netbird expose 8080

On success, the command outputs the public URL and keeps the session alive:

Service exposed successfully!
  Name:     myapp-a1b2c3
  URL:      https://myapp-a1b2c3.proxy.example.com
  Domain:   myapp-a1b2c3.proxy.example.com
  Protocol: http
  Port:     8080

Press Ctrl+C to stop exposing.

The service is now accessible at the displayed URL. Press Ctrl+C to stop exposing and remove the service.

Command reference

netbird expose <port> [flags]

Arguments

ArgumentRequiredDescription
portYesThe local port number to expose (1–65535)

Flags

FlagTypeDefaultDescription
--protocolstringhttpProtocol to use: http or https (e.g. --protocol http).
--with-pinstringProtect the exposed service with a 6-digit PIN (e.g. --with-pin 123456). Must be exactly 6 digits.
--with-passwordstringProtect the exposed service with a password (e.g. --with-password my-secret). Cannot be empty.
--with-user-groupsstringsRestrict access to specific user groups via SSO (e.g. --with-user-groups devops,Backend). Cannot be empty.
--with-custom-domainstringCustom domain for the exposed service, must be configured to your account (e.g. --with-custom-domain myapp.example.com).
--with-name-prefixstringPrefix for the generated service name (e.g. --with-name-prefix my-app).

Authentication

By default, exposed services are publicly accessible to anyone who knows the URL. You can protect them using one or more authentication methods.

PIN protection

Protect the service with a 6-digit PIN that users must enter before accessing it:

netbird expose 8080 --with-pin 123456

Password protection

Protect the service with a password:

netbird expose 8080 --with-password mysecretpassword

SSO user group restriction

Restrict access to users in specific groups from your identity provider:

netbird expose 8080 --with-user-groups engineering,devops

Users must authenticate through your configured IdP (OIDC) and belong to one of the specified groups to access the service.

Combining authentication methods

You can combine multiple authentication methods. When multiple methods are enabled, users can choose which one to use:

netbird expose 8080 --with-pin 123456 --with-password backup-password --with-user-groups admin

For more details on how each authentication method works, see Reverse Proxy Authentication.

Custom domains

By default, netbird expose generates a random subdomain on your proxy cluster domain (e.g., a1b2c3d4e5f6.proxy.example.com). You can customize this in two ways.

Name prefix

Use --with-name-prefix to add a readable prefix to the generated subdomain:

netbird expose 8080 --with-name-prefix myapp

This produces a domain like myapp-a1b2c3.proxy.example.com instead of a fully random one.

The prefix must follow these rules:

  • Lowercase letters, numbers, and hyphens only
  • Must start and end with a letter or number
  • 1 to 32 characters long

Custom domain

Use --with-custom-domain to expose the service on a domain you own:

netbird expose 8080 --with-custom-domain app.example.com

Session lifecycle

Understanding how expose sessions work helps you manage them effectively and troubleshoot issues.

How sessions work

When you run netbird expose, the following happens:

  1. The CLI sends the request to the local NetBird daemon.
  2. The daemon connects to the management server and creates an ephemeral reverse proxy service.
  3. The management server provisions a TLS certificate and domain for the service.
  4. The daemon receives the public URL and displays it to you.
  5. The daemon enters a renewal loop, sending a keep-alive signal to the management server every 30 seconds.
  6. The management server maintains an in-memory session with a 90-second TTL that resets on each renewal.

Stopping a session

There are several ways a session can end:

TriggerWhat happensService removed?
Ctrl+C or SIGTERMThe daemon sends a stop request to the management server within a 5-second timeout.Yes, immediately
Renewal failureThe daemon cannot reach the management server to renew. The CLI exits with an error. The server-side session expires after the TTL.Yes, after 90 seconds
Client disconnectsThe daemon process is killed (e.g., kill -9, system crash). No stop request is sent.Yes, after 90 seconds
Session TTL expiresThe management server's reaper removes sessions that haven't been renewed within 90 seconds.Yes, by the reaper
Management server restartIn-memory sessions are lost.Yes, immediately on restart

What causes expiration

A session expires when the management server does not receive a renewal within 90 seconds. Common causes:

  • Network interruption between the peer and the management server.
  • Client process killed without graceful shutdown (e.g., kill -9, OOM kill, system reboot).
  • Daemon restarted while an expose session is active.
  • Management server restarted — all in-memory sessions are cleared.

When a session expires, the management server automatically deletes the ephemeral service and logs a "Peer expose expired" activity event.

Rate limits

Each peer can have a maximum of 10 active expose sessions at the same time. If you need to expose more services simultaneously, either stop an existing session first or use the dashboard to create permanent services.

Attempting to exceed this limit returns an error:

peer has reached the maximum number of active expose sessions (10)

Permissions

The netbird expose command requires permission at two levels:

  1. Account level — The Peer Expose feature must be enabled in account settings.
  2. Group level — If peer_expose_groups is configured, the peer must belong to at least one of the specified groups.

If the peer does not have permission, the command returns a "permission denied" error.

Comparison with dashboard services

PropertyDashboard servicesCLI expose services
Created viaDashboard or APInetbird expose command
LifecyclePermanent until manually deletedEphemeral — removed when command exits or session expires
TTLNone90 seconds, auto-renewed every 30 seconds
Multiple targetsYes (path-based routing)Single target (the local port)
Custom domainsYesYes (if pre-configured)
AuthenticationSSO, Password, PINSSO (user groups), Password, PIN
Advanced settingsHost header, redirect rewritingNot available
VisibilityAlways shown in dashboardShown while active
Rate limitNoneMax 10 per peer
Source labelPermanentEphemeral

Activity tracking

All peer expose actions are recorded in the activity log and visible in the dashboard under Events > Audit:

EventDescription
Peer exposed serviceA peer created an expose session. Includes peer name, domain, and whether authentication is enabled.
Peer unexposed serviceA peer stopped an expose session (Ctrl+C or explicit stop).
Peer expose expiredAn expose session was removed because the renewal TTL expired.

Troubleshooting

"client is not running, run 'netbird up' first"

The NetBird daemon is not connected. Start it with:

netbird up

"permission denied"

Either peer expose is disabled in account settings, or your peer is not in an allowed group. Ask your account administrator to:

  1. Enable Peer Expose in Settings > Clients.
  2. Add your peer's group to the allowed peer groups (or leave the group list empty to allow all peers).

"peer has reached the maximum number of active expose sessions (10)"

You have 10 active expose sessions on this peer. Stop one of them before creating a new one. Check for stale sessions that may not have been properly cleaned up — they will expire within 90 seconds.

"generate service name: invalid name prefix"

The --with-name-prefix value contains invalid characters. The prefix must be:

  • Lowercase letters (a-z), numbers (0-9), and hyphens (-) only
  • Must start and end with a letter or number
  • 1 to 32 characters long

"unsupported protocol"

The --protocol flag only accepts http or https. Other protocols (e.g. TCP, UDP) are not yet supported for peer expose.

Service URL returns connection error after Ctrl+C

This is expected. The service is removed when you press Ctrl+C. The domain will stop resolving shortly after. If the TLS certificate was cached by your browser, you may see a brief connection error before the domain fully propagates.

Service still accessible after client crash

If the client disconnects without sending a stop request (e.g., process killed, network failure), the service remains active for up to 90 seconds until the session TTL expires. This is by design to tolerate brief network interruptions without interrupting active users.

Examples

Quick demo server

Expose a local development server for a quick demo:

# Start your dev server
npm run dev  # Runs on port 3000

# In another terminal, expose it
netbird expose 3000 --with-name-prefix demo

Protected staging environment

Expose a staging server with password protection:

netbird expose 8080 --with-password staging-review --with-name-prefix staging

Team-only access

Expose a service restricted to your engineering team via SSO:

netbird expose 8080 --with-user-groups engineering --with-name-prefix internal-api

Webhook receiver

Temporarily expose a local webhook endpoint for testing:

netbird expose 9000 --with-name-prefix webhooks