Guide - Public BTCPayServer with Umbrel & Tailscale

Guide - Public BTCPayServer with Umbrel & Tailscale

How to set up a public BTCPayServer with Umbrel and Tailscale.

What you will do in this guide

  • Install Tailscale & BTCPayServer on Umbrel
  • Make a Lunanode VPS to relay trafic from a domain to your umbrel
Thanks must go to the BTCPayServer team whose documentation is used verbatim in this guide.


Tailscale connects your online devices together using end to end encryption. It can be used to connect your Umbrel bitcoin node to a cheap VPS, serving your BTCPayServer at your chosen domain.

Tailscale Account

Sign up to Tailscale at

Warning: This guide uses the Tailscale web UI at with a network controller run by Tailscale Inc. If this controller gets compromised it could edit your access control list. This would allow new devices to be added to your private network which would have access to your devices. If you can modify your network Wireguard is a more sovereign / trustless option.

It is recommended you toggle Manually approve new devices on in the settings.

Turn this on


Tailscale App

Install the Tailscale app from the Umbrel app store. Open it and follow the setup (login with your GitHub username and password).

Install Tailscale from the Umbrel App Store

BTCPayServer App

Log into your Umbrel and install BTCPayServer from the Umbrel app store then:

# ssh into your umbrel 
ssh umbrel@umbrel.local    (or the IP of your Umbrel server)      
# Password is the same as Umbrel Web UI

# Edit the .env.app_proxy file
nano ~/umbrel/app-data/btcpay-server/.env.app_proxy

# Enter the following into the file

# Save using:
Control + X, then: Y, then: <enter>

# Restart BTCPayServer
~/umbrel/scripts/app restart btcpay-server


Sign up for LunaNode and add credit to your account with Bitcoin (Onchain & Lightning are both accepted).

Create a new virtual machine, an m.1s is enough. Select the latest Debian template.

Refresh the page until your Debian VM status is online.

To log in you need the data in the red boxes above in for format username@externalIP (debain@ and the password in the green box.

# SSH into the VPS
ssh username@externalIP

# switch to root user (if not logged in as root)
sudo su -

# Response will ask for (yes/no/[fingerprint])
yes <enter>

# Response will ask for password
# Enter the password from the green box above 
# The password will be invisible while typing / after pastig

# install the required dependencies
sudo su
apt update
apt install -y certbot nginx 

Install Tailscale

# Download Tailscale for debian
curl -fsSL | sh

# Wait for "Installation complete! ..."
sudo tailscale up

# Copy authentication URL into browser
Click Connect. If you enabled manual aproval you will need to click approve.

Configure your Domain

Open your domain registrar and edit the DNS records for your domain to include an A-Record from you domain, pointing to the server’s External IP address.  

Prepare SSL and Let's Encrypt

# Become root
sudo su

# generate 4096 bit DH params to strengthen the security
# This may take a while, make a coffee 
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

# create directory for Let's Encrypt files
mkdir -p /var/lib/letsencrypt/.well-known
chgrp www-data /var/lib/letsencrypt
chmod g+s /var/lib/letsencrypt

nginx map.conf

# Create a variable mapping to forward the correct protocol setting and check if the Upgrade header is sent by the client

nano /etc/nginx/conf.d/map.conf

# Enter the snippet in the code block below 

# Save using:
Control + X, then: Y, then: <enter>
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
  default $http_x_forwarded_proto;
  ''      $scheme;

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;

nginx btcpayserver.conf (http)

# Create a config file for the domain
nano /etc/nginx/sites-available/btcpayserver.conf

# Enter the snippet in the code block below 

# Save using:
Control + X, then: Y, then: <enter>
After copying the code block below replace {} with your domain.

i.e. server_name {}; would become server_name;
server {
  listen 80;
  server_name {};

  # Let's Encrypt verification requests
  location ^~ /.well-known/acme-challenge/ {
    allow all;
    root /var/lib/letsencrypt/;
    default_type "text/plain";
    try_files $uri =404;

  # Redirect everything else to https
  location / {
    return 301 https://$server_name$request_uri;

# Enable the web server config by creating a symlink
ln -s /etc/nginx/sites-available/btcpayserver.conf /etc/nginx/sites-enabled/btcpayserver.conf

# restart nginx:
systemctl restart nginx

# Test your Nginx config
service nginx configtest 

# Reload the config 
service nginx reload

SSL cert

Run certbot

Replace {} with your email and {} with your btcpayserver domain.

i.e. certbot certonly --agree-tos --email --webroot -w /var/lib/letsencrypt/ -d
certbot certonly --agree-tos --email --webroot -w /var/lib/letsencrypt/ -d

# You should get a response that includes "Congratulations! Your certificate and chain have been saved at..."

nginx btcpayserver.conf (https)

# Edit the btcpayserver.conf file to add the https server 
nano /etc/nginx/sites-available/btcpayserver.conf

# Add the snippet in the code block below to the end of the file

# Save using:
Control + X, then: Y, then: <enter>
After copying the code block below replace {} with your btcpayserver domain.

Also replace {umbrel-tailscale-ip:port} with your umbrel tailscale IP address : btcpayserver port number.

i.e. proxy_pass {umbrel-tailscale-ip:port}; would become proxy_pass;
server {
    listen 443 ssl http2;

    # Replace {} with your domain
    server_name {};
    ssl_certificate /etc/letsencrypt/live/{}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/{}/privkey.pem;
    # Replace {umbrel-tailscale-ip:port} with your details 
    # i.e.
    location / {
      # URL of BTCPay Server 
      proxy_pass {umbrel-tailscale-ip:port};
      proxy_set_header Host $http_host;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Upgrade $http_upgrade;
      proxy_http_version 1.1; # ensure replies through domain
      # For websockets (used by Ledger hardware wallets)
      proxy_set_header Upgrade $http_upgrade;

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    # Disable SSL and old TLS versions
    ssl_protocols TLSv1.2 TLSv1.3;
    # Use Diffie-Hellman (DH) key exchange parameters
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    # intermediate configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;
Settings informed by mozilla's ssl config
# You should still be root, if not enter sudo su

# Test your Nginx config
service nginx configtest 

# Expected result: Testing nginx configuration:.
# If testing fails check {umbrel-tailscale-ip} incudes http://

# Reload the config 
service nginx reload

# Restart nginx
systemctl restart nginx

Your domain should now be working.

If you got value from this guide consider sending some sats