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.

  1. Connect to your server from your computer.
  2. Pack the network into one file — one line, the script does the rest.
  3. Download the file to your computer.
  4. Upload it to mesh-net.
  5. Move the devices — two short commands on each.
  6. 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 PowerShell and open Windows PowerShell (on Windows 11 the “Terminal” app works too). The ssh and scp commands this guide uses are already there.
  • macOS: press Cmd+Space, type Terminal and 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])? — type yes and 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, type exit.
  • 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 recognized on 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:

  1. Pack it into /root/mesh-net-import-….tar.gz? [Y/n] — press Enter.
  2. 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.
  3. 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.

Keep the file like a password

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

  1. 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.
  2. In Address of the new network pick the name that becomes the network's address.
  3. 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.
  4. 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 status says Logged out or 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.