Recently I decided I wanted to run my own Bitcoin and Lightning node and I wanted it to be reachable on the public internet. I didn’t, however, want it to actually reside on the server that has the static public IPv4 and IPv6 addresses available. Thus, a reverse proxy was needed. This turned out to be a pretty simple thing to solve for thanks to the Nginx Stream Proxy module and Tailscale. Here’s the basic architecture:
- Nginx on a virtual private server (VPS) at Hetzner listens on ports 8333 & 9735 for TCP connections
- The stream proxy module forwards those connections to bitcoind and lnd over Tailscale
- The server running bitcoind and lnd uses the Hetzner VPS as a Tailscale exit node so that all outbound traffic is via the VPS
Here’s a technical breakdown of how I make that happen. My configuration is done via NixOS flakes, but the general process would work on anything using Nginx and Tailscale.
{ config, username, ... }: let
domain = "example.com";
private_btc = "some-host.your-domain.ts.net";
in {
networking.firewall.allowedTCPPorts = [
8333 # Bitcoin Core
9735 # LND
];
services = {
nginx = {
enable = true;
streamConfig = ''
server {
listen 0.0.0.0:8333;
listen [::]:8333;
proxy_pass ${private_btc}:8333;
}
server {
listen 0.0.0.0:9735;
listen [::]:9735;
proxy_pass ${private_btc}:9735;
}
'';
}; # end nginx
tailscale = {
enable = true;
authKeyFile = config.sops.secrets.tailscale_key.path;
extraUpFlags = [
"--advertise-exit-node"
"--operator"
"${username}"
"--ssh"
];
useRoutingFeatures = "both";
}; # end tailscale
}; # end services
sops = {
age.keyFile = "${config.users.users.${username}.home}/.config/sops/age/keys.txt";
defaultSopsFile = ../secrets.yaml;
secrets = {
tailscale_key = {
restartUnits = ["tailscaled-autoconnect.service"];
};
};
}; # end sops
}
Breaking that down a little:
-
networking.firewall.allowedTCPPorts
opens the firewall ports needed for bitcoind and lnd -
services.nginx
configures twongx_stream_proxy_module
instances within thestreamConfig
section that route traffic to the backend using the dns name from Tailscale -
services.tailscale
enables Tailscale on the VPS and configures it as an exit node. -
sops
configures SOPS to securely store secrets
And that’s it on the VPS. For the backend, you could be running a variety of different options from Umbrel to Nix Bitcoin to the services manually configured on a variety of operating systems. Settings those up is best left to a different post, but the keys that relates to this setup are:
- that where ever they run uses the VPS as an exit node
- the services listen on for connections incoming via Tailscale
- the services advertise the IP of the VPS as their public address
Note: the links to Hetzner and Tailscale in this post are referral / affiliate links. The Hetzner one is mine and the Tailscale one is from Jupiter Broadcasting’s Linux Unplugged podcast. I’ve used the JB link because Chris Fisher, Alex Kretzschmar, Brent Gervais, & Wes Payne have taught me about much of what’s here through their podcasting.
Top comments (0)