Knowledge baseNetworks
Run your network on your own server
Step by step, without knowing commands: move a private network from mesh-net to a server you rent — the devices keep their addresses.
On this page
This guide moves a private network from mesh-net to a server that you rent and control. You don't need to know any commands: you copy ready-made lines into a window, and one script does the setup and asks you a few simple questions. Your devices keep their addresses, names and access rules; most of them don't even need to log in again.
The steps: give the server a name, download the network from mesh-net, copy it to the server, run the installer, switch your devices over. About half an hour in total. The way back, from your server to mesh-net, is in Move your headscale network to mesh-net.
What you need
- A server. A server is a computer that is always on and reachable from the internet. The easiest
is a VPS — a virtual server you rent from a hosting provider by the month. Choose:
- the system: best is Ubuntu 26.04 LTS or Ubuntu 24.04 LTS — the newest long-term releases (LTS means updates for years); Ubuntu 22.04, Debian 13 and Debian 12 work too. The script refuses any other system;
- the smallest size is enough: 1 processor core and 1 GB of memory;
- a public IPv4 address — a fixed address on the internet; providers call it “public IP” or “dedicated IP”, and a VPS usually comes with one;
- incoming TCP ports 80 and 443 open. Many providers don't block anything by default; others have a filter in the control panel called “firewall”, “security group” or “network rules” — allow ports 80 and 443 there.
- Access to the server. When the server is created, the provider shows its IP address and a user
name with a password (often
root) or asks you for an SSH key. Keep them at hand. - A computer with Windows 10 or 11, macOS or Linux: you'll type everything from it.
- About half an hour, ideally in one sitting: the devices switch over at the end, all together.
Step 1. A name for the server
Your devices will find the server by its name, like myserver.mesh-net.space. The name is linked to the
server's IP address by a DNS record of type A — an entry that tells the whole internet “this name lives
at this address”. Pick a new name: the network's current address belongs to the network in mesh-net and goes away with
it.
Type the name you are going to use and the server's IP address here — this page will put them into every command below (your browser remembers them for the next visit):
A name in mesh-net (free):
- Open Domains in the menu of mesh-net and press Add name.
- In Name type the first part of the name (
myserver), in Domain pick the zone, in Type chooseA. - In Value type the server's IP address:
SERVER-IP. Press Add.
The record works within a couple of minutes. If you already hold a name that has no records yet, open it on Domains and use Add record with the same type and value. More about names: Names and DNS records.
A name from Domains lives as long as your mesh-net account. On the Free plan you have to sign in at least once every 30 days; otherwise, after a warning and 7 more days, the account is deleted together with its names (Inactive free accounts) — and your devices lose their server. For a network that should live for years, use your own domain.
Your own domain: at your registrar or DNS provider, add a record of type A for a name like
vpn.example.com with the value SERVER-IP.
A server at home whose IP address changes from time to time: give it a name with dynamic DNS, which follows the address by itself — see Dynamic DNS; on your router, forward ports 80 and 443 to the server.
Step 2. Download the network
- Open the network's page in mesh-net, the Backup tab: Export / backup.
- Press Download the archive and confirm. Your browser saves a file named like
mesh-net-office-2026-10-06.tar.gz, usually into the Downloads folder.
The archive holds everything the server needs: the devices with their addresses, names and keys, the users, the routes and the access rules. Keep it like a password: whoever has it can pretend to be your network.
Do this step shortly before step 5. A device you add to the network in mesh-net after the download won't be in the archive — then download it again.
Step 3. Connect to the server
You'll work on the server through SSH — a way to type commands on a remote computer from a window on your own. That window is the terminal:
- Windows 10 and 11: open the Start menu, type
PowerShelland open Windows PowerShell (on Windows 11 the Terminal app is the same thing). The commandssshandscpare already built in; if Windows sayssshis not recognized, add “OpenSSH Client” in Settings → Optional features. - macOS: press Cmd+Space, type
Terminaland press Enter. - Linux: press Ctrl+Alt+T or find Terminal among the applications.
Paste this line into the terminal and press Enter (to paste: Ctrl+V in Windows, Cmd+V on macOS, Ctrl+Shift+V in Linux):
ssh root@SERVER-IP
- The first time, the terminal asks
Are you sure you want to continue connecting (yes/no/[fingerprint])?— typeyesand press Enter. It's the server introducing itself; it won't ask again. - Then it asks for the password. The password doesn't show while you type it — no dots, no stars. That's normal: type it (or paste it) and press Enter.
- You're in when the line starts with something like
root@myserver:~#. Everything you type now runs on the server. To leave, typeexit.
If the provider gave you another user name instead of root (for example ubuntu or
debian), write it instead of root here and in step 4 — the commands on the server already start
with sudo, which runs them with full rights and may ask for that user's password.
If you see Connection timed out, check the IP address; if Permission denied, the user name
or the password.
Step 4. Copy the archive to the server
Open a second terminal window — on your computer, not on the server — and paste the line for your system. It copies the archive from the Downloads folder to the server, asking for the server's password again.
macOS and Linux:
scp ~/Downloads/mesh-net-*.tar.gz root@SERVER-IP:
Windows (PowerShell):
scp (Resolve-Path "$HOME\Downloads\mesh-net-*.tar.gz") root@SERVER-IP:
You'll see the file name and 100%. If the browser saved the archive into another folder, put that folder
in place of Downloads.
You can also copy the file with a program with windows: WinSCP on Windows or Cyberduck on macOS. Connect the same way as with SSH (protocol SFTP, the server's IP address, the user name and the password) and drag the file into the folder that opens.
Step 5. Run the installer
Back in the terminal that is connected to the server, paste this line and press Enter:
curl -fsSL https://mesh-net.space/ext/mesh-net-selfhost.sh | sudo bash -s -- --lang en
If you'd like to check the script first, download it with curl -fsSLO https://mesh-net.space/ext/mesh-net-selfhost.sh:
sha256sum mesh-net-selfhost.sh should print
8b035de427c4244e04aac654a5503d7b4b3aff1dcbeff92b2b42fa0f2e0067f8.
The script first only checks the server, the archive and the name, and stops with an explanation if something is off — nothing is changed by then. It asks:
- which archive to take — only if it finds several; the newest is number 1;
- the server's name — type
SERVER-NAME; Start? (Y/n)— press Enter;- whether to open ports 80 and 443 in the server's own firewall — only if it is on; press Enter.
Then it installs headscale — the program that manages your network now — gets a free HTTPS certificate from Let's
Encrypt and loads your network. It usually takes a minute or two. In the end you'll see
Done. Your network now runs on https://SERVER-NAME, followed by the commands for
your devices (the next step) and a list of what was changed on the server. The script's log is in
/var/log/mesh-net-selfhost.log.
- If it stops with a message, do what the message says and run the same line again. Once it has started changing things, a second run continues where it stopped. The usual reasons are in If something goes wrong.
sudo: command not foundmeans you are alreadyroot: run the line withoutsudo:curl -fsSL https://mesh-net.space/ext/mesh-net-selfhost.sh | bash -s -- --lang encurl: command not found: install it withapt-get update && apt-get install -y curl(withsudoin front if you're notroot) and repeat.
Step 6. Move the devices
Until now your devices kept working through mesh-net. Now switch each of them to your server. A device keeps its address, name, keys and routes; no new login is needed.
Computers
Linux and macOS (with the tailscale command) — in a terminal on that computer:
sudo tailscale down && sudo tailscale up --login-server=https://SERVER-NAME --accept-routes
Windows — in PowerShell opened as administrator (Start → type PowerShell → right-click →
“Run as administrator”):
tailscale down; tailscale up --login-server=https://SERVER-NAME --accept-routes
- If it prints nothing, or only a warning, it worked:
tailscale statusshows the other devices. - If tailscale answers
changing settings via 'tailscale up' requires mentioning all non-default flagsand prints a longer command, the device was set up with extra settings — it's a gateway, an exit node, or has its own name. Copy that longer command and run it (withsudoin front on Linux and macOS): it keeps those settings. - Gateways — devices that give access to a whole local network or to the internet — move together with the devices that use them: while one side is on the old server and the other on the new one, their routes don't work.
Phones and the Tailscale apps
In the apps you add your server as a new account and log in to it. The steps below are from the documentation of Tailscale and headscale; we haven't tested the apps with this move ourselves.
- iPhone and iPad: in Tailscale tap the profile icon at the top right, then the account you're
signed in with; in the menu at the top right choose “Use a custom coordination server”, enter
https://SERVER-NAMEand tap “Log in”. - Android: profile icon → the signed-in account → the menu at the top right → “Use an alternate
server” →
https://SERVER-NAME→ “Log in”. - Mac (the app from the App Store or the website): Tailscale icon in the menu bar → Settings →
Accounts → the arrow at the bottom left → enter
https://SERVER-NAME→ “Add account”. - Windows: the app has no menu for this — use the PowerShell command above.
After you log in, a browser page shows a command like headscale nodes register --key … --user USERNAME.
Run it on the server as shown in Adding a new device, and the app connects. If
sudo headscale nodes list then shows the device twice — the old entry offline and a new one
online with another address — delete the old one with sudo headscale nodes delete -i ID, using
the number (ID) of the old entry, and answer y. Access rules that named this device by its old address
don't apply to the new entry. Finally remove the old account in the app's account list (press and hold it, or “Log
out”) — before step 7: once the network in mesh-net is deleted, the app can't log out of it cleanly. If an app can't
switch at all, uninstall it, install it again and log in to your server.
To see who has switched, run on the server:
sudo headscale nodes list
Moved devices show online, with the same addresses as before.
Step 7. Delete the network in mesh-net
When every device shows online on your server, delete the old network: on its page open the
Settings tab and press Delete in Delete network, then confirm (more in
Delete a network). Until then, devices that are still there keep a
second copy of the network.
Don't delete the name from step 1 in Domains: it is your server's address now. Without it, the devices can't find the server.
Adding a new device
A new device joins with a join key — a one-time code that lets it into the network. You make it on your server. First see the users of the network and their numbers (ID):
sudo headscale users list
Then make a key valid for 24 hours, with the user's number after --user (the installer's summary has
these lines ready for every user):
sudo headscale preauthkeys create --user 1 --expiration 24h
On the new device, with the key in place of KEY:
sudo tailscale up --login-server=https://SERVER-NAME --accept-routes --auth-key=KEY
On Windows — the same in PowerShell as administrator, without sudo.
A phone or an app logs in without a key: point it to
https://SERVER-NAME (see Phones and the Tailscale apps) and
log in. The browser shows a command with a key; run it on the server with sudo and the user's name (the
Username column of headscale users list) in place of USERNAME:
sudo headscale nodes register --key KEY-FROM-THE-PAGE --user alice
A new user is created with sudo headscale users create NAME. New devices get a random free address: an
address freed by a deleted device isn't handed to the next one right away, so access rules written for the old device
don't quietly apply to a new one.
Backups and updates
Backup. Everything about your network is in two folders on the server: /var/lib/headscale
(the database, the server's own key and the certificate) and /etc/headscale (the settings). These three
lines stop headscale for a few seconds, pack both folders into one file and start it again — the devices keep working
meanwhile:
sudo systemctl stop headscale
sudo tar -czf /root/headscale-backup-$(date +%F).tar.gz /etc/headscale /var/lib/headscale
sudo systemctl start headscale
tar may note Removing leading `/' from member names — that's normal. Then copy the backup
to your computer — in a terminal on the computer:
scp root@SERVER-IP:/root/headscale-backup-*.tar.gz .
Keep it like the archive from step 2. To bring the network back, put both folders back on a server with the same name; mesh-net can also import the database again (Move your headscale network to mesh-net).
Updates. The installer sets up headscale 0.28.0. New
versions come out from time to time; to update, follow the headscale guide
Upgrade an existing installation: make a
backup first, go one version at a time, install the new .deb from
headscale releases, and when it asks about
/etc/headscale/config.yaml, keep your version. The access rules came with the network; to change them now,
see headscale's ACL guide.
If something goes wrong
“does not point to this server”
The name from step 1 has no A record yet, or it leads to another address. Open the name on Domains and
check that the record of type A has the value SERVER-IP. A new record needs a minute
or two; then run the installer again. If the server sits behind a router or NAT, the script asks whether to go on:
answer y if ports 80 and 443 of that address lead to your server.
“Port 443 is taken” or the ports are closed
A program already uses port 80 or 443 on the server (the message names it): take a clean server. If the script ends with “does not answer with a valid certificate”, Let's Encrypt couldn't reach the server — open incoming TCP 80 and 443 in the provider's control panel; if the server's own firewall is on and you said no to opening them:
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
Then run the installer again.
The certificate isn't issued
The certificate comes from Let's Encrypt for the name from step 1, and it needs both the name and port 80 to be
right. Let's Encrypt allows only a few failed attempts per name per hour: if you've run the installer several times in a
row, fix the cause, wait an hour and run it again. The installer's message shows what headscale said; more in the log
/var/log/mesh-net-selfhost.log.
“can't change --login-server without --force-reauth”
The device was still connected when you switched the server: run the whole line from step 6 — it starts with
tailscale down. Don't add --force-reauth: it makes the device log in again as a new one.
A device stays offline
- Check that the command from step 6 ran on it without errors;
tailscale statuson the device shows whether it is connected. - Open
https://SERVER-NAME/healthin a browser on that device: it should show{"status":"pass"}. If it doesn't open, the device can't reach the server — check the name and the ports. - On the server,
sudo systemctl status headscaleshows whether headscale runs, andsudo journalctl -u headscale -n 50— its latest messages.failed to get DERPMapmeans the server can't reachcontrolplane.tailscale.com, which headscale needs at start.