# Extended Manual Setup

This document lists steps for manually deploying BTCPay Server and additional related components. Following these steps is likely to take a long time. A shorter and more pragmatic approach is to use a docker based deployment (opens new window).

The instructions also build all the application components from source which can be an advantage for certain audit and/or security scenarios.


Manual installation is NOT recommended for production use unless you are very confident with your Operating System and Bitcoin security expertise. If you are unsure use the docker deployment or one of the other deployment options.

# You must have technical literacy and be able to resolve any issues on your own. The community will not provide extensive support for this deployment.

# Installation Steps Overview

The instructions in this article have been tested on Ubuntu 20.04. They should be applicable to other Linux based distributions. They are also based on all components being on the same host or virtual machine. It is possible to split the components across different hosts but these instructions don't describe that.

An example hostname of mainnet.demo.btcpayserver.org has been used, it needs to be replaced with the hostname you are using for your BTCPay Server.

# Security

If you do use these instructions to install a BTCPay Server connected to the Bitcoin mainnet then at a minimum you should understand how the wallet mechanisms work. It's highly recommended to read the two articles below and ask questions if anything is not clear.

As an additional aid below is a list of iptables rules and instructions which should include all the ports that need to be open. NO WARRANTY. Use at your own risk, including risk of locking yourself out.

~$ vi iptables.txt
# Generated by iptables-save v1.6.1 on Mon May 27 18:48:11 2019
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT     # SSH
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT     # BTCPay HTTP
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT    # BTCPay HTTPS
-A INPUT -p tcp -m tcp --dport 8333 -j ACCEPT   # Bitcoind P2P
-A INPUT -p tcp -m tcp --dport 9735 -j ACCEPT   # Lightning P2P
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Completed on Mon May 27 18:48:11 2019
~$ sudo iptables-restore < iptables.txt

At this point if you are still connected to you ssh session it's a good sign. If not the rules are temporary and you can use whatever mechanism you have to remotely reboot your server and try again.

The rules have now been temporarily applied. To apply the rules automatically each time your server starts use the iptables-persistent package.

~$ sudo apt install iptables-persistent

If you subsequently change the iptables rules and want to save them across reboots use the command below.

~$ sudo netfilter-persistent save

# Unprivileged user

These instructions configure everything to run under an unprivileged user called admin. Create this user before proceeding:

~$ sudo useradd -M admin && sudo usermod -L admin

# Prerequisites

  • Postgresql
  • Tor
  • NGINX and Let's Encrypt

# Application Components

  • Bitcoin Core1,2
  • NBXplorer1,2
  • BTCPay Server1,2
  • Lightning Network Daemon (LND)2
  • Ride The Lightning (RTL)2

1 The bare minimum install of a BTCPay Server only requires these items. Using a bare minimum configuration reduces the functionality: no Lightning payments, no auto-renewal of TLS certificates, less reliable data store, less capable of handling NAT and more.

2 Built from source code.

# Postgresql

Postgresql can be used by BTCPay Server in place of the default SQLite file based storage. It's also possible to use MySQL.

# Install
~$ sudo apt install postgresql postgresql-contrib
# Configuration

Covered in BTCPay Server Configuration.

# Check
~$ psql --version
psql (PostgreSQL) 12.2 (Ubuntu 12.2-4)
~$ sudo systemctl status postgresql
~$ sudo -u postgres psql
psql (12.2 (Ubuntu 12.2-4))
Type "help" for help.

postgres=# \q

# Tor

Tor can be used by the following components to provide enhanced privacy and/or help with NAT traversal:

  • Bitcoin Core Daemon
  • Lightning Network Daemon (LND).

Additional information running Bitcoin Core with Tor support can be found here (opens new window).

# Install
~$ sudo apt install tor
# Configuration
~$ sudo vi /etc/tor/torrc  # (and uncomment two lines below)
ControlPort 9051
CookieAuthentication 1
~$ sudo systemctl restart tor

Covered further in Bitcoin and Lightning Network Daemon sections.

# Check
~$ tor --version
Tor version
~$ sudo netstat -tlnp | grep tor # (lines below correspond to the tor control port and SOCKS proxy)
tcp        0      0*               LISTEN      1376/tor
tcp        0      0*               LISTEN      1376/tor

# NGINX and Let's Encrypt

NGINX is used as a web server to manage HTTP requests to BTCPay Server and Ride The Lightning. Paired with Let's Encrypt it allows seamless procurement and renewal of a TLS certificate for your BTCPay Server instance.

Let's Encrypt is a free service for procuring and renewing TLS certificates. The service comes with scripts that can be installed to automatically manage the whole process.

# Install
# 1. Install NGINX.
~$ sudo apt install nginx
# 2. Install Let's Encrypt
~$ sudo apt install certbot python3-certbot-nginx
# Configuration
# 1. Let's Encrypt TLS certificate

You must create an A or AAAA record for <your domain name> that points to the IP address of your server instance. If your server is behind NAT then you need to forward port 80 to your instance.

The certbot script works by checking for a specific file on the web server hosting the requested domain. If it can't get the file the TLS certificate won't be issued. If the initial attempt fails it will be periodically re-attempted or you can simply re-run the command.

sudo certbot --nginx -d <your domain name> # (e.g: sudo certbot --nginx -d mainnet.demo.btcpayserver.org)
# 2. Add NGINX configuration file

The configuration file below has been copied from the BTCPay Server docker install.

Search for "mainnet.demo.btcpayserver.org" and replace it with your own domain name.

~$ vi /etc/nginx/conf.d/default.conf
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
  default $http_x_forwarded_proto;
  ''      $scheme;
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
  default $http_x_forwarded_port;
  ''      $server_port;
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
  default upgrade;
  '' close;
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Prevent Nginx Information Disclosure
server_tokens off;
# Default dhparam
# Set appropriate X-Forwarded-Ssl header
map $scheme $proxy_x_forwarded_ssl {
  default off;
  https on;

gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
                 '"$request" $status $body_bytes_sent '
                 '"$http_referer" "$http_user_agent"';
access_log off;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
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 $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_buffer_size          128k;
proxy_buffers              4 256k;
proxy_busy_buffers_size    256k;
client_header_buffer_size 500k;
large_client_header_buffers 4 500k;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";

server {
        server_name mainnet.demo.btcpayserver.org;
        listen 80;
        access_log /var/log/nginx/access.log vhost;
        return 301 https://$host$request_uri;
server {
        client_max_body_size 100M;
        server_name mainnet.demo.btcpayserver.org;
        listen 443 ssl http2 ;
        access_log /var/log/nginx/access.log vhost;
        ssl_protocols TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_session_timeout 5m;
        ssl_session_cache shared:SSL:50m;
        ssl_session_tickets off;
        ssl_certificate /etc/letsencrypt/live/mainnet.demo.btcpayserver.org/cert.pem;
        ssl_certificate_key /etc/letsencrypt/live/mainnet.demo.btcpayserver.org/privkey.pem;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        ssl_stapling on;
        ssl_stapling_verify on;
                ssl_trusted_certificate /etc/letsencrypt/live/mainnet.demo.btcpayserver.org/fullchain.pem;
        add_header Strict-Transport-Security "max-age=31536000" always;
        #include /etc/nginx/vhost.d/default;

        # Here is the main BTCPay Server application
        location / {

        # Include the next two stanzas if and only if you want to expose your lightning gRPC & RPC interfaces to the internet
        location /lnrpc.Lightning {
                grpc_pass grpcs://;

        location /lnd-rest/btc/ {
                rewrite ^/lnd-rest/btc/(.*) /$1 break;

        # Include this stanza if you are planning to set up Ride The Lightning (RTL)
        location /rtl/ {
~$ sudo systemctl restart nginx
~$ sudo systemctl status nginx

If there is an error message restarting nginx try:

sudo journalctl -xe --unit nginx
# Check
# 1. Check Let's Encrypt

It can be a little bit tricky to get everything set up correctly for the Let's Encrypt script to work correctly. Some additional commands are listed below to help with any troubleshooting.

~$ sudo certbot certificates
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: mainnet.demo.btcpayserver.org
    Domains: mainnet.demo.btcpayserver.org
    Expiry Date: 2019-08-10 18:00:31+00:00 (VALID: 79 days)
    Certificate Path: /etc/letsencrypt/live/mainnet.demo.btcpayserver.org/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/mainnet.demo.btcpayserver.org/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
~$ cat /etc/cron.d/certbot # (check the cron job exists)
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
~$ sudo tail /var/log/letsencrypt/letsencrypt.log # (check for problems)
2019-05-22 19:36:36,062:DEBUG:certbot.main:certbot version: 0.31.0
~$ sudo certbot renew --dry-run # (test renewal)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/mainnet.demo.btcpayserver.org/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 2. Check NGINX.
~$ sudo nginx -v
nginx version: nginx/1.18.0 (Ubuntu)
~$ sudo netstat -tlnp | grep nginx
tcp        0      0   *               LISTEN      266275/nginx: maste
tcp        0      0    *               LISTEN      266275/nginx: maste
tcp6       0      0 :::443                  :::*                    LISTEN      266275/nginx: maste
tcp6       0      0 :::80                   :::*                    LISTEN      266275/nginx: maste
~$ sudo journalctl -xe --unit nginx --follow
-- A start job for unit nginx.service has finished successfully.
-- The job identifier is 19471.

Attempt to open your web site in a browser. At this point it is expected that a 502 Bad Gatewayerror will occur. The nginx logs can be checked to verify that the connection attempt was received.

~$ tail /var/log/nginx/access.log
mainnet.demo.btcpayserver.org - - [27/Jul/2020:12:19:57 +0100] "GET / HTTP/2.0" 502 552 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"

If there is a problem then the nginx error log can also be checked.

~$ tail /var/log/nginx/error.log

# Bitcoin Core

The gateway to the Bitcoin network for BTCPay Server components.

# Install

The full instructions to build Bitcoin Core from source are here (opens new window).

The alternative to building from source is to download a signed binary distribution from https://bitcoincore.org/en/download/ (opens new window).

~$ wget https://bitcoincore.org/bin/bitcoin-core-0.20.0/bitcoin-0.20.0-x86_64-linux-gnu.tar.gz
~$ wget https://bitcoincore.org/bin/bitcoin-core-0.20.0/SHA256SUMS.asc
# 1. Install Pre-requisites and dependencies

These instructions do not build the Bitcoin Core GUI components as they are not needed for BTCPay Server.

~$ sudo apt install build-essential libtool autotools-dev automake pkg-config bsdmainutils python3
~$ sudo apt install libevent-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libminiupnpc-dev libzmq3-dev
# 2. Download and Build Source

Before cloning the Bitcoin Core repository identify the most recent stable version. One convenient way to do this is on the GitHub repository page look at the latest version under the "Releases" heading. At the time of writing the stable version is 0.20.0. Adjust the tag in the git clone command below for the stable version you want to build.

~$ cd src
~/src$ git clone --depth 1 --branch v0.20.0 https://github.com/bitcoin/bitcoin.git
~/src$ cd bitcoin

A specific version of the Berkeley DB dependency needs to be installed.

~/src/bitcoin$ ./contrib/install_db4.sh `pwd`

Use the autoconf scripts to generate the make files and then build.

~/src/bitcoin$ ./autogen.sh
~/src/bitcoin$ export BDB_PREFIX='/home/admin/src/bitcoin/db4'
~/src/bitcoin$ ./configure BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include"
~/src/bitcoin$ make
~/src/bitcoin$ sudo make install
~/src/bitcoin$ bitcoind -version
Bitcoin Core version v0.20.0
# 3. Create the configuration file

An example configuration file is available on the Bitcoin Core repository at https://github.com/bitcoin/bitcoin/blob/master/share/examples/bitcoin.conf.

Create a bitcoin.conf file to suit your needs. An example file that is suitable for BTCPay Server is shown below. This configuration does not prune blocks which means as of May 2019 you will require 235 GB for the Bitcoin blockchain.

~$ vi bitcoin.conf
server=1                              # need RPC for btcpay.
rpcbind=                     # loopback is default for 0.18.0 but no harm making sure.
whitelist=                   # for nbxplorer.
rpcallowip=               # loopback is default but again no harm.
zmqpubrawblock=tcp://  # needed for lightning.
zmqpubrawtx=tcp://     # needed for lightning.
#prune=5000                           # Recommended if not enough disk space for full 600+GB blockchain.

Copy the file to the directory specified in the systemd service file and assign read permissions to all users.

~$ sudo mkdir -p /etc/bitcoin
~$ sudo cp bitcoin.conf /etc/bitcoin
~$ sudo chmod 644 /etc/bitcoin/bitcoin.conf
# 5. Create a systemd service

An example systemd service file is available in the Bitcoin Core repository at https://raw.githubusercontent.com/bitcoin/bitcoin/master/contrib/init/bitcoind.service.

Edit the service file depending on your needs.

In the example below the User and Group have been changed to use the admin user instead of requiring a new bitcoin user. If the admin user on your system is intended for running BTCPayServer this is a reasonable choice. Otherwise consider creating a dedicated bitcoin user.

~$ vi bitcoind.service
Description=Bitcoin daemon

ExecStart=/usr/bin/bitcoind -daemon \
                            -pid=/run/bitcoind/bitcoind.pid \
                            -conf=/etc/bitcoin/bitcoin.conf \

# Make sure the config directory is readable by the service user
ExecStartPre=/bin/chgrp admin /etc/bitcoin

# Process management


# Run as admin:admin

# /run/bitcoind

# /etc/bitcoin

# /var/lib/bitcoind

# Hardening measures
# Provide a private /tmp and /var/tmp.

# Deny access to /home, /root and /run/user

# Mount /usr, /boot/ and /etc read-only for the process.

# Disallow the process and all of its children to gain
# new privileges through execve().

# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.

# Deny the creation of writable and executable memory mappings.


Once the service file is ready complete the commands below.

~$ sudo cp bitcoind.service /etc/systemd/system
~$ sudo systemctl enable --now bitcoind
~$ sudo systemctl status bitcoind
Jul 26 21:51:52 ubuntu systemd[1]: Started Bitcoin daemon.

If the start attempt shows an error message check the log using:

sudo journalctl -xe --unit bitcoind

The bitcoin-cli client needs to authenticate to bitcoind for RPC calls. The easiest way to allow this is to create a symbolic link to the cookie file.

~$ cd ~
~$ ln -s /var/lib/bitcoind/.cookie .bitcoin/.cookie

It's not vital to perform this step but if not done then every bitcoin-cli command needs to specify the path to the cookie file as below.

~$ bitcoin-cli -rpccookiefile=/var/lib/bitcoind/.cookie getblockchaininfo
# Check

It will take Bitcoin anywhere from a few hours to a few days to synchronise the blockchain. Use any or all of the commands below to check its status.

~$ sudo systemctl status bitcoind
Active: active (running) since Sun 2020-07-26 21:51:52 IST; 2min 47s ago
~$ tail /var/lib/bitcoind/debug.log -f
2020-07-26T20:55:09Z UpdateTip: new best=0000000000000361c37dfb6fa905ef967b95411fa96f7dcb4eca5dd4434d9e59 height=126732 version=0x00000001 log2_work=62.952182 tx=560114 date='2011-05-25T21:26:08Z' progress=0.001018 cache=43.6MiB(291168txo)
~$ bitcoin-cli getblockchaininfo
  "chain": "main",
  "blocks": 133015,
  "headers": 640929,
  "bestblockhash": "0000000000000e81b67de8d61eab726f40585bed954b1dd59f86ab10e4e55398",
  "difficulty": 876954.4935135372,
  "mediantime": 1308897947,
  "verificationprogress": 0.001530462018729556,

When the verificationprogress gets to either 0.99.. or 1.0 your node has synchronised. To double check you can also use a public block explorer such as https://blockstream.info/ (opens new window) to view the latest Bitcoin block and compare it to the blocks value from the bitcoin-cli getblockchaininfo result.

# Check Tor and Bitcoin

If Tor was installed prior to the Bitcoin Daemon then it should have automatically registered and begun listening on a torv2 onion address (note support for torv3 onion addresses is in the pipeline (opens new window)).

The easiest way to get your Bitcoin Daemon torv2 address is using bitcoin-cli:

bitcoin-cli getnetworkinfo
  "version": 200100,
  "subversion": "/Satoshi:0.20.1/",
  "protocolversion": 70015,
  "localservices": "0000000000000409",
    "localaddresses": [
      "address": "",
      "port": 8333,
      "score": 1
      "address": "v5j6hfz4xafmeckf.onion",
      "port": 8333,
      "score": 156
  "warnings": ""

An alternative approach is to search the Bitcoin daemon log file:

~$ cat /var/lib/bitcoind/debug.log | grep onion
2019-05-23T18:24:22Z tor: Got service ID 4d4al7v4hj5p7bb6, advertising service 4d4al7v4hj5p7bb6.onion:8333
2019-05-23T18:24:22Z AddLocal(4d4al7v4hj5p7bb6.onion:8333,4)

If there is a problem and no onion address can be found in the log file then check for Tor related error messages:

~$ cat /var/lib/bitcoind/debug.log | grep tor
2020-07-27T08:03:28Z torcontrol thread start
2020-07-27T08:03:28Z tor: Authentication cookie /run/tor/control.authcookie could not be opened (check permissions)

The above error message can occur if the user accounts running the Bitcoin service does not have read access to the Tor authentication cookie file, more info (opens new window). To fix this particular error add the required user account to the debian-tor group.

sudo usermod -a -G debian-tor admin

To change your onion address:

~$ rm /var/lib/bitcoind/onion_private_key
~$ sudo systemctl restart bitcoind
~$ bitcoin-cli getnetworkinfo | grep onion
      "name": "onion",
      "address": "qud5iwbntqxlfwjv.onion",

To check your onion address from a remote host with tor installed:

~$ torsocks --shell
~$ telnet 4d4al7v4hj5p7bb6.onion 8333
 Connected to
 Escape character is '^]'.
~$ exit

To connect another bitcoind instance to your new node:

~$ bitcoin-cli addnode "4d4al7v4hj5p7bb6.onion" "add"
~$ bitcoin-cli getaddednodeinfo
   "addednode": "4d4al7v4hj5p7bb6.onion",
   "connected": true,
   "addresses": [
       "address": "4d4al7v4hj5p7bb6.onion:8333",
       "connected": "outbound"

# NBXplorer

NBXplorer is a dotnet core application that monitors the Bitcoin blockchain for transactions of interest to your BTCPay Server.

# Install
# 1. Install .NET 8.0 SDK

Follow the install instuctions (opens new window):

# Add Microsoft package repository
wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

# Install the SDK
sudo apt-get update
sudo apt-get install -y apt-transport-https
sudo apt-get update
sudo apt-get install -y dotnet-sdk-8.0

## Check
dotnet --version
# 2. Build NBXplorer
~$ cd ~; mkdir -p src; cd src
~/src$ git clone https://github.com/dgarage/NBXplorer
~/src$ cd NBXplorer
~/src/NBXplorer$ git checkout $(git tag --sort -version:refname | awk 'match($0, /^v[0-9]+\./)' | head -n 1)
~/src/NBXplorer$ ./build.sh
# 3. Create Postgresql Database

While NBXplorer support storing data in a local database via --dbtrie, this is deprecated. Here how to create the appropriate database and user for NBXlporer in Postgresql.

~$ sudo -u postgres psql

Then execute



# 4. Create a configuration file
$ vi nbxplorer.config
### Database ###
postgres=User ID=nbxplorer;Password=urpassword;Application Name=nbxplorer;MaxPoolSize=20;Host=localhost;Port=5432;Database=nbxplorer;
~$ sudo mkdir /etc/nbxplorer
~$ sudo cp nbxplorer.config /etc/nbxplorer
~$ sudo chmod 644 /etc/nbxplorer/nbxplorer.config

Note: If you previously used a dbtrie backend for NBXplorer, but want to switch to postgres, read this documentation (opens new window).

# 5. Create a systemd service

An example systemd service file is shown below. Adjust the paths, User and Group accordingly.

~$ vi nbxplorer.service
Description=NBXplorer daemon

ExecStart=/home/admin/src/NBXplorer/run.sh --conf=/etc/nbxplorer/nbxplorer.config

~$ sudo cp nbxplorer.service /etc/systemd/system
~$ sudo systemctl enable --now nbxplorer
# Check
~$ sudo journalctl -xe --unit nbxplorer --follow
May 23 19:13:35 btc run.sh[8065]: info: Configuration:  Data Directory: /home/admin/.nbxplorer/Main
May 23 19:13:35 btc run.sh[8065]: info: Configuration:  Configuration File: /home/admin/.nbxplorer/Main/settings.config
May 23 19:13:35 btc run.sh[8065]: info: Configuration:  Network: Mainnet
May 23 19:20:04 btc run.sh[8065]: info: Events:         BTC: New block 0000000000000000000c405ba5df5f5533359a4393247a0c52d26c458d4dd989 (577449)

If it doesn't start correctly stop the service and run the application directly to get any error messages.

~$ sudo systemctl stop nbxplorer
~$ cd ~; pushd ./src/NBXplorer; ./run.sh; popd
# Update

Updating could break things. Be careful on a live system.

# Stop the service
~$ sudo systemctl stop nbxplorer
# Checkout and build latest tag
~$ cd ~; pushd ~/src/NBXplorer; git fetch --tags && git checkout $(git tag --sort -version:refname | awk 'match($0, /^v[0-9]+\./)' | head -n 1) && ./build.sh; popd;
# Restart the service
~$ sudo systemctl start nbxplorer
# Migration

With BTCPay Server v1.5 we improved NBXplorer by switching to Postgres (opens new window). If you are still using the DBTrie based version, please consider migrating NBXplorer (opens new window).

Afterwards you need to set BTCPAY_EXPLORERPOSTGRES to the same connection string as NBXplorer.

# BTCPay Server

Like NBXplorer the BTCPay Server application is also .NET Core. The install steps assume .NET Core was previosuly installed.

# Install
# 1. Build BTCPay Server
~$ cd ~; mkdir -p src; cd src
# Clone the repository
~/src$ git clone https://github.com/btcpayserver/btcpayserver.git
~/src$ cd btcpayserver
# Checkout latest tag
~$ git checkout $(git tag --sort -version:refname | awk 'match($0, /^v[0-9]+\.[0-9]+\.[0-9]+$/)' | head -n 1)
# Build the app
~/src/btcpayserver$ ./build.sh
# 2. Create Postgresql Database

By default BTCPay Server will store data in a single SQLite file. A more robust option is to use Postgresql which requires the appropriate database and user to exist.

~$ sudo -u postgres psql

Then execute



# 3. Create a configuration file
$ vi btcpay.config
### Database ###
postgres=User ID=btcpay;Password=urpassword;Application Name=btcpayserver;Host=localhost;Port=5432;Database=btcpay;
explorer.postgres=User ID=nbxplorer;Password=urpassword;Application Name=nbxplorer;MaxPoolSize=20;Host=localhost;Port=5432;Database=nbxplorer;
~$ sudo mkdir /etc/btcpay
~$ sudo cp btcpay.config /etc/btcpay
~$ sudo chmod 644 /etc/btcpay/btcpay.config
# 4. Create a systemd service

An example systemd service file is shown below. Adjust the paths, User and Group accordingly.

~$ vi btcpay.service
Description=BTCPay Server

ExecStart=/home/admin/src/btcpayserver/run.sh --conf=/etc/btcpay/btcpay.config

~$ sudo cp btcpay.service /etc/systemd/system
~$ sudo systemctl enable --now btcpay
# Check
~$ sudo journalctl -xe --unit btcpay --follow
-- The start-up result is RESULT.
May 23 20:01:25 btc run.sh[10263]: info: Configuration:  Data Directory: /home/admin/.btcpayserver/Main
May 23 20:01:25 btc run.sh[10263]: info: Configuration:  Configuration File: /etc/btcpay/btcpay.config
May 23 20:01:25 btc run.sh[10263]: info: Configuration:  Network: Mainnet

If it doesn't start correctly stop the service and run the application directly to get any error messages.

~$ sudo systemctl stop btcpay
~$ cd ~; pushd ~/src/btcpayserver; ./run.sh --conf=/etc/btcpay/btcpay.config; popd;

An example of checking information in the database.

~$ sudo -u postgres psql
postgres=# \connect btcpay;
btcpay=# \dt
btcpay=# select * from "Invoices";
btcpay=# \q

Attempting to open your BTCPay Server domain in a browser now should show the "Welcome to your BTCPay Server" page. If you are not using a Lightning Node this is the end of the install.

# Update

Updating could break things. Be careful on a live system.

# Stop the service
~$ sudo systemctl stop btcpay
# Checkout and build latest tag
~$ cd ~; pushd ~/src/btcpayserver; git fetch --tags && git checkout $(git tag --sort -version:refname | awk 'match($0, /^v[0-9]+\.[0-9]+\.[0-9]+$/)' | head -n 1) && ./build.sh; popd;
# Restart the service
~$ sudo systemctl start btcpay

# Lightning Network Daemon (LND)

# Install

Full instructions (opens new window).

# 1. Install Go
~$ sudo apt install make
~$ wget https://dl.google.com/go/go1.13.linux-amd64.tar.gz
~$ sha256sum go1.13.linux-amd64.tar.gz
68a2297eb099d1a76097905a2ce334e3155004ec08cdea85f24527be3c48e856  go1.13.linux-amd64.tar.gz
~$ sudo tar -C /usr/local -xzf go1.13.linux-amd64.tar.gz
~$ export PATH=$PATH:/usr/local/go/bin
~$ export GOPATH=~/gocode
~$ export PATH=$PATH:$GOPATH/bin
~$ go version
go version go1.13 linux/amd64
# 2. Build and install LND
~$ cd ~; mkdir -p src; cd src
~$ git clone https://github.com/lightningnetwork/lnd
~$ cd lnd
~$ make
~$ make install # installs to a directory in $GOPATH/bin
~$ sudo cp $GOPATH/bin/lnd $GOPATH/bin/lncli /usr/bin
~$ lnd --version
lnd version 0.10.99-beta commit=clock/v1.0.0-229-ge64e71d86dc1ac716c30a80f85a22e8fb544697f

lnd looks for bitcoin.conf in a specific location to get necessary RPC and zeromq details.

~$ ln -s ~/.bitcoin/bitcoin.conf /etc/bitcoin/bitcoin.conf
# 4. Create a configuration file
~$ vi lnd.conf
[Application Options]
#externalip= # change to your public IP address if required.


~$ sudo mkdir -p /etc/lnd
~$ sudo mkdir -p /var/lib/lnd
~$ sudo chown admin:admin -R /var/lib/lnd
~$ sudo cp lnd.conf /etc/lnd
~$ sudo chmod 644 /etc/lnd/lnd.conf
# 5. Create a systemd service

An example systemd service file is shown below. Adjust the paths, User and Group accordingly.

~$ vi lnd.service
Description=LND Lightning Network Daemon

ExecStart=/usr/bin/lnd --configfile=/etc/lnd/lnd.conf
ExecStop=/usr/bin/lncli --lnddir /var/lib/lnd stop
PIDFile= /run/lnd/lnd.pid



~$ sudo cp lnd.service /etc/systemd/system
~$ sudo systemctl enable --now lnd
# Configuration


Running a Bitcoin Lightning daemon requires a hot wallet on your BTCPay Server.

With Bitcoin the protocol has evolved and deterministic key derivation means the keys for your wallet can be kept in a different location to the BTCPay Server. Lightning daemons do not have this facility. Any Bitcoins committed or received in your lightning channels are controlled by private keys that are on your BTCPay Server.

The install steps above use /var/lib/lnd as the data directory rather than the default /home/user/.lnd. In order to save typing when using the lncli client it's useful to add a symbolic directory link.

ln -s /var/lib/lnd .lnd
# 2. Create Lightning wallet

The first time the lnd is started a new wallet must be created and the backup seed safely recorded (if someone else gets your seed they can steal your funds so keep it safe).

~$ lncli create
Input wallet password:
Confirm password:

Do you have an existing cipher seed mnemonic you want to use? (Enter y/n): n

Your cipher seed can optionally be encrypted.
Input your passphrase if you wish to encrypt it (or press enter to proceed without a cipher seed passphrase):

Generating fresh cipher seed...

---------------BEGIN LND CIPHER SEED---------------
 1. above      2. catch    3. start     4. tape
 5. sound      6. friend   7. water     8. royal
 9. solid     10. poet    11. wisdom   12. match
13. virtual   14. zero    15. slender  16. thrive
17. idle      18. catch   19. robot    20. clay
21. resemble  22. angry   23. work     24. until
---------------END LND CIPHER SEED-----------------


lnd successfully initialized!

Note that if the symbolic directory link from the previous step was not created the command is:

lncli --lnddir /var/lib/lnd create
# 3. Unlock the wallet

Every time lnd is restarted the wallet needs to be unlocked. This is not ideal for a BTCPay Server that can is designed to run unattended but Lighting is still in its infancy.

~$ lncli unlock
# Check
~$ lncli getinfo
    "version": "0.10.99-beta commit=clock/v1.0.0-229-ge64e71d86dc1ac716c30a80f85a22e8fb544697f",
    "commit_hash": "e64e71d86dc1ac716c30a80f85a22e8fb544697f",

Check the service:

~$ sudo journalctl -xe --unit lnd --follow
Jul 27 15:46:29 ubuntu lnd[654474]: 2020-07-27 15:46:29.909 [INF] DISC: Attempting to bootstrap with: BOLT-0010 DNS Seed: [[nodes.lightning.directory soa.nodes.lightning.directory] [lseed.bitcoinstats.com ]]
Jul 27 15:49:41 ubuntu lnd[654474]: 2020-07-27 15:49:41.939 [INF] DISC: Attempting to bootstrap with: Authenticated Channel Graph
Jul 27 15:49:41 ubuntu lnd[654474]: 2020-07-27 15:49:41.940 [ERR] SRVR: Unable to retrieve initial bootstrap peers: no addresses found

The Lightning Node Connection String to use with BTCPay Server is:

# Add LND as internal node

To add LND as internal node you have to edit the btcpay.config file:

cd /etc/btcpay
vi btcpay.config

Right below the database part, add the BTC.lightning setting:

### Lightning ###

See the "Use custom node" view on the Lightning node connection setting screen in BTCPay Server for details on the connection string.

You need to restart BTCPay Server for the settings update to take effect:

~$ sudo systemctl restart btcpay
# Check Tor and LND

As with the Bitcoin daemon if Tor is installed and the configuration file enables it (the one above does) then lnd will automatically register an onion address. In lnd's case torv3 addresses are supported.

The torv3 onion address below is a lot longer than the torv2 one from the Bitcoin daemon section (16 characters compared to 56 characters).

~$ lncli getinfo | grep onion

The Tor address created by lnd can be used to connect to other Lighting peers on the Tor network. The Tor address can work in parallel with an IPv4 or IPv6 address. To register one of those make sure the externalip is set in the lnd configuration file.

# Update

Updating could break things. Be careful on a live system.

~$ sudo systemctl stop lnd
~$ export PATH=$PATH:/usr/local/go/bin
~$ export GOPATH=~/gocode
~$ export PATH=$PATH:$GOPATH/bin
~$ cd ~/src/lnd
~$ git pull
~$ make
~$ make install # installs to a directory in $GOPATH/bin
~$ sudo cp $GOPATH/bin/lnd $GOPATH/bin/lncli /usr/bin
~$ lnd --version
lnd version 0.10.99-beta commit=clock/v1.0.0-229-ge64e71d86dc1ac716c30a80f85a22e8fb544697f
~$ sudo systemctl start lnd

After the daemon has been restarted the wallet needs to be unlocked:

~$ lncli unlock

If Ride The Lightning (RTL) is installed, see next section, it may have stopped when lnd disappeared so it will also need to be restarted.

~$ sudo systemctl start rtl

# Ride The Lightning (RTL)

Ride the Lightning is a Node.js application to manage your Lightning peers, channels, wallet etc.

The advantage of the work that has gone into BTCPay Server is that the RTL web page can be controlled and accessed in the same manner as the BTCPay site.

# Install
# 1. Install dependencies
~$ sudo apt install nodejs build-essential npm
# 2. Build RTL
~$ cd ~/src
~$ git clone https://github.com/Ride-The-Lightning/RTL.git
~$ cd RTL
~$ npm install --only=prod
# 3. Create a configuration file

Copy the sample config file from sample-RTL-Config.json and adjust accordingly. An example that works with the rest of the instructions in this document is shown below.

~$ cp src/RTL/sample-RTL-Config.json RTL-Config.json
~$ vi RTL-Config.json
  "port": "3000",
  "defaultNodeIndex": 1,
  "SSO": {
    "rtlSSO": 1,
    "rtlCookiePath": "/var/lib/rtl/.cookie",  # Needs to match the value in BTCPay systemd settings.
    "logoutRedirectLink": "https://mainnet.demo.btcpayserver.org/login"
  "nodes": [
      "index": 1,
      "lnNode": "Node 1",
      "lnImplementation": "LND",
      "Authentication": {
        "macaroonPath": "/var/lib/lnd/data/chain/bitcoin/mainnet",
        "configPath": "/etc/lnd/lnd.conf"
      "Settings": {
        "userPersona": "MERCHANT",
        "themeMode": "DAY",
        "themeColor": "PURPLE",
        "channelBackupPath": "/home/admin/rtl/backup/node-1",
        "enableLogging": false,
        "lnServerUrl": "https://localhost:8080/v1",
        "swapServerUrl": "http://localhost:8081/v1",
        "fiatConversion": false

Note that RTL has different behaviour and requirements compared to the other services documented in theses instructions, specifically:

  1. The configuration file needs to exist in RTL's data directory,
  2. The RTL process may write update to the configuration file.
~$ sudo mkdir -p /var/lib/rtl
~$ sudo cp ~/RTL-Config.json /var/lib/rtl
~$ sudo chown admin:admin -R /var/lib/rtl
~$ sudo chmod 644 /var/lib/rtl/RTL-Config.json
# 4. Create a systemd service
~$ vi rtl.service
Description=Ride The Lightning

ExecStart=/usr/bin/node /home/admin/src/RTL/rtl

~$ sudo cp rtl.service /etc/systemd/system
~$ sudo systemctl enable --now rtl
# Check

Check the service:

~$ sudo journalctl -xe --unit rtl --follow
Jul 27 18:27:52 ubuntu node[988638]: Server is up and running, please open the UI at http://localhost:3000

If it doesn't start correctly stop the service and run the application directly to get any error messages.

~$ sudo systemctl stop rtl
~$ export RTL_CONFIG_PATH=/var/lib/rtl; pushd ~/src/RTL; node rtl; popd;
Server is up and running, please open the UI at http://localhost:3000

From the BTCPay Server web page the RTL interface should be accessible from Server Settings->Services under the "Crypto services exposed by your server" heading.

# Update

Updating could break things. Be careful on a live system.

~$ sudo systemctl stop rtl
~$ cd ~; pushd ~/src/RTL; git pull; npm install; popd;
~$ sudo systemctl start rtl

# The End

# Questions

Join the community chat (opens new window) on Mattermost by downloading Mattermost app (opens new window), or on Telegram (opens new window) in case you need further help or help or want to hang around with like-minded people.