Tailscale → JetKVM on Windows 11

date 2026.05.22 read ~7 min words 1,609 #networking#homelab#remote-access

A practical walkthrough for getting Tailscale running on a JetKVM, accessible from your tailnet, isolated from the rest of your infrastructure, and locked down for unattended remote operation. Written from Windows 11, but the Linux/macOS path is nearly identical (and easier).

TL;DR
  1. Generate or locate your SSH key on Windows.
  2. Enable Developer Mode on the JetKVM and paste in your public key.
  3. Use the official JetKVM install script from Git Bash. Patch one line so the ping check works on Windows.
  4. Configure Tailscale ACLs so the JetKVM can be reached, but can’t reach anything else.
  5. Disable Developer Mode, set a web UI password, and disable Tailscale key expiry.

What you’ll need

If you’re on Linux or macOS, skip the “Git Bash” parts — your shell already has everything you need.

Step 1: Generate or locate your SSH key on Windows

Open PowerShell and check what you’ve got:

ls $env:USERPROFILE\.ssh

Look for a file ending in .pub — usually id_ed25519.pub. If you have one, copy it to your clipboard:

cat $env:USERPROFILE\.ssh\id_ed25519.pub | clip

If .ssh doesn’t exist or has no .pub file, generate a new key:

ssh-keygen -t ed25519 -C "your-name@windows"

Press Enter to accept the default location, optionally set a passphrase, and then copy the public key with the cat ... | clip command above.

Public keys are designed to be shared — paste them anywhere. The private key (the file without .pub) is the one to guard.

Step 2: Enable Developer Mode on the JetKVM

JetKVM uses key-based SSH authentication, which has to be turned on explicitly.

  1. Open the JetKVM web UI in your browser.
  2. Go to Settings → Advanced.
  3. Check Enable Developer Mode.
  4. Paste your public key into the SSH key field.
  5. Save.

Verify it works from PowerShell:

ssh root@<jetkvm-ip>

You should land in a BusyBox shell. If you get prompted for a password, the key didn’t take — re-copy it with cat $env:USERPROFILE\.ssh\id_ed25519.pub | clip and paste again, making sure it ends up as one continuous line in the web UI.

HEADS UP

A note on the default root password. The JetKVM ships with a hardcoded root password (rockchip) that works for SSH whether or not you’ve added a key. This is a known issue tracked upstream. Tailscale will reduce your exposure here, but the cleanest fix is to disable Developer Mode entirely once you’re done, which we’ll do later.

Step 3: Install Tailscale using the official script

The JetKVM team maintains an install script at https://jetkvm.com/install-tailscale.sh. It’s meant to be run from your workstation, not on the JetKVM itself — it SSHes in and handles the device-side install for you.

Open Git Bash (not PowerShell — the script is a POSIX shell script) and run:

curl -O https://jetkvm.com/install-tailscale.sh
chmod +x install-tailscale.sh
./install-tailscale.sh <jetkvm-ip>

Fixing the Windows ping gotcha

If the script fails at step 1 with “JetKVM device is not reachable” but you can clearly ping and SSH to it manually, the cause is a Linux-vs-Windows ping flag mismatch. The script uses ping -c 3 -W 5 (Linux syntax), but Git Bash hands off to Windows’ ping.exe, which uses different flags.

Patch it in one line:

sed -i 's/ping -c 3 -W 5/ping -n 1 -w 5000/' install-tailscale.sh

Then re-run:

./install-tailscale.sh <jetkvm-ip>

The script will:

Open that URL in any browser logged into your Tailscale account, approve the device, and the JetKVM joins your tailnet.

Step 4: Set up ACLs so the JetKVM is isolated

The whole point of putting the JetKVM on Tailscale is remote access. But you don’t want a device that lives in a remote location and runs an embedded Linux distribution to have free run of your tailnet. The fix is Tailscale ACLs.

Go to the Tailscale admin console → Access Controls. Edit your policy file to tag the JetKVM and lock it down:

{
    "tagOwners": {
        "tag:kvm": ["autogroup:admin"]
    },
    "acls": [
        {
            "action": "accept",
            "src":    ["autogroup:admin"],
            "dst":    ["*:*"]
        }
    ]
}

Save it. The first rule lets your admin user’s devices reach anything (including the JetKVM). There’s deliberately no rule allowing tag:kvm to be a source — Tailscale’s default-deny posture means the JetKVM can’t initiate connections to anything else on your tailnet.

If you already have an ACL policy, merge tag:kvm into your existing tagOwners block. Existing rules with autogroup:admin as source already cover your access to the new device.

Apply the tag

Two options:

Verify in the Machines list: the JetKVM should show tag:kvm next to it instead of your email. That’s the signal that the device is no longer “owned” by your user identity and is now subject to the tag’s ACL posture.

Why your other devices don’t need tags

Personal devices (laptop, phone, etc.) logged into your Tailscale account are owned by your user identity and covered by autogroup:admin → *:*. Tagging them would remove that broad access and force you to write explicit rules. Tags are for infrastructure that should have a role rather than a user — the JetKVM, headless servers, IoT stuff. Personal devices stay user-owned.

Step 5: Lock the JetKVM down for unattended operation

Three changes before you walk away from this device, especially if it’s heading to a remote location.

Disable Developer Mode

This closes the rockchip SSH backdoor entirely. In the JetKVM web UI, go to Settings → Advanced and uncheck Enable Developer Mode.

You’ll lose SSH access until you re-enable it, which is the point. When you eventually need SSH (Tailscale binary updates, firmware update recovery), you can re-enable it via the web UI — which is now Tailscale-accessible from anywhere — paste your key again, do your work, and disable it again.

Set a web UI password

If you skipped the password during onboarding, set one now. Settings → Access → set a password. Tailscale ACLs protect the network path, but defense in depth matters.

Disable Tailscale key expiry on the JetKVM

This is the big one for any unattended device. By default, Tailscale machine keys expire every ~180 days and the device silently drops off your tailnet until you re-authenticate it. For a remote device, that’s a fast track to a service call.

In the Tailscale admin console: Machines → JetKVM → ⋯ menu → Disable key expiry.

Step 6: Access your KVM

From any device on your tailnet:

  1. Open a browser.
  2. Navigate to http://<jetkvm-tailscale-ip> or, if you have MagicDNS enabled, http://<jetkvm-name>.<your-tailnet>.ts.net.
  3. Enter the web UI password you just set.

That’s it. Works from your laptop on a coffee shop wifi, your phone on cell data, anywhere a tailnet device can reach the internet. No port forwarding, no public exposure.

Bookmark the MagicDNS name rather than the IP — it’s stable across the device’s lifetime.

Verifying the isolation actually works

Quick sanity check that the ACL setup is doing what you think.

If you re-enabled Developer Mode briefly to test, from the JetKVM run:

tailscale ping <some-other-tailnet-device>

This should fail. The JetKVM has no outbound permissions in your ACL, so it can’t initiate connections to anything else on your tailnet.

From your laptop:

tailscale ping <jetkvm-name>

This should succeed.

If both succeed, your ACL has an outbound rule for tag:kvm somewhere that you don’t want. Audit the JSON.

Maintenance notes

A few things to know for the long-term operation of this setup.

Firmware updates wipe /etc/init.d/. The Tailscale binary in /userdata/ survives JetKVM OS updates, but the init script that starts the daemon at boot does not. After any firmware update, you’ll need to re-enable Developer Mode, SSH in, and re-run the install script (which will detect the existing binary and just restore the init).

Tailscale binary updates. Periodically the version on the JetKVM will drift behind current. Same process as a fresh install — re-enable Developer Mode, re-run the install script from your workstation, disable Developer Mode again.

The official install script has a known issue with version 1.98.x being temporarily blocked on JetKVM. It’ll automatically fall back to the latest known-good version. If you want to force a specific version, use the -v flag.

If Tailscale itself dies on the JetKVM, SSH over Tailscale also dies — you’re stuck until physical access. Mitigations: disable key expiry (which we did), keep the device on UPS, and trust that JetKVM + Tailscale is a well-trodden path at this point. A few hundred people have done this.

Wrap-up

What you end up with is a remote KVM you can reach from anywhere, that runs on someone else’s network without ever being exposed to the public internet, and that — if it ever gets compromised — can see exactly nothing else on your tailnet. The default-deny ACL stance means you don’t have to enumerate every device you want to protect; you just don’t grant the JetKVM permission to reach them.

Total complexity: one install script, one ACL block, three checkboxes in admin consoles. Worth the half hour.