Insights

Tailscale SSH setup on Linux and macOS

Set up Tailscale SSH on Linux or supported macOS, verify access, diagnose activation failures, and keep a working recovery path.

Tailscale SSH setup editorial illustration
On this page

To set up Tailscale SSH (Secure Shell), connect both devices to your tailnet, your Tailscale network, allow network access on port 22, and add an SSH rule for the target account. On the destination, run tailscale set --ssh, then connect with ssh user@host using its Tailscale address.

If you have already tried setup, jump to activation and login diagnosis.

In short:

  • Allow both layers. Permit network traffic and SSH access to an existing local account.
  • Enable the destination. Run tailscale set --ssh on a supported host.
  • Verify safely. Check the policy and a new session while keeping tested fallback access.

Get both devices ready

Start with Tailscale installed, running and connected on both devices. The destination needs an existing local account, such as dev. You'll also need permission to edit the tailnet policy if its current rules don't allow the connection.

Embedded SSH’s documented feature minimum is Tailscale v1.24. The tailscale get command used below was added in v1.102.1. These versions establish feature eligibility and command availability; they are not recommendations for deployment or assurances about current security findings. Before enabling SSH, check Tailscale’s current release notes and security bulletins, and follow the applicable update guidance.

Choose the setup that matches each device's role.

Device roleSupported setupServer requirement
Linux destinationLinux, including FedoraEnable embedded SSH
macOS destinationOpen-source tailscale plus tailscaled CLIEnable embedded SSH
Linux clientTailscale and ordinary sshNo server needed
macOS clientTailscale and ordinary sshNo server needed

A macOS destination requires the open-source command-line interface (CLI) variant. Having access to a CLI command from a macOS graphical installation isn't enough to meet that server requirement. A Mac used only as a client can use ordinary ssh to reach Fedora without enabling an SSH server on the Mac.

1. Inspect both devices from a Linux or supported macOS CLI terminal.

Use the Tailscale CLI to inspect version and device information.

tailscale version --daemon
tailscale status

Check the installed CLI and daemon versions against the current release and security guidance, and confirm that status shows the expected devices. Status helps confirm tailnet participation, but it doesn't prove SSH permission. If a prerequisite isn't met, complete it before changing SSH settings.

Keep a way back in

2. Prepare a reversible change on one destination first.

Change only devices and policies you own or administer. Save the original tailnet policy before you change it. Keep the ordinary SSH service, configuration and keys intact.

3. Test a separate recovery path.

Use a local console or an existing private SSH connection that doesn't use the destination's Tailscale address. Don't expose remote administration to the public internet to create a fallback.

An existing SSH session through the Tailscale address isn't a safe recovery path. Tailscale warns that enabling embedded SSH will make those sessions hang. Keep your tested alternative available until policy checks and a new SSH session both pass.

Allow the connection and the account

4. Review the existing policy in Access controls.

Have an Admin or Network admin inspect the policy before adding rules. Use network grants for new network permissions. Existing access control lists (ACLs) can remain alongside grants.

Tailscale SSH needs separate permission for network traffic and for the SSH login. The network grant allows traffic to port 22. The ssh rule allows the named local account. A narrow new grant doesn't cancel broader permissions that are already there, so review all matching rules.

5. Add rules for the intended identity and local account.

For user-owned devices, autogroup:self targets the connecting user's own devices. In this example, Alice's client and destination must be owned by Alice. Replace alice@example.com with your Tailscale identity and dev with an account that already exists on the destination.

{
  "grants": [
    {
      "src": ["alice@example.com"],
      "dst": ["autogroup:self"],
      "ip": ["tcp:22"]
    }
  ],
  "ssh": [
    {
      "action": "check",
      "src": ["alice@example.com"],
      "dst": ["autogroup:self"],
      "users": ["dev"]
    }
  ]
}

Merge these entries into the existing top-level arrays instead of replacing the whole policy. Tailscale SSH gives access to existing accounts. It doesn't create the dev account. The SSH dst field has no port suffix because embedded SSH uses port 22.

autogroup:self doesn't apply to tagged devices. For an already tagged destination, use its existing tag in both rules, keeping the intended identity and local account explicit. Replace tag:server below with that existing tag.

{
  "grants": [
    {
      "src": ["alice@example.com"],
      "dst": ["tag:server"],
      "ip": ["tcp:22"]
    }
  ],
  "ssh": [
    {
      "action": "check",
      "src": ["alice@example.com"],
      "dst": ["tag:server"],
      "users": ["dev"]
    }
  ]
}

Both examples use check mode with the default 12-hour check period. The policy reference lists custom checkPeriod settings for Premium and Enterprise plans. These examples leave that field out.

Turn on SSH at the destination

6. Enable embedded SSH on the Linux or supported macOS CLI destination.

Run this command with the required local administrative permission.

tailscale set --ssh

For an already connected host, set changes only the setting you name. You don't need to restate the device's other settings.

7. Inspect the setting and record the destination address.

Run these commands on the same destination.

tailscale get ssh
tailscale ip -4

The SSH preference should report true. That shows the SSH preference is on, not that your client has permission to log in or that a session works. Record the address from tailscale ip -4 for the policy tests and client connection.

For tailnet traffic addressed to its SSH port, Tailscale uses its embedded server instead of the ordinary SSH server. You don't need to install an ordinary SSH server, edit its configuration, copy client keys or change router port forwarding for this embedded setup.

Check the policy and open a new session

8. Add assertions for the intended access.

Preserve existing hosts, tests and sshTests entries when merging this fragment. Replace <destination-tailscale-ip> with the actual address you recorded, and use the same identity and account as your rules. fedora-host is a policy alias here: use an unused alias, or an existing alias for that same destination.

{
  "hosts": {
    "fedora-host": "<destination-tailscale-ip>"
  },
  "tests": [
    {
      "src": "alice@example.com",
      "proto": "tcp",
      "accept": ["fedora-host:22"]
    }
  ],
  "sshTests": [
    {
      "src": "alice@example.com",
      "dst": ["fedora-host"],
      "check": ["dev"],
      "deny": ["root"]
    }
  ]
}

These assertions require Alice's network access to port 22, check-mode access as dev, and denial as root. Tailscale rejects a policy update if an assertion fails. If that happens, reconcile the rules with your intended permissions instead of broadening access just to pass the test.

Passing assertions prove the policy matches those tested expectations. They don't prove the live SSH service works.

9. Open a new connection from a Linux or macOS client.

If MagicDNS, Tailscale's Domain Name System (DNS) naming feature, is available, replace fedora-host with the destination's actual name.

ssh dev@fedora-host

Otherwise, use the recorded Tailscale address. Replace the placeholder before running this alternative.

ssh dev@<destination-tailscale-ip>

Follow the check-mode sign-in URL if prompted. Check mode requires signing in again when no valid recent check exists. A connection within the default check period may open without another prompt, so no prompt alone isn't a failure.

If ssh presents an unfamiliar host-key fingerprint, confirm the embedded SSH server's fingerprint through a trusted administrator or console before accepting it. Investigate a changed key instead of bypassing the warning. Embedded SSH and the ordinary SSH server can have different host keys.

Ordinary ssh is enough for this connection. The optional tailscale ssh wrapper isn't available on sandboxed macOS builds.

10. Confirm the remote host and account, then reconnect.

In the remote Linux or macOS shell, inspect the reported hostname and effective user.

hostname
whoami
exit

hostname should show the expected destination, and whoami should report dev. These checks confirm the values reported by the remote shell; they do not replace host-key verification. Open another connection using the same client command to confirm you can reconnect.

A successful session shows that this tested identity, destination and account work. It doesn't show that every unwanted identity or account is denied. If any result isn't what you expected, stop the rollout and keep fallback access available.

If Tailscale SSH is not activating

Use the first failed observation to choose the next check after an attempted setup. Keep your tested recovery path available, and confirm you are inspecting the destination rather than the connecting laptop.

Observed resultWhat it establishesNext check
Activation command failedNo successful change was confirmed.Check the destination, supported build and local permissions under Get both devices ready; retain the exact error.
Preference is falseEmbedded SSH is off for the currently connected tailnet.Confirm the host and tailnet, then follow Turn on SSH if authorized.
Preference is true, but login failsOpt-in is enabled; the connection remains unverified.Check the address, port, network permission, SSH rule and existing account under Allow the connection and Check the policy.
Preference is unknown or the query failsThere is no valid setting result.Check installed CLI help and versions under Get both devices ready. An unrecognized query does not mean false.

Policy assertions and a Tailscale ping answer different questions; neither proves a new login. If the network path fails, follow the device-connectivity checks, including identity, expiry and relevant firewall rules. When check mode applies, complete its sign-in flow; a recent valid check can mean there is no new prompt.

A SELinux warning means host policy may restrict SSH actions inside tailscaled. The warning alone does not prove activation failed. Ask an authorized Linux administrator to inspect relevant denials and review any policy change. Do not disable SELinux or use permissive mode as a generic fix.

After one justified change, repeat the policy and fresh-session checks. If failure persists, send the administrator or support the timestamp and timezone, OS/package variants, CLI/daemon versions, exact command and redacted output, preference result, destination address/port, source identity or tags, intended account and relevant receiving-side logs. Keep credentials, keys, approval URLs and unredacted diagnostics out of public posts.

Undo the change when you need to

11. Disable embedded SSH through your tested recovery path.

On the Linux or supported macOS CLI destination, run these commands with the required local administrative permission.

tailscale set --ssh=false
tailscale get ssh

The preference should now report false, which confirms embedded SSH is disabled. Don't use tailscale down as an SSH-only rollback.

12. Restore the intended policy and check ordinary access.

Restore the saved policy if no unrelated changes have happened since you saved it. Otherwise, restore or remove this rollout's SSH rules and their corresponding SSH assertions together, keeping unrelated edits as they are. Review its network grant and network assertions separately, keeping them only if they match the ordinary SSH access you intend to retain.

Ordinary SSH can still run over Tailscale with its own authentication once embedded SSH is disabled. It needs a working SSH service and network permission. Disabling embedded SSH doesn't create a working fallback by itself.

Keep ordinary SSH if you need separate permissions for different operating-system users on a shared client, or command restrictions in authorized_keys. Any operating-system user on a client can use that device's permitted Tailscale SSH access, subject to server-side policy. That matters when the client is shared or runs untrusted code.

Before you finish, confirm these checks.

  • Your destination is a supported Linux or macOS open-source CLI host.
  • Network permission and a separate SSH rule match the intended access.
  • Policy assertions pass for the allowed account and the tested denial.
  • A new session reaches the expected host and local account.
  • Your tested recovery path remains available.

Ask an AI assistant

Share this article

Private Everywhere

Stop giving the internet everything

Keep your browsing, messages, files, and agents private.