Reaching your machines¶
fleet manages no access. It runs ssh exactly as you would — with your
~/.ssh/config, your ssh agent, your own keys, Tailscale SSH, the key on your rental
provider's account — and nothing else. It has no key of its own, writes no
authorized_keys, and keeps no list of who may reach what.
So the rule is simple: a machine you can ssh into from here, fleet can watch from
here. One you cannot, it cannot either, and the fix is the one you would make for
ssh anyway. This page is that fix, for each kind of machine.
Why it works this way: what lets someone into a machine is decided by the people who own
it — in its authorized_keys, in a tailnet policy, on a provider's account page. Those
are already the places you manage access. A second system beside them, one more copy of
"who may log in where", would only be one more thing to get wrong.
| The machine | What lets you in | Section |
|---|---|---|
| Linux, on your tailnet | your tailnet's SSH policy | Linux on a tailnet |
| Windows, or a Mac | your key in its authorized_keys, once |
Windows or Mac |
| a GPU rental | the key on your provider account | Rentals |
| on your LAN, or another VPN | your key, and a Host in ~/.ssh/config if you like |
LAN or another VPN |
Every machine running fleet is equal: each one looks at the machines its own ssh reaches. There is no center to set up, and nothing is synced between them.
Linux on a tailnet¶
With Tailscale SSH turned on for a
machine (tailscale set --ssh on it), Tailscale answers ssh there itself and decides who
may log in from your tailnet's policy — not from authorized_keys. There are no keys to
copy: if the policy lets you in, ssh you@gpu-box works, and so does fleet.
fleet ls --discover # your tailnet's machines, among others
fleet add gpu-box # by the name it lists
If it refuses, the policy is where to look: in the Tailscale admin console, under
Access controls, add an ssh rule for you and that machine. fleet doctor gpu-box
prints one to start from, such as:
{"action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self"], "users": ["ubuntu"]}
Use accept, not check, for machines your agents use. A check rule makes you
sign in again in a browser every so often before ssh lets you through. You can do that;
an agent running fleet with no browser and no terminal never can, so to it the machine
simply refuses. fleet doctor recognises this and says so.
A discovered tailnet machine is added as ssh NAME, which logs in as your local user
name. If the account there is a different one, give it: fleet add "ssh ubuntu@gpu-box".
Windows or Mac¶
Tailscale's SSH server is not available on Windows, nor in the Mac app from the App
Store or Tailscale's own Mac app. Such machines answer ssh with their own OpenSSH server,
which needs your key in its authorized_keys. The tailnet still carries the connection:
you add them by their tailnet name or address, as any other machine.
Turn the server on first, if it is not:
| OS | How |
|---|---|
| macOS | System Settings → General → Sharing → Remote Login |
| Windows | in an administrator PowerShell: Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0; Start-Service sshd; Set-Service sshd -StartupType Automatic |
Then put your public key on it, once. fleet doctor NAME prints these lines with your
key already in them: the .pub beside the key file the machine was added with (-i),
or else ~/.ssh/id_ed25519.pub, id_ecdsa.pub, id_rsa.pub or any other .pub there. If
you have no key yet, ssh-keygen -t ed25519 makes one.
On a Mac (or Linux), in a terminal on that machine:
umask 077; mkdir -p ~/.ssh && printf '%s\n' 'ssh-ed25519 AAAA... you@laptop' >> ~/.ssh/authorized_keys
or, from the machine you work on, typing that machine's password once:
ssh-copy-id you@mac-mini.
On Windows, in PowerShell on that machine, as the user you will log in as:
New-Item -ItemType Directory -Force "$HOME\.ssh" | Out-Null; Add-Content -LiteralPath "$HOME\.ssh\authorized_keys" -Value 'ssh-ed25519 AAAA... you@laptop'
If that user is an administrator — most Windows logins are — Windows' sshd ignores the file above and reads one shared file instead. In an administrator PowerShell:
$f="$env:ProgramData\ssh\administrators_authorized_keys"; Add-Content -LiteralPath $f -Value 'ssh-ed25519 AAAA... you@laptop'; icacls $f /inheritance:r /grant 'SYSTEM:F' 'Administrators:F'
The icacls part matters: sshd silently ignores that file unless only SYSTEM and
Administrators can write it. Both mistakes — the wrong file, or the right file with the
wrong permissions — look the same from outside: the key is there and does not work. More
in Windows.
Rentals¶
vast.ai, RunPod, AutoDL and the like put the public key from your account on every instance they start. So add your key there once — in the provider's settings, under SSH keys — and every rental you start afterwards lets you in:
| Provider | Where |
|---|---|
| vast.ai | Account → SSH Keys; then recreate the instance, or add it on the instance |
| RunPod | Settings → SSH Public Keys; pods started after that accept it |
| AutoDL | 控制台 → 设置 → SSH 公钥, or add it on the instance |
Then add the rental with the ssh command the provider shows for it:
fleet add "ssh -p 40001 root@1.2.3.4"
An instance started before you added the key does not have it. Put it on that one by hand
(the Linux line in Windows or Mac, run on the instance), or start a new
one. When fleet recognises the provider, fleet doctor names its page.
More about rentals — cost, idle alerts, moving addresses — is in
Adding machines.
LAN or another VPN¶
A machine at home or in the office, or on ZeroTier, WireGuard, Netbird or a company VPN,
is reached as ssh reaches it. Put your key on it once (the lines above, or
ssh-copy-id user@host), and fleet adds it by any ssh command that works:
fleet add "ssh ubuntu@192.168.1.50"
A Host entry in ~/.ssh/config is the tidy way to remember a user, a port, a key or a
jump host, and fleet reads it just as ssh does:
Host nas
HostName 192.168.1.50
User admin
IdentityFile ~/.ssh/id_ed25519
fleet add nas
fleet does not create routes. Machines behind different NATs need a network between them first — a tailnet or another VPN — and then an address fleet can route to.
Finding what you can already reach¶
fleet ls --discover # or ask your agent: "what machines could I add?"
lists the machines you can probably reach but have not added yet, from two places:
- the
Hostentries in your~/.ssh/config, followingInclude(patterns such asHost *andlocalhostare skipped: they name no one machine); - the machines on your tailnet, from
tailscale status --json— only those running Linux, macOS, Windows or FreeBSD, since a phone has no sshd. It needs Tailscale running here, and no API token.
Each row says how to add it: fleet add NAME. Nothing listed is contacted until you add
it, and nothing is added by itself: a tailnet has phones and printers on it, and an ssh
config has hosts you have not used in years. An agent is told to offer, not to add them
all.
When a machine refuses¶
fleet add records a machine only if it lets fleet in. If it does not, nothing is
recorded, and fleet prints why and the fix, then asks you to run the same
fleet add again once it lets you in. For a machine already added that stops letting
fleet in, fleet ls shows it as auth_failed — up, but refusing, which is not the same
as off.
fleet doctor gpu-box # one machine, looked at now
fleet doctor # every machine whose last reading failed
fleet doctor gpu-box --json # `why` and `fix`, for an agent
fleet doctor connects once, listens to what the server says it is, and names the one
thing to do:
| What it finds | The fix it gives |
|---|---|
| your key is not accepted | the line that puts your key on it — for Linux and macOS, for a Windows user, and for a Windows administrator — or ssh-copy-id |
| you have no key at all | ssh-keygen -t ed25519, then run fleet doctor again |
| a rental without your key | the provider's account page for keys |
| Tailscale SSH does not allow you | a policy rule to add, or a user it does allow |
Tailscale SSH wants a browser sign-in (check) |
make the rule accept, or sign in once |
| ssh offered too many keys and the server gave up | name the key: fleet edit NAME --ssh "ssh ... -i ~/.ssh/THE_KEY" |
| nothing listens on the ssh port | how to start sshd on Linux, macOS and Windows |
| the host key changed | ssh-keygen -R HOST, once you know why it changed |
| nothing answered | check it is on and connected; fleet edit NAME --ssh if it moved |
It changes nothing: it only tells you. Applying the fix is up to you, because it is a change to who may log in to that machine.
Putting your key on it from here¶
At a terminal on Linux or macOS, when a machine refuses your key, fleet add offers to
run ssh-copy-id for you:
gpu-box does not accept your key yet.
Put your key on it with `-p 2222 ubuntu@10.0.0.7`? ssh-copy-id asks for its password once [Y/n]:
ssh-copy-id is the standard OpenSSH tool. It asks for the machine's password itself,
in your terminal, and appends your normal public key to its authorized_keys. fleet
only starts it and then tries again; it never sees what you type. It is not offered to an
agent (there is no one to type a password), with --json or --dry-run, when you have
no key yet, or where there is no ssh-copy-id — on Windows, fleet doctor prints the
line to run instead.
Passwords¶
fleet never asks for a password, and never stores one. It runs ssh in batch mode, so
ssh does not prompt either: a machine that only takes a password simply refuses. The one
time a password is typed is into ssh-copy-id, by you, to put your key on the machine —
after that, the key is what lets you in.
An agent is told never to type a password or accept one from you. If it needs a machine
that refuses, it runs fleet doctor and gives you the fix.
Each machine keeps its own list¶
The machines you add by hand are kept on the machine you added them from, in
machines.yaml in fleet's configuration folder (fleet paths shows where). A second
machine running fleet has its own list, of the machines its ssh reaches.
To use the same list on several machines, share the file the way you share your other settings — in your dotfiles, for example. It holds names, ssh commands, tags and costs, and no keys or passwords. Each machine still connects with its own ssh, so a machine that cannot reach one in the list shows it as not answering, and nothing worse.
What a machine says about itself — where it keeps models, its tags, its cost — lives on
that machine, in its machine.yaml, so every fleet that looks at it sees the same. See
What is on each machine.