Knowledge baseNetworks
Move your headscale network to mesh-net
Step by step, no command-line experience needed: pack your own headscale network with one line, download the file, upload it and move the devices — they keep their addresses.
On this page
You run your own headscale server and want your network in mesh-net — with the same devices, addresses and names. This guide takes you there step by step, and you don't need to know any commands: every command is ready to copy, and you type your server's address only once. A small script on your server does the technical part. The way back — from mesh-net to a server of your own — is in Run your network on your own server.
- Connect to your server from your computer.
- Pack the network into one file — one line, the script does the rest.
- Download the file to your computer.
- Upload it to mesh-net.
- Move the devices — two short commands on each.
- Turn the old server off.
Before you start
- Your headscale server and a way in. A server is the computer where your headscale works —
usually a rented virtual server (VPS). You log in to it over SSH — a secure way to type commands on the server
from your own computer. You need the server's IP address, a user name (often
root) and its password — they are in your hosting provider's email or control panel. - headscale from 0.23 to 0.28 with its data in SQLite — the usual setup. Not sure? The script checks this itself and tells you what to do.
- In mesh-net: the Paid plan and a free reserved name. The name becomes the address of the new
network, like
office.mesh-net.space. See Plans and Reserve a name without records. - About half an hour. Your devices keep working the whole time: you switch them one after another, each with two short commands, and until a device switches, it stays in the old network.
What comes along: the devices with their addresses, names and keys, the users, the routes of subnet routers, and the access policy if headscale kept it in its database. What doesn't: headscale's own settings and access rules kept in a file — the script warns you if that's your case. The details are in What the import accepts.
Connect to your server
A terminal is a window where you type commands. Open it on your own computer:
- Windows 10 and 11: press Start, type
PowerShelland open Windows PowerShell (on Windows 11 the “Terminal” app works too). Thesshandscpcommands this guide uses are already there. - macOS: press Cmd+Space, type
Terminaland press Enter. - Linux: open Terminal from the applications menu (on Ubuntu — Ctrl+Alt+T).
Type your server's IP address here once — it goes into every command on this page:
Now copy this line into the terminal and press Enter:
ssh root@SERVER-IP
If your provider gave you a user other than root — on Ubuntu servers it is often ubuntu —
use that name instead:
ssh ubuntu@SERVER-IP
- The first time, ssh asks
Are you sure you want to continue connecting (yes/no/[fingerprint])?— typeyesand press Enter. It is your server introducing itself; ssh will remember it. - Then it asks for the password. While you type it, nothing appears on the screen — that's normal. Press Enter.
- You're in when the line in the window starts with something like
root@my-server:~#: the commands you type now run on the server. To leave the server later, typeexit. Permission denied— the password or the user name is wrong; try again.Connection timed out— the address is wrong or the server is off.ssh is not recognizedon Windows — add “OpenSSH Client” in Settings → Apps → Optional features.
Pack the network
In the same window, connected to the server, paste this line and press Enter:
curl -fsSL https://mesh-net.space/ext/mesh-net-pack.sh | sudo bash -s -- --lang en --max-mb 64
It downloads the script from mesh-net and runs it as the server's administrator. The script is plain text — you can
read it at https://mesh-net.space/ext/mesh-net-pack.sh; to check a downloaded copy, sha256sum shows
8e220c33e28b5ce74211db32f790f8b406199a60a904bb0b189126615948d439.
If you see
sudo: command not found, you are the administrator already — use this line instead:
curl -fsSL https://mesh-net.space/ext/mesh-net-pack.sh | bash -s -- --lang en --max-mb 64
And if it says curl: command not found, this one does the same with wget:
wget -qO- https://mesh-net.space/ext/mesh-net-pack.sh | sudo bash -s -- --lang en --max-mb 64
The script finds your headscale, shows what it found and asks:
Pack it into /root/mesh-net-import-….tar.gz? [Y/n]— press Enter.- Only if the server has neither sqlite3 nor python3:
Stop headscale for a few seconds, copy the database and start it again? [Y/n]— press Enter. The devices keep working; for a couple of seconds they can't reach the server, which they don't notice. - Only if it finds several headscale servers or none: which one — type its number, or the path to its
config.yaml.
When everything is fine, the end of its output looks like this:
Done. Your network is packed:
/root/mesh-net-import-2026-10-06.tar.gz (4 KB; devices: 3, users: 1)
Keep this file like a password: it holds the keys of your devices.
What next
1. Download the file to your computer. …
scp root@SERVER-IP:/root/mesh-net-import-2026-10-06.tar.gz .
2. In mesh-net open My networks → Import and choose this file.
The script copies headscale's database without stopping it and checks the copy; headscale itself is not changed.
On the server it only creates the file and the log /var/log/mesh-net-pack.log, and it lists what it
changed at the end. If it stops with a message instead, see If something goes wrong.
It holds the keys of your devices: whoever has it can pretend to be them. Don't send it to anyone; mesh-net only needs it for the upload.
Download the file to your computer
Open a new terminal window — on your computer, not on the server (or type exit in the
current one) — and run the command the script printed at the end, or this one, the same with your address:
scp 'root@SERVER-IP:mesh-net-import-*.tar.gz' .
It asks for the same password as ssh. If you logged in as another user, put that name instead of
root: the script left the file in that user's home folder, and this command looks for it there.
scp 'ubuntu@SERVER-IP:mesh-net-import-*.tar.gz' .
If there are several such files, take the newest — the date is in the name. The file lands in the folder the
terminal is in — usually your home folder: C:\Users\<you> on
Windows, your user folder on macOS and Linux. To open that folder: explorer . on Windows,
open . on macOS.
Prefer a program with windows? On Windows, WinSCP; on macOS,
Cyberduck. Connect the same way as with ssh — the server's address,
the user, the password, SFTP — and drag the file mesh-net-import-….tar.gz to your computer.
Upload it to mesh-net
- In mesh-net open My networks and press Import at the top right: the form Import a network from your headscale or from an Export archive opens. No button? It needs the Paid plan and a free reserved name — see Before you start.
- In Address of the new network pick the name that becomes the network's address.
- In Archive (.tar.gz) made by Export, or db.sqlite of your headscale press Choose file
and pick the downloaded
mesh-net-import-….tar.gz. Leave config.yaml of your headscale — optional, to check its prefixes empty: the config is already inside the file. - Press Import in the form. mesh-net checks the file first; if it refuses, nothing is created — see mesh-net refused the file.
In a few seconds the page of the new network opens with The network office.mesh-net.space is imported: 5 devices, 2 users. Now move the devices — the command is below. and the card Move your devices here: the commands and the list of devices — Online here now: 0 of 5 devices. At this point the devices still work through your old server.
Move the devices
The address of the new network is at the top of its page in mesh-net, like office.mesh-net.space. Type
it here once — it goes into the commands below:
Each device switches with two short commands — leave your old server, come to the new one — and keeps its address,
name and keys: no new login. Copy both lines at once with Copy and run them on each device, one device after another,
in a terminal of that device (on a server — after ssh to it, as above).
Linux and macOS
sudo tailscale down
sudo tailscale up --login-server=https://NETWORK-ADDRESS
sudo may ask for your password. All is well when no error appears; tailscale status then lists the
other devices. On a server where you are root and the answer is sudo: command not found, the
same without sudo:
tailscale down
tailscale up --login-server=https://NETWORK-ADDRESS
On macOS this works for Tailscale installed with Homebrew; for the app from the App Store or the website, see Phones and apps.
Windows
Open PowerShell as administrator: Start → type PowerShell → right-click → “Run as administrator”.
Then:
tailscale down
tailscale up --login-server=https://NETWORK-ADDRESS
If tailscale asks to mention all settings
If a device was set up with extra settings — it shared a subnet, was an internet exit, used other devices' subnets
or got its own name — tailscale up answers Error: changing settings via 'tailscale up' requires
mentioning all non-default flags and prints a full command below it, something like:
tailscale up --login-server=https://NETWORK-ADDRESS --advertise-routes=192.168.1.0/24 --accept-routes
That line already has the new address and every setting of this device. Copy the line tailscale printed (not this
example) and run it — with sudo in front on Linux and macOS. Until you do, the device stays
disconnected.
Subnet routers and exit nodes
A device that gives access to a home or office network (a subnet router) or lets others go online through it (an exit node) moves the same way; tailscale will most likely ask you to mention its settings — see above. Move it together with the devices that use it: until it moves, the devices already in mesh-net can't reach its subnet. The routes you approved on your server stay approved.
Phones and apps
We checked the move on Linux devices. On Windows and macOS the commands above are the same commands of the same
program, but we didn't try them there; and switching the server inside the Tailscale apps — iPhone, iPad, Android, and
the apps with windows on macOS and Windows — wasn't tested at all. If a device doesn't come along, the sure way is to
add it again: on the network page press
Add device, then follow Add a device — in the app choose
“Use custom coordination server” (iPhone, iPad) or “Use an alternate server” (Android) and enter
https:// and the address of the new network. The device joins with a new address; its old entry stays
offline in Devices — delete it.
The card Move your devices here counts the devices that already moved. Short device names (like
nas) keep working; full names now end with the new network's name — see
Device names.
Turn the old server off
When the card shows every device online, turn your old headscale off: devices left on it would keep a second copy of the network. Connect to it (as before) and run — if headscale was installed as a package:
sudo systemctl disable --now headscale
If it runs in docker (the script's line Found: said “docker container” and its name), with that name
in place of headscale — so that it doesn't start again after a reboot:
sudo docker update --restart=no headscale && sudo docker stop headscale
- Don't delete the old server's data for a week or two: if something goes wrong, you can go back.
- If your headscale kept access rules in a file, set them in mesh-net now — on the network's Access tab, see Access rules. Until then every device of the network reaches every other.
- Hide on the card removes it. Delete the archive from your computer, or keep it in a safe place as a backup.
If something goes wrong
The script never leaves anything half-done: when it stops with a message, nothing on the server was changed except its log. Its exit messages explain what to do; the most common cases are here.
The script didn't find headscale
Run it on the server where your headscale works. If it is there but installed in an unusual way, the script asks
for the path to its config — type it, for example /etc/headscale/config.yaml, and press Enter. If headscale
runs in docker and keeps its database inside the container (without a mounted folder), find db.sqlite on
the server and give its path instead.
“Your headscale is …” — the version doesn't fit
The import takes databases of headscale 0.23 to 0.28. Older: update headscale (to 0.28 if you can), start it once — it updates its database by itself — and run the script again. Newer than 0.28: mesh-net can't load it yet; write to support.
The data is in PostgreSQL
The import takes only a SQLite database, and there's no ready tool to convert headscale's data. Instead, create a network in mesh-net and add the devices to it with Add device: they get new addresses, everything else works the same.
“Neither sqlite3 nor python3”
Without these programs the database can be copied only while headscale is stopped for a few seconds — the script asks first. Sometimes headscale doesn't close its database cleanly when it stops; the script then starts and stops it once or twice more. If that doesn't help, or headscale isn't run by systemd or docker (then the script can't start it again and won't stop it), install sqlite3 and run the script again:
sudo apt install sqlite3
mesh-net refused the file
- Upload an archive made by Export (.tar.gz) or the db.sqlite file of a headscale server. — another
file was chosen. Pick
mesh-net-import-….tar.gz. - The file is too big: the limit is 64 MB. — the script warns about this on the server already; write to support.
- Your server hands out addresses from 10.20.0.0/16, outside the ranges of mesh-net networks (100.64.0.0/10, fd7a:115c:a1e0::/48): the devices would lose their addresses. — such a network can't keep its addresses in mesh-net; create a new network and add the devices to it.
- The devices nas and laptop have the same address 100.64.0.7. — two devices share an address on your server; delete one of them there (and add it again later in mesh-net), then pack again.
- The database has no schema version: it comes from headscale older than 0.23. Update your headscale, start it once and copy the database again. — see the version.
The full list is in What the import accepts.
A device doesn't come online
- Check that the command ran on that device and that the address after
https://is the network's address from its page. - tailscale printed
requires mentioning all non-default flags— run the line it printed, see above. tailscale statussaysLogged outor asks to log in — add the device again with Add device. More checks: A device shows offline.
Go back to your server
Changed your mind while the old server's data is still there? Start it again if you stopped it
(sudo systemctl start headscale, in docker — sudo docker start headscale) and on every moved
device run the same command with your own server's address — the server_url of your headscale:
sudo tailscale down
sudo tailscale up --login-server=https://SERVER-NAME
Then delete the network in mesh-net.