[GH-ISSUE #5777] Feature Request: Extend netbird expose to support remote targets (hostname or IP + port) #11058

Open
opened 2026-08-05 01:28:18 -04:00 by saavagebueno · 7 comments
Owner

Originally created by @renne on GitHub (Apr 2, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5777

Problem

netbird expose <port> always targets 127.0.0.1:<port> on the local machine. There is no way to specify a remote host, which limits the command to services that are co-located with the NetBird agent.

This is a significant constraint in several common topologies:

  • Docker shared-network: The NetBird container is joined to a Docker network alongside other services. Today, reaching other-service:8080 requires either network_mode: service:netbird (sidecar — forces all containers onto the same network namespace) or installing a second NetBird agent inside each service container.
  • Routing peer / gateway host: A single NetBird peer has LAN or subnet access (e.g., an OPNsense VM, a router, or a jump host). Services on that LAN (10.0.0.29:8080) cannot be exposed without installing a NetBird agent on each individual service host.
  • Remote service over overlay: A service is reachable at a NetBird peer IP (100.x.x.x:9000) but the peer itself does not run netbird expose.

Proposed solution

Add an optional --with-target-host flag (accepting both IP addresses and hostnames) to netbird expose:

netbird expose <port> [--with-target-host <hostname|IP>]

Examples

# Existing behaviour — unchanged
netbird expose 8080

# Expose a service on a Docker DNS hostname (shared network topology)
netbird expose 8080 --with-target-host other-service

# Expose a LAN service by IP (routing peer topology)
netbird expose 8080 --with-target-host 10.0.0.29

# Expose a service on a NetBird overlay peer IP
netbird expose 9000 --with-target-host 100.64.0.5

# Combined with protocol and auth flags (existing flags unchanged)
netbird expose 5432 --protocol tcp --with-target-host db.internal

Hostname resolution

Hostnames should be resolved by the NetBird daemon at session-creation time (not by the CLI process), so that Docker DNS names and overlay-network hostnames resolve correctly in the daemon's network namespace. Resolution should be re-attempted on renewal if the initial resolve fails.

Behaviour when --with-target-host is omitted

Default remains 127.0.0.1 — fully backwards-compatible.

Alternatives considered

  • network_mode: service:netbird (sidecar): Works for Docker but forces containers onto a shared network namespace, preventing independent port binding and breaking some service discovery patterns. Also does not help for LAN/routing-peer topologies.
  • Dashboard permanent services: The dashboard does support arbitrary target host:port (Peer / Host / Domain / Subnet types), but there is no CLI or public API path to create these services. The declarative/scripted workflow is not possible today.
  • Label-based Docker provider (#5677): Addresses the Docker Compose declarative use case but requires the label provider feature to be built first. A --with-target-host flag is the minimal imperative/CLI complement that works today for all topologies, including non-Docker ones.
  • SSH remote forwarding hack (noted in #5465): ssh -R can proxy a remote port to a local one, making it reachable via netbird expose. This is a workaround, not a solution.
  • #5677 — Label-based auto-expose for Docker Compose (Traefik-style provider) — declarative complement to this request
  • #5465 — Extend netbird expose to allow raw TCP (now shipped in v0.66)
  • #5319 — Arbitrary TCP/UDP support for the Reverse Proxy (61 👍) — dashboard remote targets; this request adds the same capability to the CLI

Additional context

The dashboard already models a target as a separate concept from the exposing peer (Peer / Host / Domain / Subnet with an explicit port). The --with-target-host flag would align the CLI expose command with that model, making the CLI a first-class citizen for all expose scenarios rather than a local-only shortcut.

Originally created by @renne on GitHub (Apr 2, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5777 ## Problem `netbird expose <port>` always targets `127.0.0.1:<port>` on the local machine. There is no way to specify a remote host, which limits the command to services that are co-located with the NetBird agent. This is a significant constraint in several common topologies: - **Docker shared-network**: The NetBird container is joined to a Docker network alongside other services. Today, reaching `other-service:8080` requires either `network_mode: service:netbird` (sidecar — forces all containers onto the same network namespace) or installing a second NetBird agent inside each service container. - **Routing peer / gateway host**: A single NetBird peer has LAN or subnet access (e.g., an OPNsense VM, a router, or a jump host). Services on that LAN (`10.0.0.29:8080`) cannot be exposed without installing a NetBird agent on each individual service host. - **Remote service over overlay**: A service is reachable at a NetBird peer IP (`100.x.x.x:9000`) but the peer itself does not run `netbird expose`. ## Proposed solution Add an optional `--with-target-host` flag (accepting both **IP addresses** and **hostnames**) to `netbird expose`: ``` netbird expose <port> [--with-target-host <hostname|IP>] ``` ### Examples ```bash # Existing behaviour — unchanged netbird expose 8080 # Expose a service on a Docker DNS hostname (shared network topology) netbird expose 8080 --with-target-host other-service # Expose a LAN service by IP (routing peer topology) netbird expose 8080 --with-target-host 10.0.0.29 # Expose a service on a NetBird overlay peer IP netbird expose 9000 --with-target-host 100.64.0.5 # Combined with protocol and auth flags (existing flags unchanged) netbird expose 5432 --protocol tcp --with-target-host db.internal ``` ### Hostname resolution Hostnames should be resolved by the **NetBird daemon** at session-creation time (not by the CLI process), so that Docker DNS names and overlay-network hostnames resolve correctly in the daemon's network namespace. Resolution should be re-attempted on renewal if the initial resolve fails. ### Behaviour when `--with-target-host` is omitted Default remains `127.0.0.1` — fully backwards-compatible. ## Alternatives considered - **`network_mode: service:netbird` (sidecar)**: Works for Docker but forces containers onto a shared network namespace, preventing independent port binding and breaking some service discovery patterns. Also does not help for LAN/routing-peer topologies. - **Dashboard permanent services**: The dashboard *does* support arbitrary target host:port (Peer / Host / Domain / Subnet types), but there is no CLI or public API path to create these services. The declarative/scripted workflow is not possible today. - **Label-based Docker provider (#5677)**: Addresses the Docker Compose declarative use case but requires the label provider feature to be built first. A `--with-target-host` flag is the minimal imperative/CLI complement that works *today* for all topologies, including non-Docker ones. - **SSH remote forwarding hack** (noted in #5465): `ssh -R` can proxy a remote port to a local one, making it reachable via `netbird expose`. This is a workaround, not a solution. ## Related issues - #5677 — Label-based auto-expose for Docker Compose (Traefik-style provider) — declarative complement to this request - #5465 — Extend `netbird expose` to allow raw TCP (now shipped in v0.66) - #5319 — Arbitrary TCP/UDP support for the Reverse Proxy (61 👍) — dashboard remote targets; this request adds the same capability to the CLI ## Additional context The dashboard already models a `target` as a separate concept from the exposing peer (Peer / Host / Domain / Subnet with an explicit port). The `--with-target-host` flag would align the CLI `expose` command with that model, making the CLI a first-class citizen for all expose scenarios rather than a local-only shortcut.
saavagebueno added the enhancement label 2026-08-05 01:28:18 -04:00
Author
Owner

@Christopher87R commented on GitHub (Apr 8, 2026):

That's exactly what I was looking for - great suggestion!

<!-- gh-comment-id:4204642386 --> @Christopher87R commented on GitHub (Apr 8, 2026): That's exactly what I was looking for - great suggestion!
Author
Owner

@Christopher87R commented on GitHub (Apr 8, 2026):

To be more specific, I have several Docker containers on a host, and I want to make these services accessible. There’s currently no way to do this, so the reverse proxy option isn’t useful.
The workarounds (network_mode or the Netbird agent in Docker) aren’t really viable options.
Am I missing something that would make my use case possible?

<!-- gh-comment-id:4204958970 --> @Christopher87R commented on GitHub (Apr 8, 2026): To be more specific, I have several Docker containers on a host, and I want to make these services accessible. There’s currently no way to do this, so the reverse proxy option isn’t useful. The workarounds (network_mode or the Netbird agent in Docker) aren’t really viable options. Am I missing something that would make my use case possible?
Author
Owner

@renne commented on GitHub (Apr 8, 2026):

@Christopher87R After a lot of research with the sparse documentation I finally understand how exposing Docker network services works in Netbird.

  1. Enable "Settings" -> "Networks" -> "Enable DNS Wildcard Routing" in the Netbird Dashboard to have Netbird Management server resolve hostnames via Netbird clients.
  2. On your Docker host, create a container with the Netbird client image.
  3. Add Docker services connected to the Docker network of the Netbird client (e.g. "docker1").
  4. In the Netbird dashboard, create a new network
  5. Add the Netbird client as routing-peer to the network
  6. Add a "Domain Name" ressource with the wildcard name of the Docker network to the network (e.g. "*.docker1").
  7. Netbird is now capable to resolve the FQDNs of the Docker services (e.g "nextcloud.docker1").
  8. For each Docker service, add a Domain Name ressource pointing to the FQDN (e.g "nextcloud.docker1").
  9. In "Reverse Proxy" -> "Custom domains" make sure you have setup your domain.
  10. In "Reverse Proxy" -> "Services" create a service sudomain under your domain with the FQDN as "Protocol & Host/IP" and Port.
  11. Optionally configure Location, Request Timeout and Custom Headers.

Remote targets via "expose" command would be much simpler for testing, of course.

This has been tested with Netbird v0.67.3/Dashboard v2.36.0. I do not know if it already works with Netbird Cloud.

<!-- gh-comment-id:4206561118 --> @renne commented on GitHub (Apr 8, 2026): @Christopher87R After a lot of research with the sparse documentation I finally understand how exposing Docker network services works in Netbird. 1. Enable "Settings" -> "Networks" -> "Enable DNS Wildcard Routing" in the Netbird Dashboard to have Netbird Management server resolve hostnames via Netbird clients. 2. On your Docker host, create a container with the Netbird client image. 3. Add Docker services connected to the Docker network of the Netbird client (e.g. "docker1"). 4. In the Netbird dashboard, create a new network 5. Add the Netbird client as routing-peer to the network 6. Add a "Domain Name" ressource with the wildcard name of the Docker network to the network (e.g. "*.docker1"). 7. Netbird is now capable to resolve the FQDNs of the Docker services (e.g "nextcloud.docker1"). 8. For each Docker service, add a Domain Name ressource pointing to the FQDN (e.g "nextcloud.docker1"). 9. In "Reverse Proxy" -> "Custom domains" make sure you have setup your domain. 10. In "Reverse Proxy" -> "Services" create a service sudomain under your domain with the FQDN as "Protocol & Host/IP" and Port. 11. Optionally configure Location, Request Timeout and Custom Headers. Remote targets via "expose" command would be much simpler for testing, of course. This has been tested with Netbird v0.67.3/Dashboard v2.36.0. I do not know if it already works with Netbird Cloud.
Author
Owner

@omarahmed786 commented on GitHub (Jun 11, 2026):

Thank you so much for that. I really think we shouldn't have to do this messy workaround and instead should be able to target hostname:port in addition to IP:port just like Pangolin and many reverse proxies allow

<!-- gh-comment-id:4685369918 --> @omarahmed786 commented on GitHub (Jun 11, 2026): Thank you so much for that. I really think we shouldn't have to do this messy workaround and instead should be able to target hostname:port in addition to IP:port just like Pangolin and many reverse proxies allow
Author
Owner

@aturkenov commented on GitHub (Jun 17, 2026):

Hi @lixmal, is it possible to implement?

<!-- gh-comment-id:4737366953 --> @aturkenov commented on GitHub (Jun 17, 2026): Hi @lixmal, is it possible to implement?
Author
Owner

@aturkenov commented on GitHub (Jun 18, 2026):

docker compose example

  some_service:
    image: ...
    networks:
      - service_network

  netbird:
    image: netbirdio/netbird-expose:latest
    cap_add:
      - NET_ADMIN
      - SYS_ADMIN
    networks:
      - service_network
    environment:
      - NB_SETUP_KEY="..."
      - NB_EXPOSE_HOST="some_service"
      - NB_EXPOSE_PORT=80
      - NB_EXPOSE_CUSTOM_DOMAIN="some-service.example.com"
<!-- gh-comment-id:4738555670 --> @aturkenov commented on GitHub (Jun 18, 2026): docker compose example ``` some_service: image: ... networks: - service_network netbird: image: netbirdio/netbird-expose:latest cap_add: - NET_ADMIN - SYS_ADMIN networks: - service_network environment: - NB_SETUP_KEY="..." - NB_EXPOSE_HOST="some_service" - NB_EXPOSE_PORT=80 - NB_EXPOSE_CUSTOM_DOMAIN="some-service.example.com" ```
Author
Owner

@renne commented on GitHub (Jun 18, 2026):

@aturkenov @lixmal This would have to be some kind of YAML array as there could be multiple services at one Netbird client instance.

<!-- gh-comment-id:4738975620 --> @renne commented on GitHub (Jun 18, 2026): @aturkenov @lixmal This would have to be some kind of YAML array as there could be multiple services at one Netbird client instance.
Sign in to join this conversation.
No Label enhancement
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#11058