Authenticating to LXD-UI using Tailscale + tsidp (Feb. 2026)

This is an update from a previous post of mine, using a newer and official version of tsidp directly maintained by Tailscale.

A small guide for authenticating to (Canonical) LXD-UI using Tailscale with tsidp (Tailscale OpenID Connect (OIDC) Identity Provider).

I am running Ubuntu 22.04 LTS, with LXD installed via snap (as per official LXD docs ). At time of writing, I have LXD 5.21.4 and tsidp v0.0.11.

⚠️ tsidp itself is experimental and under active development.

Step 1: get Tailscale Certificates for LXD

This step allows accessing LXD through a nice tailnet https URL: it could be skipped if not needed.

By default, LXD uses self-signed certs: let's swap it with a cert from Tailscale.

Define some temporary variables for your Tailscale installation:

TS_DOMAIN="<your-tailnet>.ts.net"
# change "lxd" to your hostname running LXD:
TS_LXD_HOSTNAME="lxd.$TS_DOMAIN"

Enable remote access to LXD over Tailscale, using the lxc CLI:

# Use your 100.xx.xx.xx tailscale IP for lxd host
# Choose a LXD port
lxc config set core.https_address $(dig +short $TS_LXD_HOSTNAME):8943

Get a TLS cert from Tailscale and replace LXD's default certs:

sudo tailscale cert \
--cert-file /var/snap/lxd/common/lxd/server.crt \
--key-file /var/snap/lxd/common/lxd/server.key \
$TS_LXD_HOSTNAME

Reload LXD:

sudo systemctl reload snap.lxd.daemon

You should now be able to access https://<TS_LXD_HOSTNAME>:8943/ in your browser without https warnings.

As usual with Tailscale, don't forget to set your ACLs if needed to allow access!

Note that you will need to redo the procedure after certificate expiration in 90 days. If you want automated renewal, here are some examples .

Step 2: install tsidp

If not already done: install tsidp as per instructions (Recommended to use their pre-built Docker image).

Include in docker-compose:

environment:
TSIDP_ENABLE_STS=1

As indicated, don't forget to set the Application Capability Grant in your Tailscale ACLs (the example provided in the instructions is a good starting point).

Define some temporary variable:

# change "idp" to your TS_HOSTNAME from tsidp install:
TS_IDP_HOSTNAME="idp.$TS_DOMAIN"

Access tsidp UI (at first run, wait a bit for the HTTPS certificate to be generated) in your browser: https://<TS_IDP_HOSTNAME>/

Step 3: use tsidp as LXD Identity Provider

In tsidp UI, "Add New Client":

Client name = LXD

Redirect URIs = https://<TS_LXD_HOSTNAME>:8943/oidc/callback

Copy the generated client id and secret.

Now, configure LXD to trust tsidp:

lxc config set oidc.issuer=https://$TS_IDP_HOSTNAME
lxc config set oidc.client.id=<as generated in tsidp>
lxc config set oidc.client.secret=<as generated in tsidp>
lxc config set oidc.scopes="openid email profile"
# for missing scopes, see https://github.com/tailscale/tsidp/issues/109

# restart to be sure (might not be needed)
sudo systemctl reload snap.lxd.daemon

Add LXD users/groups for access control:

lxc auth group create tsadmins
# your-tailscale-identity e.g. your-name@gmail.com
lxc auth identity group add oidc/<your-tailscale-identity> tsadmins
lxc auth group permission add tsadmins server admin

Now in the LXD UI, you should see a “Login with SSO” button:

Just click, and you should be logged directly as LXD admin, using your Tailscale identity 🎉

Small Issue: Token Expiry

Currently, after ~5-10 minutes, the OIDC token expires and doesn't always auto-refresh:

Authentication failed

Failed OIDC Authentication: Failed to authenticate: Failed to refresh ID tokens: http status not ok: 400 Bad Request Error 400: invalid_grant - invalid refresh token

You might need to re-auth again. Not sure if this is a missing feature in tsidp, a config issue, or an LXD-side limitation. If anyone has insights on how to fix this, please share!