Skip to content

[os-tailscale] Support multiple concurrent Tailscale/Headscale instances #5411

@unixversal

Description

@unixversal

Important notices
Before you add a new report, we ask you kindly to acknowledge the following:

Is your feature request related to a problem? Please describe.

The os-tailscale plugin (currently version 1.2) manages exactly one tailscaled daemon, one state directory, and one tailscale0 interface. This makes it impossible, via the plugin UI, to join OPNsense to more than one Tailscale/Headscale control plane simultaneously.

This is a significant functional gap relative to the other VPN plugins that ship with OPNsense:

  • WireGuard (os-wireguard): supports multiple instances natively via GUI
  • OpenVPN (core): supports multiple clients/servers natively via GUI
  • IPsec (core): supports multiple connections natively

Real-world use cases that hit this wall:

  • Consultants / MSPs who need to reach multiple client environments, each with their own Tailscale or self-hosted Headscale control server.
  • Users separating work and personal tailnets where merging under one control server is not possible because one side (employer, client) is administered externally.
  • Multi-tenant routing where OPNsense should act as the gateway/router for LAN clients into several independent tailnets at once — exactly what is already possible today with multiple WireGuard peers or OpenVPN clients.

The only current workaround is to SSH into OPNsense and hand-write additional rc.d scripts (tailscaled_work, tailscaled_client, etc.) with custom -state, -socket, -tun, and -port flags, then manually register each instance via CLI (tailscale --socket=... up --login-server=... --auth-key=...) and manually assign the resulting tailscale1, tailscale2 tun devices as OPNsense interfaces. This has significant drawbacks:

  • No UI, no status display, no log integration for instances beyond the first
  • Not idempotent across plugin updates
  • Invisible to OPNsense backup/restore
  • Each interface assignment, per-instance firewall rule set, and outbound NAT rule must be created and maintained manually
  • Fragile across OPNsense major upgrades and os-tailscale plugin updates
  • Not discoverable — most users will simply conclude OPNsense cannot do what other VPN plugins already do

Describe the solution you'd like

Extend os-tailscale so that the data model supports N named instances rather than a single global configuration, modeled after how os-wireguard represents multiple peer/server configurations.

For each instance the plugin should manage:

  • Identity: user-provided name (e.g. personal, work, client-acme)
  • Daemon parameters:
    • Unique --state directory (e.g. /var/db/tailscale-<name>/)
    • Unique --socket path (e.g. /var/run/tailscale-<name>.sock)
    • Unique --tun device name (e.g. tailscale0, tailscale1, ...)
    • Unique --port (starting from 41641 and incrementing, or user-configurable)
  • Control plane: --login-server URL (or default to hosted Tailscale), per-instance auth key
  • Runtime preferences: accept-routes, advertise-routes, exit-node flags, tags, hostname — all existing plugin settings, scoped per instance
  • Service management: one rc.d script per instance, started/stopped independently by configd
  • Interface integration: each tailscale<N> tun device assignable as a distinct OPNsense interface with its own firewall rules, outbound NAT, and status page

Proposed UI layout (mirrors os-wireguard):

VPN → Tailscale
├── Overview                  ← list of configured instances with status
├── Instances
│   ├── [+ Add instance]
│   ├── personal              ← click to edit settings / authentication / advanced
│   ├── work
│   └── client-acme
└── Diagnostics / Log File    ← per-instance selector

Implementation notes:

The underlying daemon already supports this. tailscaled accepts -state, -socket, -tun, and -port flags precisely to enable multiple instances on the same host (see upstream workaround in tailscale/tailscale#5721). The tailscale CLI honours --socket=<path> for targeting a specific daemon. The core work is therefore in the plugin:

  1. Convert the singleton data model (XML schema) to a keyed list of instances
  2. Generate one rc.d script per instance from a single template
  3. Wire each instance into configd for start/stop/status/reload
  4. Expose each tailscale<N> tun device to the interface assignments dropdown
  5. Update the tailscale CLI wrapper calls in the plugin to always pass --socket=... explicitly
  6. Migration path: existing single-instance installs become a single default instance with no behavior change

Describe alternatives you've considered

  • Sidecar VM or container (e.g. Alpine Linux VM beside OPNsense, running one tailscaled per tailnet and acting as subnet router back into a dedicated OPNsense transit VLAN). Works, but requires a hypervisor, extra machine management, and is harder to integrate into OPNsense's unified firewall/interface model than a native plugin instance.

  • FreeBSD jails on the OPNsense host. Better isolation but not a first-class OPNsense feature, and user-hostile compared to a plugin UI.

  • Manual rc.d scripts as described above. Works but defeats the purpose of having a plugin at all for instances beyond the first.

  • Community fork of os-tailscale adding this capability as a separate plugin. Feasible but fragments the ecosystem and creates ongoing duplicate-maintenance burden — upstream support would be the right home.

  • Single Headscale with users/tags/ACLs to logically separate environments. Only viable when the user controls all environments, which is not the case for client/employer tailnets.

Additional context

Existing demand:

Precedent within OPNsense: os-wireguard, core OpenVPN, and core IPsec all support multiple instances. There is no principled reason for Tailscale to be the only VPN technology in OPNsense limited to a single control plane per firewall.

Related existing issues in this repository that would benefit from the refactor:

Secondary but related gap (optional / follow-up):

[Tailscale KB 1299](https://tailscale.com/kb/1299/opnsense-unbound) acknowledges that tailscaled DNS settings are not integrated with OPNsense's Unbound resolver, forcing manual Domain Override configuration per tailnet. A multi-instance plugin would be a natural place to also surface per-instance DNS integration (auto-writing Unbound Domain Overrides for each instance's base domain pointing at 100.100.100.100 or the instance's configured DNS). Out of scope for this FR but worth anticipating in the design.

Environment of the requester:

  • OPNsense: 25.x
  • os-tailscale: 1.2
  • Use case: single OPNsense firewall needing to terminate three independent tailnets concurrently (personal Headscale, client development Headscale, work Tailscale), with LAN clients routed through the appropriate tunnel per-destination via outbound NAT on each tailscale<N> interface.

Disclosure:
Assisted by Antropic Claude Opus 4.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions