[os-tailscale] Support multiple concurrent Tailscale/Headscale instances
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 1.2k
- Forks
- 863
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 10
Description
Important notices
Before you add a new report, we ask you kindly to acknowledge the following:
- I have read the contributing guide lines at https://github.com/opnsense/plugins/blob/master/CONTRIBUTING.md
- I have searched the existing issues, open and closed, and I'm convinced that mine is new.
- When the request is meant for an existing plugin, I've added its name to the title.
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-tailscaleplugin 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
--statedirectory (e.g./var/db/tailscale-<name>/) - Unique
--socketpath (e.g./var/run/tailscale-<name>.sock) - Unique
--tundevice name (e.g.tailscale0,tailscale1, ...) - Unique
--port(starting from 41641 and incrementing, or user-configurable)
- Unique
- Control plane:
--login-serverURL (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:
- Convert the singleton data model (XML schema) to a keyed list of instances
- Generate one rc.d script per instance from a single template
- Wire each instance into
configdfor start/stop/status/reload - Expose each
tailscale<N>tun device to the interface assignments dropdown - Update the
tailscaleCLI wrapper calls in the plugin to always pass--socket=...explicitly - 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
tailscaledper 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-tailscaleadding 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:
- [OPNsense Forum thread "Tailscale Multiple Instances"](https://forum.opnsense.org/index.php?topic=50830.0) (jss, February 10, 2026) — exactly this request, currently unanswered.
[tailscale/tailscale#5721](https://github.com/tailscale/tailscale/issues/5721)— "FR: Connect to multiple tailnets simultaneously", with documented CLI workaround.[tailscale/tailscale#16430](https://github.com/tailscale/tailscale/issues/16430)(July 2025) — "Support multiple simultaneous Tailscale connections (official + headscale)", explicitly calls out consultant / multi-client / self-hosted-plus-official use cases.
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:
- [#5114](https://github.com/opnsense/plugins/issues/5114) — advertised route changes not pushed by the UI; a cleaner per-instance management surface would help address this.
- [#5012](https://github.com/opnsense/plugins/issues/5012) — binaries missing after reboot when
os-tailscaleis combined with rawpkg install tailscale; clearer plugin ownership of the daemon lifecycle would reduce footgun cases.
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the existing os-tailscale singleton XML schema and its rc.d/configd service entry points, then compare them with os-wireguard's multi-instance model. Trace how tailscale CLI calls, tun devices, and interface assignments are represented today. Done means named instances can be managed independently with migration from the existing single instance and per-instance interface integration.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100