How to Accept Bitcoin

January 30, 2026  |  Bitcoin  ·  Programming  ·  Self-Hosting  ·  Linux  ·  Pinned

Who This Guide Is For

This guide is mainly for open‑source software developers who want to accept donations or sell paid services for bitcoins. Ideally, you should have basic Linux knowledge and some self‑hosting experience.

You can also share your node with friends and other open‑source projects. Collectively owned nodes reduce costs, simplify maintenance, and let people without Linux ops skills integrate Bitcoin payments into their work, accepting payments from anywhere in the world with no exceptions and no red tape.

Why Bitcoin

A lot of developers I know have to work around sanctions or simply don’t want to form legal entities or kneel to financial institutions. Open‑source software needs open‑source money. As you’ll see, self‑hosting your own “bank account” is pretty easy. You stay in full control, no paperwork or permission needed to send or receive money.

Expected Functionality

After finishing this guide, you’ll have a fully functional Bitcoin and Lightning node that can create and process invoices via a straightforward REST API. You can also use this server to make Bitcoin payments or withdraw funds to your personal wallet.

Fixed on‑chain addresses are great for donations, while programmable Lightning invoices let you integrate Bitcoin payments into your backend. With this setup, you can sell premium features or offer a hosted version of your FOSS software for a monthly fee, for example.

High‑Level Technical Overview

My distro of choice for this setup is Arch Linux, but you should be able to reproduce it on any popular Linux distro. They’re all Systemd‑based, and that’s the only hard dependency.

The two moving parts (bitcoind and lnd) are self‑contained binaries, each wrapped in a Systemd service for easy daemon management and better observability.

The core priority here is simplicity and easy maintenance, so I won’t cover security hardening. For small amounts of money, that’s often overkill. It’s usually easier to sweep the funds out periodically than to be paranoid. There’s no point in hardening a system that might only bring in $100 in donations and other payments, a harsh reality for many open‑source projects, especially early on.

Part 1: Preparing the Server

You’ll need a server with a public IP address. Aim for at least 2 GB of RAM and ideally 2+ (v)CPUs. I’ve run this setup on old, slow hardware before, and it worked fine. If you don’t have enough RAM, adding swap will help you avoid the OOM killer.

Provision a fresh server and log in via SSH as root. This guide prioritizes simplicity, so we won’t create extra users or set up complicated permissions.

Because we’ll use Let’s Encrypt, make sure you have a domain name that you can point to this server so HTTPS works from the start. This guide uses lnd.example.com, and you’re supposed to change it to your actual domain.

Part 1: bitcoind

Getting the Binary

You need to download a binary called bitcoind and place it in the /usr/local/bin/ directory. You can also put the bitcoin-cli binary there, it’s a handy admin interface for your node.

For a detailed walkthrough on downloading Bitcoin binaries, check out one of my other posts: Bitcoin 29 Unpacked.

Config

Create /root/.bitcoin/bitcoin.conf:

# Enable RPC but only allow local connections
server=1
rpcbind=127.0.0.1
rpcallowip=127.0.0.1

# Measured in MB
# Can be adjusted or disabled depending on available disk space
prune=25000

# Improved Systemd integration
startupnotify=systemd-notify --ready
shutdownnotify=systemd-notify --stopping

Systemd Service

Create /etc/systemd/system/bitcoind.service:

[Unit]
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/local/bin/bitcoind -datadir=/root/.bitcoin

[Install]
WantedBy=multi-user.target

Sync

systemctl enable --now bitcoind.service

Your Bitcoin node will start syncing. It stores all its data in /root/.bitcoin/, so expect that directory to grow steadily.

Wait for your node to finish syncing. This can take anywhere from hours to days. Here’s how you can check the progress:

bitcoin-cli getblockchaininfo
{
  "chain": "main",
  "blocks": 934319,
  "headers": 934319,
  "bestblockhash": "000000000000000000018b7caca393ae6b0e009828b03d91fbdc415d70d6abd2",
  "bits": "1701fca1",
  "target": "00000000000000000001fca10000000000000000000000000000000000000000",
  "difficulty": 141668107417558.2,
  "time": 1769759954,
  "mediantime": 1769757145,
  "verificationprogress": 0.9999960291979498,
  "initialblockdownload": false,
  "chainwork": "00000000000000000000000000000000000000010b06672e15fc4dd7fd029150",
  "size_on_disk": 5399547383,
  "pruned": true,
  "pruneheight": 931376,
  "automatic_pruning": true,
  "prune_target_size": 26214400000,
  "warnings": [
  ]
}

You should check the verificationprogress field. If it’s close to 1.0, your node is fully synced and you can move on to installing a Lightning daemon.

Part 2: lnd

Lightning implementations run on top of bitcoind, which is why we installed it first. Now it’s time to add the Lightning layer, this enables cheap, instant payments and provides a straightforward REST API for integrating this borderless, permissionless system into your projects.

Getting the Binary

Grab the latest release from the official GitHub repo:

https://github.com/lightningnetwork/lnd/releases

Once you have the lnd binary, put it in /usr/local/bin/, the same directory where you placed bitcoind and bitcoin-cli in part 1. You should also put lncli there. It’s like bitcoin-cli, but for Lightning.

You’ll need to run lnd manually (and without a config file) the first time to set a wallet password.

Config

Create /root/.lnd/lnd.conf:

[Application Options]
listen=:9735
rpclisten=127.0.0.1:10009
restlisten=0.0.0.0:443
restlisten=[::]:443
letsencryptdomain=lnd.example.com
wallet-unlock-password-file=/root/.lnd/password.txt

[Bitcoin]
bitcoin.mainnet=true
bitcoin.node=bitcoind

[Bitcoind]
bitcoind.rpccookie=/root/.bitcoin/.cookie
bitcoind.rpcpolling=true

Note that you need to create a file /root/.lnd/password.txt manually and put your wallet unlock password there.

Change lnd.example.com to your actual domain or subdomain.

Systemd Service

Create /etc/systemd/system/lnd.service:

[Unit]
Requires=bitcoind.service
After=bitcoind.service

[Service]
Type=notify

ExecStart=/usr/local/bin/lnd
TimeoutStartSec=1200

ExecStop=/usr/local/bin/lncli stop
TimeoutStopSec=3600

Restart=on-failure
RestartSec=60

[Install]
WantedBy=multi-user.target

Sync

systemctl enable --now lnd.service

I’m not sure if you should wait, but I usually let lnd run for a few minutes and check the logs to make sure it’s healthy and there aren’t any errors:

journalctl --unit lnd.service --since '5m ago'

Part 3: Inbound Liquidity

Lightning nodes need inbound liquidity to accept payments. You can ask a friend to open a channel to you, or just buy liquidity from one of the well‑known providers:

https://lnbig.com/#/

Last time I checked, $50k worth of inbound liquidity costs about $10.

Part 4: First Invoice

See this page with examples in Javascript and Python:

https://lightning.engineering/api-docs/api/lnd/lightning/add-invoice/

Here’s a curl example:

curl -X POST \
  -H "Grpc-Metadata-macaroon: 030303" \
  -H "Content-Type: application/json" \
  https://lnd.example.com/v1/invoices \
  -d '{ "value": 25, "memo": "test", "expiry": 3600 }'

On the first REST API call, lnd will try to get a TLS certificate from Let’s Encrypt. Double‑check that your domain is set up and points to your server’s IP address.

Macaroons are essentially bearer tokens. Your lnd instance generated a set of default macaroons in /root/.lnd/data/chain/bitcoin/mainnet/.

Different macaroons have different permissions. For invoices, I recommend using this one to follow the principle of least privilege:

xxd -ps -u -c 1000 /root/.lnd/data/chain/bitcoin/mainnet/invoices.macaroon

You’ll get a HEX string that you should put into the Grpc-Metadata-macaroon header when making REST API calls.

Part 5: Checking Invoice Status

When you create an invoice, the JSON response returns an object with the following structure:

# {
    "r_hash": "<bytes>",
    "payment_request": "<string>",
    "add_index": "<uint64>",
    "payment_addr": "<bytes>",
 }

You should pay attention to these two fields:

  • r_hash - This is the invoice ID, which you’ll use to check the invoice status.
  • payment_request - This is the invoice itself, show it to your users as a string or QR code.

The r_hash field is base64‑encoded, but the invoice lookup endpoint expects it in HEX. Convert it like this:

echo -n "r_hash" | base64 -d | xxd -ps -c 64

API reference:

https://lightning.engineering/api-docs/api/lnd/lightning/lookup-invoice/

curl -H "Grpc-Metadata-macaroon: A012345" https://lnd.example.com/v1/invoice/fa045443 | jq

If the state field is SETTLED, the invoice has been paid and you can take whatever action you need.

Part 6: Further Reading

If you want to get comfortable with bitcoin-cli, here’s a great course:

https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line

To build your LND skills, check out these links:

https://docs.lightning.engineering/

https://lightning.engineering/api-docs/category/api-reference/

If you’re interested in hosting Lightning wallets for friends and other projects, take a look at this nice web‑wallet UI that runs on top of LND:

https://lnbits.com/

Conclusion

While setting up a Bitcoin node isn’t the easiest thing in the world, it’s often simpler than dealing with traditional payment providers. It’s also an indispensable tool for anyone serious about self‑sovereignty.

The truth is, there are no real alternatives if you want to enable truly borderless payments with no exceptions. Open‑source software has no borders, and neither should open‑source money.