6 min read

Installing the NGINX reverse proxy with an SSL certificate for Umbrel / BTCPay Server

Stein from BuidlBuidl wanted to expose his Umbrel node on the internet to accept Bitcoin payments on his soon-to-be-launched website. He reached out to me and asked if I could create a manual to help him navigate the complex procedure of setting up a reverse SSL proxy.
Installing the NGINX reverse proxy with an SSL certificate for Umbrel / BTCPay Server
Run your personal Bitcoin and Lightning Network node, self-host open source apps, cut out the middlemen, and use Bitcoin to its full potential. For free. — Umbrel

Update 2023 May 23th: This guide has been used very often and has since been online for nearly two years. A lot has changed in Umbrel, and people have reached out to me that it isn't compatible anymore. Please use this guide as a general direction to proceed into, but it shouldn't be used as direct instructions anymore.


  • A fully installed Umbrel node;
  • The BTCPayServer app is enabled;
  • You have a domain name configured for your website.

For this manual, I will use the following values for examples;

  • The home IP is;
  • The internal IP (of Umbrel) is;
  • The domain name is jorijn.com;
  • The desired domain for BTCPay Server is btcpay.jorijn.com.

Step 1: Pointing the domain name to your home IP address

Navigate to the control panel your domain owner offers. You should edit its DNS zone and add a new record:

DNS zone editor

Note: The changes could take up to 24 hours to propagate throughout the internet. It would be best if you only continued with this manual when the update is visible.

Step 2: Verifying the DNS change from step 1

You can use this online tool to check if the DNS update has propagated throughout the internet.


Step 3: Adding a port forwarding to your local router

To accept payments and issue an SSL certificate to your domain, your Umbrel should be partially reachable from the internet. Therefore, we need to open up specific ports on your internet router. The specifics depend on the make and model of your internet router.

First, you need to find out what the internal IP address is of your Umbrel node. Using SSH:

umbrel@umbrel:~ $ ip addr show wlan0
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether dc:a6:32:a4:91:8c brd ff:ff:ff:ff:ff:ff
    inet brd scope global dynamic noprefixroute wlan0

In this case, is the internal IP address.

Note: This Umbrel installation is connected using WiFi; therefore, the interface name is wlan0. If your Umbrel node is connected using an ethernet cable, the interface name should be eth0.

Next, create two port forwardings in your internet router. The router in this illustration uses a mobile app;

Screenshot of (example) the router management interface

You need the following port forwardings:

    IP address:
    Source port: 80
    Destination port: 15080
    Protocol: TCP
    IP address:
    Source port: 443
    Destination port: 15443
    Protocol: TCP

Step 4: Installing NGINX & Certbot

Before installing, update your package repository list.

umbrel@umbrel:~ $ sudo apt update
[sudo] password for umbrel:
Hit:1 http://deb.debian.org/debian buster InRelease
Get:2 http://deb.debian.org/debian-security buster/updates InRelease [65.4 kB]
Hit:3 http://archive.raspberrypi.org/debian buster InRelease
Get:4 http://deb.debian.org/debian buster-updates InRelease [51.9 kB]
Hit:5 https://download.docker.com/linux/debian buster InRelease
Fetched 117 kB in 2s (65.9 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
6 packages can be upgraded. Run 'apt list --upgradable' to see them.

Then, install the required components.

umbrel@umbrel:~ $ sudo apt install python3-acme python3-certbot python3-mock python3-openssl python3-pkg-resources python3-pyparsing python3-zope.interface python3-certbot-nginx nginx
Reading package lists... Done
Building dependency tree
Reading state information... Done
python3-pkg-resources is already the newest version (40.8.0-1).
python3-pkg-resources set to manually installed.
The following additional packages will be installed:
  certbot libgd3 libnginx-mod-http-auth-pam libnginx-mod-http-dav-ext libnginx-mod-http-echo libnginx-mod-http-geoip libnginx-mod-http-image-filter libnginx-mod-http-subs-filter libnginx-mod-http-upstream-fair libnginx-mod-http-xslt-filter
  libnginx-mod-mail libnginx-mod-stream libpython-stdlib libpython2-stdlib libpython2.7-minimal libpython2.7-stdlib libxpm4 libxslt1.1 nginx-common nginx-full python python-minimal python-pyicu python2 python2-minimal python2.7 python2.7-minimal
  python3-configargparse python3-configobj python3-future python3-josepy python3-parsedatetime python3-pbr python3-requests-toolbelt python3-rfc3339 python3-tz python3-zope.component python3-zope.event python3-zope.hookable

The installation will fail; this is expected behavior. It is happening because Umbrel is already claiming port 80. Therefore, we need to change this in the configuration and finish the installation.

umbrel@umbrel:~ $ sudo sed -i 's/80 default_server/15080/g' /etc/nginx/sites-available/default

Then, finish the installation.

umbrel@umbrel:~ $ sudo apt install -f

After this, you should see a running NGINX welcome page on

Step 5: Creating the BTCPay Server configuration for NGINX

Create a new configuration file:

umbrel@umbrel:~ $ sudo nano /etc/nginx/sites-available/btcpay

Paste in the following contents:

proxy_buffer_size          128k;
proxy_buffers              4 256k;
proxy_busy_buffers_size    256k;
client_header_buffer_size 500k;
large_client_header_buffers 4 500k;
http2_max_field_size       500k;
http2_max_header_size      500k;

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

server {
    server_name btcpay.jorijn.com;

    location / {
        proxy_set_header Host $host;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

    listen 15080;
    listen [::]:15080;

Note: Pay attention to changing the desired domain name in this configuration file.

Save the file (CTRL+O) and exit the editor (CTRL+X).

Then, enable the configuration:

umbrel@umbrel:~ $ sudo ln -s /etc/nginx/sites-available/btcpay /etc/nginx/sites-enabled/

Test the validity of the configuration using:

umbrel@umbrel:~ $ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Then, reload the configuration.

umbrel@umbrel:~ $ sudo systemctl reload nginx.service

Step 6: Request a new SSL certificate from LetsEncrypt

Request a new certificate from LetsEncrypt.

umbrel@umbrel:~ $ sudo certbot --nginx -d btcpay.jorijn.com -m [email protected] --agree-tos --tls-sni-01-port 15443 --http-01-port 15080

Note: Make sure to replace [email protected] and btcpay.jorijn.com with your own email address and domain.

When Certbot asks you about redirecting, choose 1: No redirect.

Step 7: Manually add the HTTP-redirect

The goal of this step is to force users to use our new, secure, connection to BTCPay Server. When you add this block, all traffic headed for the non-secure port 15080 will be redirected through HTTP to secure port 15443.

Open up the configuration file again:

umbrel@umbrel:~ $ sudo nano /etc/nginx/sites-available/btcpay

Then, at the end of the file, add this server block:

server {
    if ($host = btcpay.jorijn.com) {
        return 301 https://$host$request_uri;

    listen 15080;
    listen [::]:15080;

    server_name btcpay.jorijn.com;
    return 404;

Note: Replace the two occurrences of btcpay.jorijn.com.

Then, validate & reload the configuration:

umbrel@umbrel:~ $ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
umbrel@umbrel:~ $ sudo systemctl reload nginx.service

Your BTCPay Server should now be accessible using SSL at: https://btcpay.jorijn.com/.

Screenshot of the BTCPay Server login page on our new domain

The Pay Button

BTCPay Server will now intelligently use the requesting domain and protocol to generate the example code, and you're off to accepting payments on your website.

Screenshot of the BTCPay Server widget