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.
    Use a clean server: nothing else may use ports 80 and 443 on it, so no website or other web service.
  • 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):

  1. Open Domains in the menu of mesh-net and press Add name.
  2. In Name type the first part of the name (myserver), in Domain pick the zone, in Type choose A.
  3. 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.

How long a mesh-net name lives

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

  1. Open the network's page in mesh-net, the Backup tab: Export / backup.
  2. 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 PowerShell and open Windows PowerShell (on Windows 11 the Terminal app is the same thing). The commands ssh and scp are already built in; if Windows says ssh is not recognized, add “OpenSSH Client” in Settings → Optional features.
  • macOS: press Cmd+Space, type Terminal and 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])? — type yes and 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, type exit.

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 found means you are already root: run the line without sudo:
    curl -fsSL https://mesh-net.space/ext/mesh-net-selfhost.sh | bash -s -- --lang en
  • curl: command not found: install it with apt-get update && apt-get install -y curl (with sudo in front if you're not root) 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 status shows the other devices.
  • If tailscale answers changing settings via 'tailscale up' requires mentioning all non-default flags and 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 (with sudo in 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-NAME and 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.

Keep the name

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 status on the device shows whether it is connected.
  • Open https://SERVER-NAME/health in 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 headscale shows whether headscale runs, and sudo journalctl -u headscale -n 50 — its latest messages. failed to get DERPMap means the server can't reach controlplane.tailscale.com, which headscale needs at start.