The NordBastion polar-bear mascot standing behind a carved Nordic stone merchant counter, a floating cyan holographic checkout panel above it showing a QR code with an amber Bitcoin symbol and a lightning-bolt glyph, and a single unbroken arc of glowing coins running from the panel straight into an open runed vault door beside him, with a server rack and an aurora-lit arched window behind
How-to · Crypto payments·21 min read · 60 min hands-on

Take the payment yourself.
There is nobody left to freeze it.

Every card processor and every hosted crypto gateway sits between you and your customer, takes a percentage, keeps the list of who paid you what, and reserves the right to stop. BTCPay Server is the piece of software that removes them: an open-source, self-hosted, non-custodial payment processor where the coins go straight into a wallet the server cannot spend from. Six steps to a working Bitcoin and Lightning checkout on a box you rent anonymously — the node shape that decides the budget, the watch-only key, the inbound-liquidity problem, and the backup rule that Lightning breaks. The box costs $7.90 a month. Tested on Debian 12.

The six steps
  1. 01

    Choose

    Full, pruned or external node

  2. 02

    Provision

    Domain first, then four ports

  3. 03

    Deploy

    One generator, one command

  4. 04

    Connect

    Watch-only key, never a seed

  5. 05

    Lightning

    Channels and inbound capacity

  6. 06

    Ship

    Checkout, backups, upgrades

Before you start · What a processor is

Bitcoin removed the bank. Most shops put one back.

A payment on a blockchain does not need a company in the middle. The customer signs a transaction, the network confirms it, and the money is yours — there is no acquirer, no settlement window and nobody whose approval the payment depends on. That is the whole point of the thing, and it is why the technology exists at all.

Then a shop needs to actually take payments, and the practical requirements arrive all at once. Every invoice needs its own address, so payments can be told apart. Somebody has to watch the chain and decide when an invoice is paid. The amount has to be quoted in a currency the customer thinks in, at a rate that holds long enough for them to press send. The order status has to flow back into the shop. And it has to work at two in the morning without anyone watching. Building all of that is a project; not building it is why almost everyone signs up with a hosted gateway instead — and, in the space of one form, puts an intermediary back into a system that was designed not to have one.

What that intermediary holds is worth stating plainly, because it is more than the fee. It holds the coins, at least until a payout runs, which means it can delay one. It holds the list: every customer, every amount, every timestamp, every product, in a database you cannot read and cannot delete. It holds an identity file on you, because a company that touches other people's money is required to collect one. And it holds the switch, because the terms of service reserve the right to decide your business is no longer welcome — a decision that arrives by email, applies to the balance as well as the account, and is not, in practice, appealable.

BTCPay Server is the answer to exactly that problem and to nothing else. It is a free, open-source payment processor you run on your own machine: it derives an address per invoice, watches the chain, quotes fiat prices, exposes a checkout and an API, and speaks Lightning. It never holds your coins, because the wallet it watches is one you already own. Four ways to take a crypto payment, and what each one actually costs:

Property A hosted crypto gateway A raw address in the footer BTCPay on somebody's instance BTCPay on your own VPS
Who holds the coinsThe gateway, until payoutYouYou — it watches, it cannot spendYou — it watches, it cannot spend
Who can stop you tradingThe gateway, by emailNobodyThe instance operatorNobody
Who sees every customer and amountThe gateway, permanentlyEveryone — one address, one public totalThe instance operatorYou
Identity document requiredYes, before the first payoutNoWhatever the operator asks forNo
Cost per transactionA percentage, foreverNothingNothingNothing — $7.90 a month, flat
Tells one invoice from anotherYesNo — you reconcile by handYesYes
Pick it whenYou want zero operations and accept the termsDonations, a handful a yearYou want the software but not the serverThe takings are yours and so is the list

The second column is not a joke either — a single address in a footer is genuinely fine for a donation link, and pretending otherwise sells software nobody needs. It stops being fine the moment two people pay the same amount on the same day, or the moment you would rather your annual income were not a public figure anyone can total.

Step 01 · Choose

One decision sets the whole bill. How much chain do you keep?

A payment processor has to be able to answer one question honestly: has this invoice been paid? Answering it means watching the Bitcoin network, and watching it properly means running a node that validates blocks itself rather than believing what somebody tells it. That node is the entire hardware budget of this project — BTCPay itself, the indexer and the database together are a rounding error next to it.

There are three arrangements that make sense and one that does not. An archival node keeps every block ever mined: comfortably past 700 GB in 2026 and growing by roughly 50 GB a year. It is what you want if you also intend to run a block explorer or do historical analysis, and it is overkill for a shop. A pruned node downloads and fully validates every block in exactly the same way, then throws away the old ones and keeps a rolling window. The security model is identical — this is the part people get wrong, because "pruned" sounds like "partial" and it is not — and the deployment generator ships ready-made fragments for it.

An external node is the third: point the stack at a Bitcoin node you already operate, over a private link. If you already followed the Lightning node guide there is no reason to keep a second copy of the chain, and a mesh VPN between the two boxes is the clean way to connect them. The arrangement that does not make sense is pointing a shop at a stranger's public node: it learns every address you are watching, which is your revenue, and it is in a position to lie about whether an invoice was paid.

Deployment Chain on disk Total volume needed What you give up
Pruned, smallest window~5–25 GB~40 GBRescans, and any margin for error. The smallest setting is not recommended.
Pruned, ~50 GB window~50 GB~80 GBNothing a shop notices. The sensible floor.
Pruned, ~100 GB window~100 GB~140 GBNothing. The comfortable default, with room for Lightning.
Archival, full history700 GB +~960 GBNothing — but you pay for it every month, forever.
External node you already runNone on this box~40 GBIndependence — the checkout is down when the other box is.
The one thing pruning genuinely costs you

A pruned node cannot rescan blocks it has thrown away. In practice that means it cannot reconstruct the history of a wallet whose transactions predate the deployment — the indexer scans forward from the day you connect the key, so old payments to that account will simply not appear in the store. This is not a problem if you do what step 04 recommends and give the shop a freshly created account; it is a genuinely confusing afternoon if you connect a wallet you have been using for three years and half its history is missing. Decide which you are doing before you install, not after.

One more decision belongs here rather than later, because it changes what the installer builds: whether you want Lightning at all on day one. On-chain-only is a complete configuration and a perfectly respectable place to launch from. Adding Lightning later is supported and not painful, but it is a second daemon with its own funds and its own backup rules, and there is a lot to be said for proving that real money arrives through the simple path before you introduce the complicated one.

Step 02 · Provision

The domain has to exist first. The installer will ask for a certificate.

Order the box. In the panel: Order → VPS → Garrison, image Debian 12, in whichever bastion you want the shop's records to live in — Helsinki, Stockholm, Oslo or Reykjavík. Garrison is the right default because it holds a 100 GB pruned chain, a Lightning node and the database without you having to think about disk again for years. Sentinel works with a tighter prune; Bulwark is the tier for an archival node. No email address is required to open the account and the invoice settles in Monero, Bitcoin, Lightning or any other supported asset — which is the pleasing part of this particular project, since you are building a machine to take crypto payments and paying for it with one.

Then run the first-hour hardening checklist before anything else — key-only SSH on a moved port, unattended upgrades, a default-deny firewall. This box will eventually hold the record of every customer you have and the key material of a Lightning node with live funds in it. It is not a box to leave with password authentication for a week.

Now the DNS record, and do it before you run the installer rather than after. Pick the hostname the checkout will live on — shop.example.com, pay.example.com, whatever fits — and point an A record at the VPS address, plus an AAAA if you want it reachable over IPv6. Let it propagate. The reverse proxy the installer sets up will try to obtain a certificate for that exact name during its first boot, using an HTTP challenge that requires the name to already resolve to this machine. A record that is not live yet is comfortably the most common way a first install ends in a confusing error.

The hostname is not quite permanent, but treat it as if it were

The deployment ships a script for changing the domain later, so this is recoverable rather than fatal. What is not recoverable in one command is everything downstream: the certificate is reissued, every pay button and webhook URL you have handed out points at the old name, a Lightning address is a name at a domain and moves with it, and any shop plugin needs reconfiguring. Choose a name you are willing to keep on the letterhead for years, and prefer a subdomain of a domain you already publish over a new one that describes what the machine does.

Then write the firewall rules while nothing is listening yet. Four ports, and only four. The checkout is a public HTTPS endpoint by necessity — a customer's browser has to reach it — and port 80 stays open because the certificate renewal uses it. The Lightning peer-to-peer port is only needed if you want other nodes to be able to open channels towards you, which step 05 will argue you do. The Bitcoin peer-to-peer port does not need to be open at all: your node makes outbound connections perfectly well, and opening it inbound only makes it easier to find.

ufw default deny incoming
ufw allow 47222/tcp                  # administrative SSH, moved off 22
ufw allow 80/tcp                     # ACME HTTP-01 challenge and the redirect
ufw allow 443/tcp                    # the checkout
ufw allow 9735/tcp                   # Lightning peer-to-peer, only if you want inbound channels
ufw enable
ufw status numbered

Two resource notes before you continue. Give the box swap if it has none — the initial block download is the memory-hungry hour of this project and 4 GB of RAM with 2 GB of swap behaves far better than 4 GB alone. And check the disk maths from step 01 against the tier you actually ordered, because a node that fills its volume mid-sync does not fail politely; it corrupts its own index and starts again.

fallocate -l 2G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
df -h /                              # confirm the volume matches step 01
Step 03 · Deploy

A generator, not a compose file. You describe it; it assembles it.

The official deployment is not a docker-compose.yml you edit. It is a generator: you describe the deployment you want in environment variables, and a script assembles the compose file from a library of fragments — one for Bitcoin Core, one for the indexer, one for PostgreSQL, one for the BTCPay application, one for each Lightning implementation, one for the reverse proxy, and a long tail of optional extras. This is a better design than it first appears, because upgrades regenerate the file rather than asking you to merge somebody else's changes into yours. It also means the environment block below is the real configuration of your deployment, and worth keeping in the same place as your other infrastructure notes.

Clone the repository and export the environment. Every line below maps directly onto a decision from step 01: the host name is the domain you pointed in step 02, the storage fragment is the prune window you chose, and the Lightning variable is either an implementation or omitted entirely for an on-chain-only launch.

mkdir BTCPayServer && cd BTCPayServer
git clone https://github.com/btcpayserver/btcpayserver-docker
cd btcpayserver-docker

export BTCPAY_HOST="pay.example.com"          # exactly the name you pointed in step 02
export NBITCOIN_NETWORK="mainnet"
export BTCPAYGEN_CRYPTO1="btc"
export BTCPAYGEN_REVERSEPROXY="nginx"
export BTCPAYGEN_LIGHTNING="clightning"       # or "lnd", or omit for on-chain only
export BTCPAYGEN_ADDITIONAL_FRAGMENTS="opt-save-storage-s"   # ~50 GB pruned window
export BTCPAY_ENABLE_SSH=true
export LETSENCRYPT_EMAIL="you@example.com"

. ./btcpay-setup.sh -i

Two details in that block deserve a sentence each. The leading dot in . ./btcpay-setup.sh -i is not a typo — the script is sourced rather than executed, so the environment it sets stays in your shell and the helper commands work afterwards. And the storage fragment name is version-sensitive: the generator has shipped variants pruning to roughly 100, 50, 25 and 5 GB, under names that have been adjusted between releases. Read the fragment list in the repository you just cloned rather than trusting a value copied from a blog post, including this one.

The installer will take a few minutes, pull a lot of images, obtain the certificate and start everything. Then the wait begins: the node has to download and validate the chain before the shop can confirm a payment, which is hours rather than minutes even pruned, because pruning discards blocks after validating them rather than skipping the work. The web interface is available immediately and will show you a synchronisation banner. Do not issue a real invoice until it is gone.

./bitcoin-cli.sh getblockchaininfo | grep -E 'blocks|headers|verificationprogress|pruned'
docker ps --format '{{.Names}}\t{{.Status}}'
Claim the administrator account in the next five minutes

A fresh instance has no users, and the first account to register becomes the administrator. Until you claim it, anyone who reaches the URL can — and the URL is not secret: the moment the certificate was issued, the hostname was published in the public certificate-transparency logs, which are indexed and scanned continuously by people looking for exactly this. Open the site and register before anything else, then check Server Settings and confirm that registration is not open to strangers. This is a five-minute window that people leave open for days because the shop is "not live yet".

Two optional fragments are worth knowing about while you are still in the configuration. One adds a Tor hidden service, so the checkout is reachable at a v3 onion address as well as the clearnet one — useful for customers who would rather not resolve your domain, and the same idea as the onion service guide. Another adds a second cryptocurrency. Both are set the same way, by adding to the additional-fragments variable and re-running the setup script; the generator rebuilds the compose file and restarts only what changed.

Step 04 · Connect

Give it a public key. Never give it a seed.

Create a store, and then make the one decision on this page that actually matters. BTCPay will offer two ways to attach a Bitcoin wallet: connect an existing one, or let it generate a new one and keep the seed on the server. The first is watch-only and is what you want. The second turns a public, internet-facing machine into a hot wallet holding your revenue, and no amount of hardening makes that a good trade for a box whose entire job is to be reachable by strangers.

The mechanism behind the recommendation is elegant enough to be worth understanding. Modern wallets are hierarchical: one seed derives a tree of keys, and there is an extended public key — an xpub, or its zpub variant for native segwit — from which every receiving address in an account can be computed without any ability to sign. Hand that to the store and it can generate a fresh address for every invoice, watch the chain for payments to each one, and show you the balance. It cannot move a satoshi. The private half stays in your wallet, or on your hardware device, and never travels.

So: open Sparrow, Electrum, or the desktop app for your hardware wallet, create a new account dedicated to this shop, and export its extended public key. In the store's wallet settings choose to connect an existing wallet, paste the key, and check the first few addresses BTCPay derives against the ones your wallet shows. If they match, the derivation path is right and the connection is done. If they do not, you have chosen the wrong script type — the difference between an xpub and a zpub is the difference between two entirely separate sets of addresses.

An extended public key is not a harmless string

It cannot spend, which is the point — but it can enumerate. Anyone who obtains it can derive every address the account will ever use and therefore read every payment it has ever received and will ever receive, for as long as you use it. That is a permanent, unrevokable disclosure of your shop's takings, and it is why the account you connect should be dedicated to the shop rather than the one holding your savings. Treat the key as confidential, keep it out of screenshots and support tickets, and if it does leak, move the shop to a new account rather than hoping.

Now the invoice settings, which are where a shop is actually tuned. Expiry defaults to around fifteen minutes and is a rate lock rather than a technical limit: it is how long you are willing to hold a fiat-denominated price against a volatile asset. Shorten it for a volatile market, lengthen it if your customers are slow. Confirmations decide when an invoice counts as settled — zero-confirmation is instant and appropriate for a coffee, one confirmation is the ordinary default, and six is for amounts where an hour of waiting is cheaper than the alternative. Underpayment tolerance lets you accept an invoice that arrives a fraction short, which happens constantly when a customer's wallet deducts its fee from the amount rather than adding to it; a small percentage here removes a whole category of support email.

Then test it with real money, not with testnet. Issue a payment request for a trivial amount, pay it from a phone wallet, and watch the invoice move from new to processing to settled. That single transaction proves the node is synchronised, the derivation path is right, the rate provider works, the certificate is valid on a mobile browser, and the money landed in a wallet you control. Nothing else proves all five at once.

Step 05 · Lightning

The channels are the easy part. Being payable is the hard part.

On-chain Bitcoin settles a $400 invoice beautifully and a $4 one badly: the customer waits for a confirmation and pays a fee that may be a noticeable fraction of the purchase. Lightning inverts both — settlement in about two seconds, fees measured in fractions of a cent — which is why a shop selling small things needs it and a shop selling large ones may not. If you set a Lightning implementation in step 03, the daemon is already running and BTCPay has already connected to it; the store settings only need Lightning enabling as a payment method.

Which implementation you chose has consequences worth naming. Core Lightning is the conservative, lightweight option with a clean plugin architecture. LND is the most widely deployed and has the largest ecosystem of management tools around it. There is also a third kind of option now — a lightweight daemon that outsources channel management to a service provider while keeping the keys on your machine — which trades some independence for the disappearance of everything in the next three paragraphs. That is a legitimate choice for a shop that wants Lightning but does not want to become a node operator; it is simply a different trade, not a free one.

A brand-new node cannot be paid, and nothing tells you so

A Lightning channel holds a fixed amount of money, split between the two ends. When you open a channel and fund it, all of that money is on your side — which is capacity to send, and exactly zero capacity to receive. A shop needs the opposite. So a freshly funded node with three healthy channels can still fail every single incoming invoice, and the failure looks like a routing error rather than a configuration mistake. This is the single most common reason a self-hosted Lightning checkout appears broken on the first day.

There are three honest ways to get inbound capacity, and they are not mutually exclusive. You can open outbound channels and then spend through them — buying something, paying an invoice, moving money to an exchange — which pushes your balance to the far side and converts sending capacity into receiving capacity. You can ask a well-connected node to open a channel towards you, which is a normal request in the Lightning community and free apart from the peer's on-chain fee. Or you can buy inbound liquidity from one of the several services that sell it, which costs a small percentage and takes minutes rather than weeks. There is no affiliate recommendation on this page and there is not going to be one; pick by reputation and current price.

Once you can be paid, two features turn the node into a shop front. A Lightning address — the same shape as an email address, at your own domain — lets anyone send you money from any modern wallet without an invoice existing first, which is the friendliest donation and tipping mechanism available anywhere. And the QR code on the checkout page can offer both rails at once, so a customer with a Lightning wallet pays in seconds and a customer without one pays on-chain, from the same screen and with no decision required from you.

Before the first Lightning payment arrives, do the backup step that has to happen now rather than later. Every implementation keeps a small file describing the current state of your channels — LND calls it a static channel backup — and it is the artefact that lets you recover funds if the machine is lost. It changes every time a channel is opened or closed, so copy it off the box automatically rather than by hand. Read the warning in step 06 before you assume your nightly archive already covers this, because for Lightning specifically it does not.

Step 06 · Ship

Now put it in front of customers. Then keep it boring.

Pick the smallest checkout surface that fits what you already have. A payment request is a link you send someone — no website required, and the fastest way to invoice a client. A point of sale app turns the same instance into a till with a product list, which works from a phone at a market stall. A pay button is a snippet of HTML for a static site. A shop plugin adds Bitcoin as a payment method to a real storefront and writes the order status back automatically. And the REST API issues invoices, reports their state and calls a webhook when one settles, which is the integration that ages best because it depends on nothing but HTTP and a shared secret.

Whichever you choose, wire the webhook and then test that it fires. An invoice that settles and never notifies your application is the failure mode nobody catches, because everything looks correct on both sides in isolation: the money arrived, the customer saw a confirmation page, and the order sits unfulfilled. Send yourself a settled-invoice webhook on purpose, confirm your endpoint received it, and verify the signature rather than trusting the payload — that endpoint is public and anyone can post to it.

A nightly backup is not a Lightning restore path

This is the one warning on this page that can cost you money rather than time, and the project's own documentation states it in the same words: old Lightning channel state is toxic. Restoring last night's copy of a channel database and letting the node run means broadcasting a state your counterparty has already superseded — which the protocol treats as an attempted cheat and punishes by awarding them the whole channel balance. So the archive is right for the store database, the configuration and the invoice history, and it is not a recovery plan for channels. Copy the static channel backup off the machine continuously, keep the newest one, and if you ever restore a whole server from an archive, do it knowing the Lightning side needs the documented recovery procedure rather than a straight restore.

With that understood, the routine is short. The deployment ships a backup script that archives the volumes and dumps the database while skipping the blockchain data, and it accepts a passphrase so the archive is encrypted before it leaves the box. Run it on a timer, then push the result to a machine in a different jurisdiction the way the backups guide describes — an append-only repository is worth the extra ten minutes here, because a compromised payment server should not be able to erase the record of what it processed.

cd ~/BTCPayServer/btcpayserver-docker
export BTCPAY_BACKUP_PASSPHRASE="a long passphrase you keep elsewhere"
./btcpay-backup.sh                   # archives volumes + database, skips the chain
# then ship the archive offsite, and keep the channel backup separately

Upgrades come next, and a payment box is the one machine where "update it later, on purpose" is the correct policy. The deployment has its own update script that regenerates the compose file and pulls the new images; run it when you have read the release notes, not when a cron job feels like it. Take the archive first. Bump one release at a time. And keep the previous version written down, because a rollback you can do in a minute is what makes the whole thing low-stakes.

./btcpay-update.sh                   # deliberate, after reading the release notes
df -h /                              # the check that should be an alert, not a habit

Finally, set one alert and one reminder. The alert is on disk usage — a blockchain node is the one process on the machine that grows on its own, and a full volume takes the checkout down in a way that is slow to diagnose because nothing else changed. Fire it at eighty per cent, not ninety-five. The reminder is monthly, and it is to open the store and look at two things: that invoices are still settling, and that the Lightning channels you opened are still alive. A payment processor fails quietly, and the way you find out is a customer telling you they could not pay.

The honest part · What this does not do

You removed the middleman. You did not become invisible.

Self-hosting the processor is a real and specific improvement, and it is not a disappearing act. Five things stay true after everything above is done, and knowing them is the difference between a defence and a comforting story.

The Bitcoin ledger is public and permanent. Removing the gateway removed a company's private database; it did not encrypt the chain. Every payment your shop receives is visible to anyone who knows an address, forever, and addresses cluster: a wallet that combines two payments into one spend has just told the world those two customers paid the same shop. This is a property of the asset, not of the software, and it is the reason a business with a real privacy requirement looks at Monero as well.

The checkout hostname is published the moment it exists. Certificate transparency logs are public and continuously indexed, so the name you chose in step 02 is discoverable even if you never link to it. That is why the administrator account has to be claimed in the first minutes rather than the first week, and why the login page deserves a strong password and a second factor from day one.

The exit to fiat is where identity comes back. Nothing about this stack requires you to identify yourself to accept a payment. Converting the proceeds into bank money almost always does, because that conversion happens at a regulated exchange or broker. Self-hosting moves the identity requirement from the point of sale to the point of exit — a genuine improvement in exposure, and not the same thing as its absence.

Your reporting obligations did not move with the server. Where a business is registered and where its owner lives decide what is owed and what has to be declared. The jurisdiction of the machine issuing the invoices does not enter into it. In most places, receiving crypto for goods or services is ordinary income at the value on the day, and disposing of it later is a separate event again. A self-hosted instance is a better record than a processor account you might lose — it is not a way of not having one.

There are no chargebacks, and that cuts both ways. A confirmed payment is final, which is exactly the property that makes fraud against a merchant nearly impossible and is why the fee is zero. It is also why an unhappy customer has no mechanism at all beyond your goodwill. Publish a refund policy, use the payout feature to honour it, and understand that you have taken on the dispute-resolution job that the percentage used to pay for.

What you have actually bought, precisely: the money arrives in a wallet only you can spend from, nobody can stop the sale, nobody takes a cut, and the list of who paid you what exists only on a machine you rent under no particular name. That is a narrow claim and a real one. It is worth what it costs only if step 06 is done — a self-hosted payment processor without a tested restore is not a business improvement, it is a single point of failure holding your revenue.

Sizing · What to order

The application is tiny. The chain is the whole invoice.

Tier Monthly Resources What it comfortably runs
Sentinel$3.902 vCPU · 4 GB · 120 GB · 1 GbpsA 25–50 GB pruned node with Lightning. Add swap; watch the disk.
Garrison$7.904 vCPU · 8 GB · 240 GB · 1 GbpsA 100 GB pruned node, Lightning and years of headroom. The default.
Ravelin$16.908 vCPU · 16 GB · 480 GB · 2.5 GbpsThe same, plus a second chain or a busy store with plugins.
Bulwark$32.9016 vCPU · 32 GB · 960 GB · 5 GbpsAn archival node with room to keep growing. Only if you need the history.

BTCPay, the indexer and PostgreSQL together are a few gigabytes and almost no CPU once the node is synchronised; a shop taking a hundred payments a day will not trouble the smallest tier on compute. What the tiers differ on is disk, and disk is decided entirely by step 01. Sync is the only genuinely heavy hour of the project — it is IO-bound rather than CPU-bound, which is why NVMe matters here and core count does not. Bandwidth is unlimited on every tier, so a publicly reachable Lightning node costs nothing extra to run.

Field notes · Six traps

Six ways this goes wrong. Two of them cost money.

Trap 01 · Fatal

The administrator account nobody claimed

The first person to register on a fresh instance becomes the administrator, and the hostname was published in certificate-transparency logs the moment the certificate was issued. Register within minutes of the install finishing, then confirm in Server Settings that registration is not open to strangers.

Trap 02 · Fatal

The hot wallet on the public box

Letting the server generate the wallet is one click and puts the seed on a machine whose job is to answer strangers. Connect an extended public key instead: the store watches, derives and reconciles, and cannot spend. Keep the private half in a wallet or on a hardware device.

Trap 03 · Toxic

Restoring last night's Lightning state

A channel database from yesterday describes a state your counterparty has superseded. Bringing it back online can hand them the entire channel balance as a penalty. The nightly archive covers the store; channels need the documented recovery path and a continuously copied channel backup.

Trap 04 · Silent

Lightning invoices nobody can pay

Funding a channel gives you capacity to send, not to receive. A new node with money in it still fails every incoming payment, and the error looks like a routing problem. Get inbound liquidity before you advertise the Lightning option, not after the first complaint.

Trap 05 · Confusing

A pruned node and a wallet with a past

Connect a three-year-old account to a pruned node and its old transactions never appear, because the blocks that contained them were discarded. Nothing is broken and nothing says so. Give the shop a freshly created account, or run archival.

Trap 06 · Avoidable

The volume that filled during sync

A node is the one process that grows on its own, and a full disk corrupts the index rather than pausing politely — which means starting the download again. Do the arithmetic from step 01 against the tier you ordered, and alert at eighty per cent rather than ninety-five.

FAQ · Self-hosted Bitcoin payments

Questions, answered.

Eleven questions that come up while building this — starting with the two that decide whether you should build it at all.

Is BTCPay Server really free, or is there a catch?

It is genuinely free, and the shape of that matters more than the price. BTCPay Server is open-source software released under a permissive licence and maintained by a foundation rather than a company with a revenue target; there is no per-transaction fee, no percentage, no minimum volume, no account to open and no contract to sign, because there is no counterparty at all — you are running a program on your own server the way you run a web server. What you pay instead is real but different: a few dollars a month for the box, an evening to set it up, and the standing obligation to keep a payment-handling service patched. The comparison people should actually make is not "free versus one per cent". It is "an operational task you own versus a dependency you do not", and the honest answer is that a shop taking twenty payments a year is probably better served by something hosted, while a shop taking twenty a week gets both cheaper and freer here.

Is it custodial? Who actually holds the coins?

You do, and the mechanism is worth understanding rather than trusting. The recommended setup gives BTCPay an extended public key from a wallet you already control. From that key the server can derive a fresh receiving address for every invoice and watch the chain for payments to it — and it cannot construct a valid spend, because the private half never leaves your wallet. Money moves from the customer straight into an address only you can sweep; nothing is ever held on the server, so there is nothing to withhold, freeze, or lose in a breach. BTCPay will also offer to generate a wallet for you and keep the seed on the server, which is convenient and is the one configuration this guide argues against for anything holding real revenue: it converts a public, internet-facing box into a hot wallet. Lightning is the genuine exception — channel funds have to be live to route — which is why the Lightning balance should be treated as a till rather than a vault.

How much disk do I actually need?

It depends entirely on one decision, and the range is two orders of magnitude. An archival Bitcoin Core node keeps every block ever mined: comfortably past 700 GB in 2026 and adding roughly 50 GB a year, so it needs a tier sized for it and it will keep needing more. A pruned node validates every block exactly the same way and then discards the old ones, keeping a rolling window — the deployment generator ships fragments that prune to about 100, 50, 25 or 5 GB, and anything from 50 GB up is a perfectly serious production choice for a merchant. Add roughly 10 to 20 GB for the indexer, the database, the Lightning node and the operating system, and the arithmetic falls out: a 120 GB tier runs a 25–50 GB pruned deployment, a 240 GB tier runs a 100 GB one with years of headroom, and archival wants 960 GB. Pick pruned unless you have a specific reason not to; the security model is identical.

Can I run BTCPay without my own Bitcoin node?

Partly, and it is worth knowing exactly where the line is. The stack can be pointed at a Bitcoin node you already run somewhere else — a box in the same tailnet, or one behind a tunnel — and that is a fully supported arrangement that saves you a second copy of the chain if you have already done the work in the Lightning node guide. What you cannot sensibly do is run a payment processor against somebody else's public node, and the reason is not licensing but privacy and trust: whoever answers your queries learns every address your shop is watching, which is the same as learning your revenue, and can also lie to you about whether an invoice was paid. There is a third option people reach for — using a hosted BTCPay instance run by a third party — and it is a perfectly reasonable way to start. Just be clear that it moves the store's data to an operator who can see it, which is most of what self-hosting was for.

Do I have to do the Lightning part?

No, and it is entirely reasonable to skip it on day one. On-chain-only is a complete, working configuration: invoices are paid, the customer waits for a confirmation, the shop settles. What Lightning buys is the difference between "roughly ten minutes and a fee that hurts on small amounts" and "two seconds and a fraction of a cent", which is the difference between a checkout that works for a $400 invoice and one that also works for a $4 one. The cost is that Lightning is a second daemon with its own funds, its own failure modes and its own backup discipline — including the inbound-liquidity problem in step 05 and the toxic-state warning in step 06. A sensible order is to launch on-chain, confirm the whole pipeline works with real money, and add Lightning as a deliberate second project rather than bolting it on in the same evening.

Which wallet should I connect to the store?

Any wallet that can export an extended public key for a single account — Sparrow, Electrum, BlueWallet, or a hardware wallet through one of those — and the important part is not the brand but that it is a dedicated account rather than the one holding your savings. Two reasons. The first is privacy and it is permanent: an extended public key is a master index of every address it will ever derive, so anyone who obtains it can enumerate every payment that account has ever received and will ever receive. Giving the store its own account means a leak exposes the shop's takings and nothing else. The second is operational: a dedicated account makes the shop's income legible without you doing forensics on a mixed history, and it makes sweeping to cold storage a single, boring decision. A hardware wallet holding the private key with the store watching over the public one is the arrangement to aim for.

Can I accept Monero, or other coins, on the same instance?

Yes, with the caveat that each additional chain is a whole additional node with its own disk, its own synchronisation time and its own maintenance. The generator supports a second and third cryptocurrency directly, and there is a Monero integration available as an additional fragment — the same deployment then presents the customer with a choice of rails at checkout. What people underestimate is the resource bill: every chain you enable is another daemon indexing another ledger on the same volume, and two chains on a tier sized for one is how a payment box runs out of disk on a Sunday. If Monero is important to your shop, read the Monero node guide first and consider giving it its own box; a payment processor that is down because an unrelated daemon filled the disk is worse than one that only takes Bitcoin.

How does this plug into an actual shop?

Four ways, in increasing order of how much software you already have. If you have nothing, BTCPay ships a hosted point of sale and a payment request page you can send as a link — that is enough to invoice a client or take payments at a market stall with no website at all. If you have a static site, a pay button is a snippet of HTML. If you have a real storefront, there are maintained plugins for the common e-commerce platforms that turn Bitcoin into another payment method at checkout with the order status wired back automatically. And if you have your own application, the REST API issues invoices, reports their state, and calls a webhook you specify when one settles — which is the integration that ages best, because it depends on nothing but HTTP. Start with a payment request, prove the money arrives, then integrate.

What happens if the VPS dies — do I lose money?

For on-chain, no, and this is the quiet reward of the watch-only design: the coins were never on the server. Addresses are derived from a public key, so a fresh install with the same extended public key rediscovers every payment; what you lose in a total failure is the store's configuration and its invoice history, which is what the backup script is for. Lightning is a genuinely different answer and it deserves its own sentence: channel funds are live, the channel state changes with every payment, and restoring an old copy of that state can cost you the channel balance outright, because publishing a superseded state is exactly what the protocol punishes. So the correct posture is asymmetric. Back up the database and configuration on a schedule, encrypt it, send it somewhere that is not this machine — and treat the Lightning static channel backup as a separate, continuously-copied artefact rather than a line in the nightly archive.

Does self-hosting the processor mean I have no reporting obligations?

No, and it is worth being blunt about it because the opposite is quietly assumed. Removing the payment processor removes an intermediary, a fee and a party that could freeze your funds. It does not change what you owe, to whom, or whether a sale is taxable — those follow from where you and your business are, not from where the server is or which software issued the invoice. In most jurisdictions receiving cryptocurrency for goods or services is ordinary business income at the value on the day it was received, and disposing of it later is a separate event again; the fact that the record now lives in your own database rather than a processor's does not make it optional, and a self-hosted instance is in practice a better audit trail than a screenshot from an account you no longer have. Where the honest privacy gain sits is narrower and real: no third party holds a running list of your customers and amounts. Ask an accountant in your own jurisdiction about the rest.

Does NordBastion allow a payment processor on a VPS?

Yes — a BTCPay instance is an ordinary web application with an ordinary database and an ordinary blockchain daemon, and there is nothing in the acceptable-use policy that treats it differently from a shop or a blog. The practical notes are about resources rather than policy: a synchronising node is disk-heavy and IO-heavy for its first day and then almost idle, bandwidth is unlimited on every tier so a public Lightning node is not metered, and inbound ports for peer connections are yours to open. The part that lines up with the rest of the site is the symmetry: the account does not need your legal name, the invoice settles in Monero, Bitcoin or Lightning, and the machine that now knows every customer you have is rented from a company that does not know who you are. A processor you self-host on an identified account has moved the ledger, not the exposure.

Get the box

A checkout of your own, on an account that is not your name. KYC-free, crypto-paid.

Garrison — 4 vCPU, 8 GB and 240 GB of NVMe for $7.90 a month, in Helsinki, Stockholm, Oslo or Reykjavík. Enough for a pruned Bitcoin node, a Lightning daemon, the indexer and the store, with headroom for years. No email at signup, no identity document, and no company holding the list of everyone who has ever paid you.

Last reviewed · 2026-10-12 · Sources · BTCPay Server documentation (Docker deployment, storage fragments, backup and restore, store settings), Bitcoin Core documentation on pruning, Lightning implementation documentation on static channel backups · Cadence · yearly

Private hosting, built in the North.