[GH-ISSUE #5355] Client chooses random UDP port on restart and becomes unavailable for P2P behind firewall #11108

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

Originally created by @mtdcr on GitHub (Feb 16, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5355

Describe the problem

As a direct consequence of https://github.com/netbirdio/netbird/issues/546, the client chooses a random UDP port for Wireguard if the configured port number is already in use.

Example log message:

INFO client/internal/connect.go:529: using 33957 as wireguard port: 51820 is in use

However, the configured port is in use, because the Wireguard interface wasn't freed when the container was stopped.

This breaks availability for P2P connections if firewall rules are in place, blocking the randomly chosen port.

It can be fixed by manually deleting the Wireguard interface after the container was stopped: ip link delete wt0

To Reproduce

  1. Start netbird container
  2. netbird up
  3. wg | grep port
  4. See the configured or default port number
  5. Restart netbird container
  6. wg | grep port
  7. See a random port number
  8. Restart netbird container
  9. Go to step 3

Edit: Updated description and the steps above. No waiting for a timeout is needed, so my former theory about SO_REUSEADDR was wrong. I can reproduce it every single time on different machines running containers with podman on Ubuntu 25.10. I can't reproduce it with a plain .deb install on a Debian 12 VM.

Expected behavior

The client should not try to be smart and use random port numbers, if a port number was configured. Instead, it should retry or just fail.

NetBird version

0.65.1

Is any other VPN software installed?

no

Additional context

  • OS: Ubuntu 25.10 (amd64)
  • podman

Have you tried these troubleshooting steps?

  • Reviewed client troubleshooting (if applicable)
  • Checked for newer NetBird versions
  • Searched for similar issues on GitHub (including closed ones)
  • Restarted the NetBird client
  • Disabled other VPN software
  • Checked firewall settings
Originally created by @mtdcr on GitHub (Feb 16, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5355 **Describe the problem** As a direct consequence of https://github.com/netbirdio/netbird/issues/546, the client chooses a random UDP port for Wireguard if the configured port number is already in use. Example log message: ``` INFO client/internal/connect.go:529: using 33957 as wireguard port: 51820 is in use ``` However, the configured port is in use, because the Wireguard interface wasn't freed when the container was stopped. This breaks availability for P2P connections if firewall rules are in place, blocking the randomly chosen port. It can be fixed by manually deleting the Wireguard interface after the container was stopped: `ip link delete wt0` **To Reproduce** 1. Start netbird container 2. `netbird up` 3. `wg | grep port` 4. See the configured or default port number 5. Restart netbird container 6. `wg | grep port` 7. See a random port number 8. Restart netbird container 9. Go to step 3 Edit: Updated description and the steps above. No waiting for a timeout is needed, so my former theory about SO_REUSEADDR was wrong. I can reproduce it every single time on different machines running containers with podman on Ubuntu 25.10. I can't reproduce it with a plain .deb install on a Debian 12 VM. **Expected behavior** The client should not try to be smart and use random port numbers, if a port number was configured. Instead, it should retry or just fail. **NetBird version** 0.65.1 **Is any other VPN software installed?** no **Additional context** - OS: Ubuntu 25.10 (amd64) - podman **Have you tried these troubleshooting steps?** - [ ] Reviewed [client troubleshooting](https://docs.netbird.io/how-to/troubleshooting-client) (if applicable) - [ ] Checked for newer NetBird versions - [x] Searched for similar issues on GitHub (including closed ones) - [ ] Restarted the NetBird client - [ ] Disabled other VPN software - [ ] Checked firewall settings
saavagebueno added the triage-needed label 2026-08-05 01:28:30 -04:00
Author
Owner

@mtdcr commented on GitHub (Feb 19, 2026):

After further debugging it's clear that the issue is caused by two things:

  • the container needs CAP_KILL for netbird-entrypoint.sh
  • Ubuntu's AppArmor needs a policy to allow sending signals.

netbird-entrypoint.sh executes this function when the container stops, where kill -TERM fails silently due to redirection of STDERR:

on_exit() {
  info "Shutting down NetBird daemon..."
  if test "${#service_pids[@]}" -gt 0; then
    info "terminating service process IDs: ${service_pids[@]@Q}"
    kill -TERM "${service_pids[@]}" 2>/dev/null || true
    wait "${service_pids[@]}" 2>/dev/null || true
  else
    info "there are no service processes to terminate"
  fi
}

As a temporary fix, I decided to skip the entire script and just use /usr/local/bin/netbird service run as the entrypoint. This way, netbird directly receives the signal from the container runtime and shuts down cleanly. It also improved the time it takes to restart the container.

<!-- gh-comment-id:3930782841 --> @mtdcr commented on GitHub (Feb 19, 2026): After further debugging it's clear that the issue is caused by two things: - the container needs `CAP_KILL` for `netbird-entrypoint.sh` - Ubuntu's AppArmor needs a policy to allow sending signals. `netbird-entrypoint.sh` executes this function when the container stops, where kill -TERM **fails silently** due to redirection of `STDERR`: ```sh on_exit() { info "Shutting down NetBird daemon..." if test "${#service_pids[@]}" -gt 0; then info "terminating service process IDs: ${service_pids[@]@Q}" kill -TERM "${service_pids[@]}" 2>/dev/null || true wait "${service_pids[@]}" 2>/dev/null || true else info "there are no service processes to terminate" fi } ``` As a temporary fix, I decided to skip the entire script and just use `/usr/local/bin/netbird service run` as the entrypoint. This way, netbird directly receives the signal from the container runtime and shuts down cleanly. It also improved the time it takes to restart the container.
Author
Owner

@mtdcr commented on GitHub (Feb 19, 2026):

Since there may be many unforeseeable reasons for the clean-up to fail, including OOM killer or crashes, netbird should probably delete the interface before setting up the new listening socket on startup.

<!-- gh-comment-id:3930803286 --> @mtdcr commented on GitHub (Feb 19, 2026): Since there may be many unforeseeable reasons for the clean-up to fail, including OOM killer or crashes, netbird should probably delete the interface before setting up the new listening socket on startup.
Sign in to join this conversation.
No Label triage-needed
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#11108