Connecting to my home PC from the mobile phone

I have an always-on PC at home, and it would be nice to connect to it to watch movies, sync photos, and other things for which you'd normally use a cloud drive. Unfortunately, my ISP doesn't provide static IP addresses, so a simple VPN setup was out of the equation.

After googling for a bit, I've found that Tailscale gives you a way to connect between devices that don't have "direct" access to each other (as in, both are behind a NAT). It handles all the hassles of configuring Wireguard tunnels, plus uses technology similar to what Zoom or Google Meet use to achieve fast and seemingly direct connections. It uses NAT traversal tricks and STUN/TURN. To put simply - it just works like a "magic tether".

The only thing that I didn't like about this is that the server software is controlled by Tailscale and is closed source. But just recently I've learned that there's an open-source server reimplementation called Headscale which is a drop-in replacement, and which works with regular Tailscale clients (both desktop and mobile). It is something you can self-host on a VPS, and is very light on configuration.

I've set aside 2 hours last Sunday to get it set up, and expected to spend all of it on debugging weird issues. It was surprising that in fact there were almost none. Here's my NixOS configuration for headscale (server side):


services.headscale = {
  enable = true;

  settings = {
    server_url = "https://headscale.knazarov.com:443";
  };
};

services.nginx = {
  enable = true;

  ...

  virtualHosts = {
    "headscale.knazarov.com" = {
      enableACME = true;
      forceSSL = true;
      locations."/" = {
        proxyPass = "http://127.0.0.1:8080";
        extraConfig = ''
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection $connection_upgrade;
          proxy_set_header Host $server_name;
          proxy_buffering off;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
        '';
      };
    };
  };
};

And that's it! As you can see, it's mostly just configuring nginx proxying because I have other things running on the same server. If you only need to run headscale, you can expose it without nginx, and your configuration would consist of only one file with a few lines.

Now with this setup, I can access my Jellyfin or Syncthing on the go, without having to do anything special. Tailscale just runs in the background on my phone and provides a secure persistent tunnel to the home machine.