Setup Core Lightning And Zeus Bitcoin Wallet
on Ubuntu and Android
Would you like to set up and run a self-hosted Bitcoin Lightning server, and connect a mobile wallet to it so you can use it when you are out and about?
Then this guide is for you. Let me warn you that the guide is quite complex.
You can follow along by watching this video guide.
https://youtu.be/GSTnvQyuXEE
In this article, we are going to:
In a prior article and video, I covered how to build your own Bitcoin solo pool using ckpool-solo and Bitcoin Knots. You need to have completed installing and have Knots and ckpool running before following this guide.
We are going to be doing two setups, one for the “mainnet” (with real Bitcoin), and one for “testnet4” (Bitcoin on this network has no value). “testnet4” is optional, and is only used for testing. “testnet4” is good for experimenting without using actual real Bitcoin.
When setting up a Lightning channel, it is recommended that you set the minimum channel capacity to at least 0.01 BTC (1 million SATs). In this guide, we will connect to the NiceHash Lightning node, which has a relaxed limit of 0.005 BTC (500,000 SATS). You don’t have to be a customer of NiceHash, do KYC, or have a NiceHash login to be able to connect to their Lightning node. You also need a small amount of Bitcoin to pay fees.
Consequently, if you follow this guide, you will need at least 0.006 BTC to send to your Lightning node.
Many TCP ports will be created or used. A summary of the ports used is:
TCP, Description
3010, Core Lightning mainnet HTTPS RPC Port
8332, Bitcoin mainnet RPC port
9735, Core Lightning mainnet node to node port
9736, Core Lightning mainnet GRPC Port
43010, Core Lightning testnet4 HTTPS RPC Port
48332, Bitcoin testnet4 RPC Port
49735, Core Lightning testnet4 node to node port
49736, Core Lightning testnet4 GRPC Port
51820, Wireguard VPN
Create a build environment
We will build all the software from scratch. To achieve this, we will create a build environment.
Let’s start by making sure your system is fully patched.
sudo apt update
sudo apt upgradeLet's install all the dependencies. You likely already have most of these. Paste all of these lines in at once.
sudo apt install -y \
jq autoconf automake build-essential git libtool libsqlite3-dev libffi-dev \
python3 python3-pip net-tools zlib1g-dev libsodium-dev gettext \
protobuf-compiler lowdown qrencodeCreating a Backup Environment
This section is optional, but unless you don’t mind losing Bitcoin, you really should set up a backup environment.
Three key pieces of data secure your Lightning funds:
HSM secret: This is a static key and only needs to be backed up once.
Static channel data: This needs to be backed up each time you open a new channel.
Database: Detailed information about your funds locked in channels is stored in the database and is ONLY kept on your machine. This changes with every transaction, and if you lose this, you can lose funds. This needs to be backed up frequently.
Amazon AWS S3
We will set up an environment that uses off-site backups stored in an Amazon S3 bucket. Buckets are used for storing files. This should cost less than $10 per year. Probably half that. We will also encrypt those backups using PGP.
Start by signing up for an Amazon AWS account here:
https://signin.aws.amazon.com/signup?request_type=register
Note that you can sign up for a free six-month trial account at the time of writing this article.
Once you have completed the sign-up process, we need to create an access key. This consists of two parts: the key ID and a secret. The secret cannot be retrieved once created, so you must store it in a secure location.
Click on your account name in the top right-hand corner, and select “Security Credentials”. Scroll down to the section labelled “Access keys”, and create the access key. Make sure you save the access key ID and secret.
In this same section, you can add MFA to your account login. You really should do that as well.
We are going to use a cloud service called “S3,” which provides really cheap cloud storage. In the “Search” bar in the top left of the console, type in “s3”. The first option that comes up should be “Scalable Storage in the Cloud”. Click on this.
In the top right-hand corner, next to your account name, is the region in the world you are currently working in. I typically select a region that is close to me. Because this is for backups, you might prefer a region on the other side of the planet. Choose a region that makes sense to you. Next to the region name in the drop-down list is a region code. For example, for “Asia Pacific (Sydney)”, the code is “ap-southeast-2”. Note this region code down for later.
Click “create bucket”. You need to generate a globally unique bucket name. I tend to use something that includes my name and the bucket's purpose. For example, “pdath-bitcoin-backups”.
Choose the default settings for everything. Scroll to the very bottom of the page and select “Create bucket”.
Back to Ubuntu. The Amazon AWS CLI snap is built to use SELinux, but Ubuntu uses AppArmor. This doesn’t prevent the AWS CLI from working, but it generates many cosmetic warning messages. A clean fix to remove the warning messages is to disable SELinux. Create /etc/selinux/config:
sudo vi /etc/selinux/configAdd this one line:
SELINUX=disabledAnd reboot. This is a kernel boot-time parameter. You don’t have to reboot right now, but you’ll see a lot of warning messages if you don’t (which you can ignore).
sudo shutdown -r nowAfter rebooting, install the AWS CLI snap.
sudo snap install aws-cli --classicOnce installed, give it a quick test with this command:
sudo aws --versionYou should then see a line starting with something like “aws-cli/2.31.22”. If you see this, it is working.
Now we need to configure the AWS CLI. Run the below and fill in the answers you have recorded above (access key ID, access secret and region code). When it asks about the “Default output format”, hit return to choose the default.
sudo aws configureIf you run this command, you should see your new bucket:
sudo aws s3 lsWe now have the basics set up for our backup environment.
PGP
Now that we have an off-site backup system available, we need an encryption system. We are going to use PGP.
We'll back up everything from the root account, so we’ll set up that user account.
sudo su -Let’s generate a random passphrase for encrypting the backups.
openssl rand -base64 48 > .passphrase.txtLet’s make sure no one else can access this file.
chmod 400 .passphrase.txtYou need to back up this passphrase on another computer in a safe location. Without this passphrase, you won’t be able to decrypt the backups. This command will display the passphrase.
cat .passphrase.txtGet the exact path to this file and note it for later.
realpath .passphrase.txtLet’s do a mock backup and restore to confirm the principles are correct. Change “/root/.passphrase.txt” to be the path of your passphrase file above. Change “pdath-bitcoin-backups” to the name of the Amazon S3 bucket.
Copy and paste this all in one go.
tar -c /etc | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --symmetric -o - | \
aws s3 cp - s3://pdath-bitcoin-backups/etc.tar.pgpWe can check that the file was created in the S3 bucket with:
aws s3 ls --human-readable s3://pdath-bitcoin-backups/etc.tar.pgpWe can do a test of the backup with:
aws s3 cp s3://pdath-bitcoin-backups/etc.tar.pgp - | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --decrypt | \
tar -tWe can do a test restore to the /tmp directory with:
aws s3 cp s3://pdath-bitcoin-backups/etc.tar.pgp - | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --decrypt | \
tar -xC /tmpYou can check that you can see all the extracted files with:
ls /tmp/etcLet’s clean everything up now.
rm -rf /tmp/etc
aws s3 rm s3://pdath-bitcoin-backups/etc.tar.pgp
exitSet up a VPN
Two popular VPN methods are WireGuard and Tailscale.
WireGuard is built into the Linux kernel. It requires you to either have a static IP address or a dynamic DNS setup, and to forward the WireGuard UDP port to your Ubuntu server.
Another option is TailScale. It actually uses WireGuard as its VPN protocol. Tailscale adds an orchestration layer so you don’t need a static IP address, dynamic DNS or NAT forwarding. Tailscale is popular because it is free for personal use.
In this guide, I will use WireGuard because I don’t want to rely on any third parties. There are many guides for setting up Tailscale if you want something a little easier.
WireGuard
Ubuntu Server Setup
Let’s become the root user because we need to run many privileged commands. Then we can install the WireGuard service.
sudo su -
apt install wireguardNow switch to the WireGuard directory. Then set the default permissions on any newly created files to be accessible only by root.
cd /etc/wireguard
umask 0077The server will need a private and public key.
wg genkey >server_privatekey
wg pubkey <server_privatekey >server_publickeyOur phone (which will connect to WireGuard) will also need a private and a public key.
wg genkey >phone_privatekey
wg pubkey <phone_privatekey >phone_publickeyFor the server config file, we will need the server’s private key and the phone’s public key. You can display both of these with:
cat server_privatekey
cat phone_publickeyNow we need to create /etc/wireguard/wg0.conf using your favourite editor. This is used for the server end of the VPN. I’m using vi:
vi wg0.confAnd put this into the configuration file. Note that “PostUp” and “PostDown” are each a single line. Put in the server’s private key and the phone’s private key from above. Replace eth0 with the interface name your Ubuntu instance uses (you can find it with “ip -4 addr”).
[Interface]
PrivateKey = <Server’s private key>
Address = 10.0.0.1/24 # Or your desired VPN network
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <Phone’s public key>
AllowedIPs = 10.0.0.2/32IP forwarding must be enabled.
sysctl -w net.ipv4.ip_forward=1And to enable IP forwarding across reboots, edit the file below:
vi /etc/sysctl.confFind this line:
#net.ipv4.ip_forward=1Delete the leading # and save the file.
Let's start the WireGuard service and configure it to start on system boot.
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0Let’s check that it began running. Make sure it says that it is active.
systemctl status wg-quick@wg0You now need to port forward UDP/51820 from your Internet router to your Ubuntu server. If you are not sure, search online for instructions on how to do this for your specific router model.
Phone Setup
Let’s generate a QR code for your phone to make setting up the WireGuard client easier. We will need the phone's private key and the server’s public key.
cat phone_privatekey
cat server_publickeyCreate a file on your Ubuntu server called something like phone.conf.
vi phone.confAnd add this config to it. Add the keys from above and enter your router's public IP address, where you configured NAT. Make this file as small as possible, because we need to encode it into a QR code.
[Interface]
PrivateKey = <Phone’s private key>
Address = 10.0.0.2/32
[Peer]
PublicKey = <Servers’s public key>
Endpoint = <Your public IP address>:51820
AllowedIPs = 0.0.0.0/0And finally, generate a QR code.
qrencode -t ansiutf8 <phone.confBefore we install the mobile WireGuard client, we need to install the Zeus wallet. Go to your favourite app store and install the Zeus wallet. Make sure it says it is made by “Asta 21 Inc”. We’ll come back to setting this up later.
Once that is done, we can proceed to set up the mobile WireGuard VPN client. Search for and install the “WireGuard” app from your app store. Make sure it says the publisher is “WiredGuard Development Team”.
Select the option to create a new tunnel. Use the “Scan from QR code” option. Choose a tunnel name, such as “home”. Click on the new tunnel, and then the pencil in the top right-hand corner.
Click “All applications”, choose “Include only”, and then select “Zeus”. Click “Include 1 app”. You don’t need to take this step, but it ensures that only Zeus Wallet can access your home server.
Click the “save” button at the top.
Now turn the VPN on. Hopefully, you won’t get any errors. You now have a working VPN that lets the Zeus Lightning wallet talk to Core Lightning when you are away from home.
Lastly, let's leave privileged mode on our Ubuntu server.
exitInstalling Core Lightning
testnet4
Bitcoin on “testnet” has no value. It is only used for testing. Testnet4 is excellent to practice on. You can’t lose anything of value on testnet4. This section is optional, but if you set it up, you can practice using Lightning without risking real Bitcoin.
Make sure you start this section with the initial account you use to log in to your Ubuntu machine. You can use “whoami” to check who you are logged in as. If you are logged in with another account, you can use “exit” to return to that initial account.
We need to retrieve the Bitcoin testnet4 RPC username and password from the ckpool-solo setup we set up in a prior video. The text below should be copied and pasted as a single command.
You can also use the Bitcoin script “share/rpcauth/rpcauth.py” to generate a username or password.
Note the results down for later.
sudo -u ckpool-testnet grep -e auth -e pass \
/home/ckpool-testnet/solobtc/ckpool.confNext, we’ll create an account to run the Core Lightning testnet node, then switch to that account.
sudo useradd -g testnet -m -s /bin/bash cl-testnet
sudo su - cl-testnetWe need to set up the Rust environment.
rustup default stableWe will be using a Python tool called “uv”. We need to prepare a location for it to install its binaries into. We might as well create the standard local bin directory and the directory for storing the Lightning server config in one go.
mkdir -p ~/.local/bin ~/bin ~/.lightningCore Lightning uses the bitcoin-cli command, so we need to create a symlink for it in the cl-testnet user account.
ln -s /home/knots-testnet/bin/bitcoin-cli ~/bin/bitcoin-cliWe can now run the “uv” installer.
curl -LsSf https://astral.sh/uv/install.sh | shSeveral changes have been made to our login environment. Let’s log out and back in again to make them all apply.
exit
sudo su - cl-testnetWe are now ready to clone the Core Lightning repository.
git clone https://github.com/ElementsProject/lightning.git
cd lightningWe are now ready to begin building Core Lightning. Paste these commands one at a time. The Rust compiler is really slow. This is going to take a while.
uv sync --all-extras --all-groups --frozen
COPTFLAGS="-march=native -O2" CDEBUGFLAGS="" ./configure --prefix=~
RUST_PROFILE=release uv run make -j4
RUST_PROFILE=release make installNext, we need to create a config file. Create ~/.lightning/config and add these lines. Fill in the RPC username and password you stored from the beginning of this section. We also specify that we will be using testnet4.
”alias” lets you define a name for your node. It can be up to 32 characters long. This is an optional field.
bitcoin-rpcuser=rpcadmin
bitcoin-rpcpassword=...
bitcoin-rpcport=48332
network=testnet4
alias=<yourname>-testnet4
grpc-port=49736
clnrest-host=0.0.0.0
clnrest-port=43010
clnrest-protocol=httpsWe can now test starting the lightning server.
lightningd &We can test accessing it using the CLI:
lightning-cli getchaininfoWe can now stop lightningd from running and exit back to the privileged user.
lightning-cli stop
exitLet’s now set the daemon to run automatically. Invoke your favourite editor and create the file below.
sudo vi /etc/systemd/system/cl-testnet.servicePaste in these contents:
[Unit]
Description=Core Lightning testnet4 daemon
Requires=knots-testnet.service
Requires=network-online.target
After=knots-testnet.service
After=network-online.target
[Service]
User=cl-testnet
Group=testnet
Type=simple
Environment=PATH=/home/cl-testnet/bin:/usr/local/bin:/usr/bin:/bin
WorkingDirectory=/home/cl-testnet/
PIDFile=/home/cl-testnet/.lightning/lightningd-testnet4.pid
ExecStart=/home/cl-testnet/bin/lightningd
ExecStop=/home/cl-testnet/bin/lightning-cli stop
RestartSec=30
Restart=always
PrivateTmp=yes
ProtectSystem=full
NoNewPrivileges=yes
PrivateDevices=yes
[Install]
WantedBy=multi-user.targetLet’s enable and start the new service (paste in the commands one at a time).
sudo systemctl daemon-reload
sudo systemctl enable cl-testnet.service
sudo systemctl start cl-testnet.service
sudo systemctl status cl-testnet.service
sudo journalctl -xeu cl-testnet.serviceNow we need to create a new on-chain testnet4 wallet and fund it. Once that is done, we can open a channel and fund it.
sudo su - cl-testnet
lightning-cli newaddrIf you have some testnet4 Bitcoin, you can now send it to this address. Otherwise, you can use a faucet like:
https://coinfaucet.eu/en/btc-testnet4/
Also, note the return address so we can send the funds back when testing is complete.
It is recommended that the channel sizes be at least 0.01 BTC (1 million SATS). You’ll want to transfer slightly more than this to cover fees.
Once you have transferred the testnet4 Bitcoin, use the commands below to verify receipt.
lightning-cli listfundsIt’s probably going to take a while. This is a good time to take an hour-long break.
Keep running the command above periodically until you see the testnet4 Bitcoin appear.
Opening a channel on testnet4
Now, let’s open a testnet4 Lightning channel. Run the following command to list the “stable” testnet4 nodes.
dig test4.nodes.lightning.wiki srv +shortYou will get many lines of output. The first line will look a bit like:
10 10 9735 ln1q03sawjme2l6n03yt3cnsj29uufe5aes88a79uwecwn8rzwzhw55....The third number (9735 in this case) is the port number. Note this for later. The following field, starting with 'ln', is the lightning DNS seed hostname.
testnet4 nodes may be down, so let’s use the “nc” command to verify our chosen node is up. This is all one line. If this doesn’t return “succeeded”, repeat the test with the following entry and keep going until you find one that is up.
nc -zv ln1qt4dlmn3qdnm66geyezsf6txw4aaygkzy69emzj30emjhxhwgm7lkty97hv.test4.nodes.lightning.wiki 49735The “nc” command will also display the IP address. Make a note of that.
Now, a painful bit. We need to calculate the node ID from this DNS hostname. First, change to the lightning directory and start Python.
cd ~/lightning
pythonCopy and paste the below, making sure to update the DNS hostname on the first line.
hostname="ln1qt4dlmn3qdnm66geyezsf6txw4aaygkzy69emzj30emjhxhwgm7lkty97hv"
import sys
sys.path.insert(0, "contrib/pyln-proto/pyln/proto")
import bech32
hrp, data = bech32.bech32_decode(hostname)
decoded_bytes = bech32.convertbits(data, 5, 8, False)
node_id = bytes(decoded_bytes).hex()
print("Decoded node ID:", node_id)
exit()Now, try to connect to that node using the node ID, IP address and port from before. This is all one line.
lightning-cli connect 02eadfee710367bd6919264504e966757bd222c2268b9d8a517e772b9aee46fdfb 158.220.90.103 49735You should receive a JSON response containing details about your new channel. The next step is to fund the channel. You’ll re-use the node ID from before. We will fund it with 0.001 testnet4 BTC (100,000 satoshis). If you got less than this from the faucet, reduce the funding accordingly.
lightning-cli fundchannel 02eadfee710367bd6919264504e966757bd222c2268b9d8a517e772b9aee46fdfb 100000Make sure you don’t get any errors. If you do, pause and resolve them first. Once funded, you can run the following command to verify everything.
lightning-cli listfundsIt can take up to 6 confirmations before the new channel funding shows up under “listfunds” with a state of “CHANNELD_NORMAL“, so be patient.
Configuring Certificates
We need to change into the testnet4 directory.
cd ~/.lightning/testnet4We’ll configure a custom openssl.conf file, as the command lines will get pretty long soon. Create openssl-server.conf and add the below to it. Update the “CN” and “IP.1” fields with the IP address of your machine.
prompt = no
[req]
distinguished_name = server_distinguished_name
x509_extensions = server_extensions
[server_distinguished_name]
CN = 192.168.80.250
[server_extensions]
subjectAltName = @alt_names
extendedKeyUsage = serverAuth
[alt_names]
IP.1 = 192.168.80.250We can now generate the server certificate and sign it with the CA certificate. First, we need to create a private key.
openssl ecparam -name prime256v1 -genkey -noout -out server-key.keyNow we can generate the public certificate (you need to paste all of this in at once).
openssl req -new \
-key server-key.pem \
-out server.pem \
-nodes \
-CA ca.pem -CAkey ca-key.pem \
-config openssl-server.conf -days 7300You can use the following commands to look at the server certificate fields.
openssl x509 -in server.pem -text -nooutWe now need to make all of this config active.
exit
sudo systemctl stop cl-testnet
sudo systemctl start cl-testnet
sudo su - cl-testnetBackup of testnet4
Before we go any further, let’s configure a backup to Amazon S3. Although this is a testnet and you don’t have real Bitcoin to lose, it is good practice because if this were real Bitcoin, with a funded channel, and you lost the data, you could lose real Bitcoin.
Let’s become the root user. Paste these in one at a time. It won’t work if you paste them both in at the same time.
exit
sudo su -Let’s create a script called backup-cl-testnet using your favourite editor.
#!/usr/bin/env bash
systemctl stop cl-testnet
tar -c --exclude=lightning-rpc /home/cl-testnet/.lightning | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --symmetric -o - | \
aws s3 cp - s3://pdath-bitcoin-backups/cl-testnet.tar.pgp
systemctl start cl-testnetThe script stops Core Lightning. We need to do this to put the SQLite database into a consistent, safe state for backup. Then we do the backup. Lastly, we restart Core Lightning.
Once you have saved the script, mark it as executable.
chmod +x backup-cl-testnetRun the script to verify it works.
./backup-cl-testnetNext, let’s test the backup.
aws s3 cp s3://pdath-bitcoin-backups/cl-testnet.tar.pgp - | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --decrypt | \
tar -tFinally, let’s schedule the backup to run weekly. If you use Lightning frequently, you might do this daily (or more often). We’ll use cron to do this.
crontab -eI’m going to do my backups at midnight every Sunday.
0 0 * * 0 /root/backup-cl-testnetLet's go back to being our standard privileged user.
exitConnecting a wallet to testnet4
Become the testnet4 user again.
sudo su - cl-testnetWe need to add the CA certificate to our phone. Android requires the phone to have a *.cer extension, so let’s create a symlink.
cd ~/.lightning/testnet4/
ln -s ca.pem ca-testnet4.cerWe will make this available via a temporary web server. First, get your machine’s IP address.
ip -4 addrThen start the temporary web server.
python3 -m http.server 8080On your phone, open a web browser and download the ca-testnet4.cer file. Change the IP address to your server’s IP address.
http://192.168.80.250:8080/ca-testnet4.cerIf the phone asks if you want to keep the file, tap “Keep”. If it warns you that the file must be installed via settings, then (for a Samsung phone) go:
Click on Settings/Security & Privacy/More Security Settings
Under “Credential storage” click on “Install from device storage”.
Select “CA Certificate”.
Select “Install anyway”.
Navigate to the location where your certificate is saved, then click “Done”.
You can now stop the mini web-server with CTRL-C.
Now we need to generate a “rune” for Zeus wallet.
lightning-cli createruneUse the following command to generate an onscreen QR code. Change 192.168.80.250 to be the IP address of your server, and the rune “EA…==” to be the rune from above.
echo "clnrest://192.168.80.250:43010?rune=EA...==" | qrencode -t ansiutf8Start the Zeus wallet on your mobile device. If you haven’t yet created a testnet4 wallet, then select:
Advanced Setup
Create or connect a wallet
Otherwise, click the round picture icon in the top-right corner to show the wallets, then click “+” to add a wallet.
In the top right-hand corner, choose the [ ] option, and scan the QR code.
Optionally specify a nickname for your node. Something like “Testnet4”.
Turn on “Certificate Verification”.
Click “Save Wallet Config”
Inbound Liquidity
You now have everything you need to send Lightning on testnet4. However, you can not receive Lightning unless you have “inbound liquidity”.
How can you get that?
Every time you send Lightning, you gain inbound liquidity. If you send 1000 sats to someone, you can then receive 1000 sats.
This is fine if you want to use Lightning to spend Bitcoin. However, it is a significant issue if you primarily want to receive payments, such as regular payments from a Bitcoin mining pool, or if you want to DCA buy regularly.
As of the time I wrote this article, I could not find any services that allow you to obtain inbound liquidity on testnet4. On mainnet, you can get inbound liquidity by buying it from services like https://amboss.space/magma or from a swap service like https://boltz.exchange/.
mainnet
Now that we have done a practice run on testnet4, it’s time to do it again, but on mainnet and using real Bitcoin!
Make sure you start this section with the initial account you use to log in to your Ubuntu machine. You can use “whoami” to check who you are logged in as. If you are logged in with another account, you can use “exit” to return to that initial account.
We need to retrieve the Bitcoin RPC username and password from the ckpool-solo setup we set up in a prior video. The text below should be copied and pasted as a single command.
You can also use the Bitcoin script “share/rpcauth/rpcauth.py” to generate a username or password.
Note the results down for later.
sudo -u ckpool-mainnet grep -e auth -e pass \
/home/ckpool-mainnet/solobtc/ckpool.confNext, we’ll create an account to run the Core Lightning mainnet node, then switch to that account.
sudo useradd -g mainnet -m -s /bin/bash cl-mainnet
sudo su - cl-mainnetWe need to set up the Rust environment.
rustup default stableWe will be using a Python tool called “uv”. We need to prepare a location for it to install its binaries into. We might as well create the standard local bin directory and the directory for storing the Lightning server config in one go.
mkdir -p ~/.local/bin ~/bin ~/.lightningCore Lightning uses the bitcoin-cli command, so we need to symlink that into the cl-mainnet user account.
ln -s /home/knots-mainnet/bin/bitcoin-cli ~/bin/bitcoin-cliWe can now run the “uv” installer.
curl -LsSf https://astral.sh/uv/install.sh | shSeveral changes have been made to our login environment. Let’s log out and back in again to make them all apply.
exit
sudo su - cl-mainnetWe are now ready to clone the Core Lightning repository.
git clone https://github.com/ElementsProject/lightning.git
cd lightningWe are now ready to begin building Core Lightning. Paste these commands one at a time. The Rust compiler is really slow. This is going to take a while.
uv sync --all-extras --all-groups --frozen
COPTFLAGS="-march=native -O2" CDEBUGFLAGS="" ./configure --prefix=~
RUST_PROFILE=release uv run make -j4
RUST_PROFILE=release make installNext, we need to create a config file. Create ~/.lightning/config and add these lines. Fill in the RPC username and password you stored from the beginning of this section.
”alias” lets you define a name for your node. It can be up to 32 characters long. This is an optional field.
bitcoin-rpcuser=rpcadmin
bitcoin-rpcpassword=...
alias=pdath-mainnet
clnrest-host=0.0.0.0
clnrest-port=3010
clnrest-protocol=httpsWe can now test starting the lightning server.
lightningd &We can test accessing it using the CLI:
lightning-cli getchaininfoWe can now stop lightningd from running and exit back to the privileged user.
lightning-cli stop
exitLet’s now set the daemon to run automatically. Invoke your favourite editor and create the file below.
sudo vi /etc/systemd/system/cl-mainnet.servicePaste in these contents:
[Unit]
Description=Core Lightning mainnet daemon
Requires=knots-mainnet.service
Requires=network-online.target
After=knots-mainetnet.service
After=network-online.target
[Service]
User=cl-mainnet
Group=mainnet
Type=simple
Environment=PATH=/home/cl-mainnet/bin:/usr/local/bin:/usr/bin:/bin
WorkingDirectory=/home/cl-mainnet/
PIDFile=/home/cl-mainnet/.lightning/lightningd-bitcoin.pid
ExecStart=/home/cl-mainnet/bin/lightningd
ExecStop=/home/cl-mainnet/bin/lightning-cli stop
RestartSec=30
Restart=always
PrivateTmp=yes
ProtectSystem=full
NoNewPrivileges=yes
PrivateDevices=yes
[Install]
WantedBy=multi-user.targetLet’s enable and start the new service (paste in the commands one at a time).
sudo systemctl daemon-reload
sudo systemctl enable cl-mainnet.service
sudo systemctl start cl-mainnet.service
sudo systemctl status cl-mainnet.service
sudo journalctl -xeu cl-mainnet.serviceNow we need to create a new on-chain mainnet wallet address and fund it. Once that is done, we can open a channel and fund that channel.
sudo su - cl-mainnet
lightning-cli newaddrYou now need to send some real Bitcoin from your wallet to this address. It is recommended that you don’t open channels smaller than 0.01 BTC (1 million SATs). You’ll want to transfer slightly more than this to cover fees.
I’m going to give an example of connecting to the NiceHash Lightning node, which has a relaxed limit of 0.005 BTC. You don’t have to be a customer of NiceHash, do KYC, or have a NiceHash login to be able to connect to their Lightning node.
A good starting amount to transfer might be .006 BTC (600,000 SATS). .005 BTC (500,000 SATs) will be used for the channel, leaving us a little bit left over to pay fees.
Once you have transferred the Bitcoin, use the commands below to verify receipt.
lightning-cli listfundsIt’s probably going to take a while. This is a good time to take an hour-long break.
Keep running the command above periodically until you see the Bitcoin appear.
Opening a channel on mainnet
Let’s open a mainnet Lightning channel. You can use a service like https://1ml.com/ to search for a node, but in this example, we will use NiceHash because it accepts channels as small as 0.005 BTC (500,000 SATS).
You can get information about the NiceHash node here:
https://www.nicehash.com/nicehash-lightning-network-node
Note the node ID, IP address and port. You can copy these at the very bottom of the page under “Connect to the NiceHash Lightning Network Node” below the QR code.
037659a0ac8eb3b8d0a720114efc861d3a940382dcfa1403746b4f8f6b2e8810ba@34.78.139.195:9735Now, try to connect (this is all one line).
lightning-cli connect 037659a0ac8eb3b8d0a720114efc861d3a940382dcfa1403746b4f8f6b2e8810ba@34.78.139.195:9735You should receive a JSON response containing details about your new channel. The next step is to fund the channel. You’ll re-use the node ID from before. We will fund it with 0.005 BTC (500,000 satoshis).
lightning-cli fundchannel 037659a0ac8eb3b8d0a720114efc861d3a940382dcfa1403746b4f8f6b2e8810ba 500000Make sure you don’t get any errors. If you do, pause and resolve them first. Once funded, you can run the following command to verify everything.
lightning-cli listfundsIt can take up to 6 confirmations before the new channel funding shows up under “listfunds” with a state of “CHANNELD_NORMAL“, so be patient.
Configuring Certificates
We need to change into the lightning mainet directory.
cd ~/.lightning/bitcoinWe’ll configure a custom openssl.conf file, as the command lines will get pretty long soon. Create openssl-server.conf using your favourite editor and add the below to it. Update the “CN” and “IP.1” fields with the IP address of your machine.
prompt = no
[req]
distinguished_name = server_distinguished_name
x509_extensions = server_extensions
[server_distinguished_name]
CN = 192.168.80.250
[server_extensions]
subjectAltName = @alt_names
extendedKeyUsage = serverAuth
[alt_names]
IP.1 = 192.168.80.250We can now generate the server certificate and sign it with the CA certificate. First, we need to create a private key.
openssl ecparam -name prime256v1 -genkey -noout -out server-key.keyNow we can generate the public certificate (you need to paste all of this in at once).
openssl req -new \
-key server-key.pem \
-out server.pem \
-nodes \
-CA ca.pem -CAkey ca-key.pem \
-config openssl-server.conf -days 7300You can use the following commands to look at the server certificate fields.
openssl x509 -in server.pem -text -nooutWe now need to make all of this config active.
exit
sudo systemctl stop cl-mainnet
sudo systemctl start cl-mainnet
sudo systemctl status cl-mainnet
sudo su - cl-mainnetBackup of mainnet
Before we go any further, let’s configure a backup to Amazon S3. If you don’t do this, and something happens to your machine, you could lose real Bitcoin.
Let’s become the root user. Paste these in one at a time. It won’t work if you paste them both in at the same time.
exit
sudo su -Let’s create a script called backup-cl-mainnet using your favourite editor.
#!/usr/bin/env bash
systemctl stop cl-mainnet
tar -c --exclude=lightning-rpc /home/cl-mainnet/.lightning | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --symmetric -o - | \
aws s3 cp - s3://pdath-bitcoin-backups/cl-mainnet.tar.pgp
systemctl start cl-mainnetThe script stops Core Lightning. We need to do this to put the SQLite database into a consistent, safe state for backup. Then we do the backup. Lastly, we restart Core Lightning.
Once you have saved the script, mark it as executable.
chmod +x backup-cl-mainnetRun the script to verify it works.
./backup-cl-mainnetNext, let’s test the backup.
aws s3 cp s3://pdath-bitcoin-backups/cl-mainnet.tar.pgp - | \
gpg -v --batch --passphrase-file /root/.passphrase.txt --decrypt | \
tar -tFinally, let’s schedule the backup to run weekly. If you use Lightning frequently, you might do this daily (or more often). We’ll use cron to do this.
crontab -eI’m going to do my backups at midnight every Sunday.
0 0 * * 0 /root/backup-cl-mainnetLet’s go back to being our standard privileged user.
exitConnecting a wallet to mainnet
Become the mainnet user again.
sudo su - cl-mainnetWe need to add the CA certificate to our phone. Android requires the phone to have a *.cer extension, so let’s create a symlink.
cd ~/.lightning/bitcoin/
ln -s ca.pem ca-mainnet.cerWe will make this available via a temporary web server. First, get your machine's IP address.
ip -4 addrThen start the temporary web server.
python3 -m http.server 8080On your phone, open a web browser and download the ca-mainnet.cer file. Change the IP address to your server's IP address.
http://192.168.80.250:8080/ca-mainnet.cerIf the phone asks if you want to keep the file, tap “Keep”. If it warns you that the file must be installed via settings, then (for a Samsung phone) go:
Click on Settings/Security & Privacy/More Security Settings
Under “Credential storage” click on “Install from device storage”.
Select “CA Certificate”.
Select “Install anyway”.
Navigate to the location where your certificate is saved, then click “Done”.
You can now stop the mini web-server with CTRL-C.
Now we need to generate a “rune” for Zeus wallet.
lightning-cli createruneUse the following command to generate an onscreen QR code. Change 192.168.80.250 to be the IP address of your server, and the rune “EA…==” to be the rune from above.
echo "clnrest://192.168.80.250:3010?rune=EA...==" | qrencode -t ansiutf8Start the Zeus wallet on your mobile device. If you haven’t yet created a wallet, then select:
Advanced Setup
Create or connect a wallet
Otherwise, click the round picture icon in the top-right corner to show the wallets, then click “+” to add a wallet.
In the top right-hand corner, choose the [ ] option, and scan the QR code.
Optionally specify a nickname for your node. Something like “Mainnet”.
Turn on “Certificate Verification”.
Click “Save Wallet Config”
Inbound Liquidity
You now have everything you need to send Lightning on mainnet. However, you can not receive Lightning unless you have “inbound liquidity”.
How can you get that?
Every time you send Lightning, you gain inbound liquidity. If you send 1000 sats to someone, you can then receive 1000 sats.
This is fine if you want to use Lightning to spend Bitcoin. However, it is a significant issue if you primarily want to receive payments, such as regular payments from a Bitcoin mining pool, or if you want to DCA buy regularly.
You can purchase inbound liquidity from services like https://amboss.space/magma or a swap service like https://boltz.exchange/

