Self-Hosting a BTCPay Server on a VPS

What a self-hosted BTCPay node really needs in CPU, RAM and NVMe, and why a no-KYC VPS at $15.20/mo annual-eff fits the model.
What a self-hosted BTCPay node really needs in CPU, RAM and NVMe, and why a no-KYC VPS at $15.20/mo annual-eff fits the model.
Why settlement ownership beats a hosted processor
The loud case for self-hosting payments is ideology; the durable case is who holds the settlement key. A hosted processor generates invoices on infrastructure somebody else controls, then releases your funds after a review window. BTCPay generates the invoice on your own node and pays your wallet directly, with 2 BTC confirmations landing in roughly 10 to 60 minutes. Nobody in that loop can freeze the checkout, hold reserves, or demand your customers' papers, and that difference is operational rather than philosophical.
The mechanism sits in three pieces a single VPS hosts comfortably. The Bitcoin node, full or pruned, validates blocks and answers balance queries from chain data instead of an API's word. BTCPay Server itself, a .NET application, renders invoices and watches the chain through that node. The deployment path the project documents is Docker-based, and btcpayserver-docker wires the pieces together with sane defaults. You end up with one box, one hostname, and no third party anywhere in the request path between buyer and wallet.
Where the naive version breaks is trust that was never actually moved. Operators point BTCPay at a public block explorer for balance checks and keep exactly the risk they tried to escape: the explorer can lie, throttle, or disappear at 3 a.m. with your settlement queries unanswered. The whole exercise exists so amounts verify against chain data the node checked itself, so treat any query answered remotely as a gap in the design.
The second classic mistake is sizing by price instead of by workload. A storage plan offering 1 to 5 TB of bulk NVMe looks tempting and is the wrong shape entirely: chain validation wants fast small-file reads and steady single-thread CPU, not archive capacity, and the 40 GB floor of the entry tier fills even pruned. If you size by the sticker first, the next section is the correction — it explains what the node actually computes before it earns a single invoice.
VPS from $3* (fund $50+), storage 1–5 TB NVMe from $24/mo annual-eff, dedicated with IPMI from $30.80/mo. Crypto invoice, no KYC.
Launch nowWhat the node actually computes during sync
The counter-intuitive part of running your own node is that the initial sync is not a download — it is verification. Every block since January 2009 gets checked: headers, signatures, script results, difficulty targets, in order. A mirror or a crowd snapshot can shorten the path, but a node that skips verification hands you an opinion about balances instead of proof. Budget hours rather than minutes for the pass, and spend them once, deliberately, before real invoices flow.
During that pass the CPU leads and the disk follows. New blocks arrive on roughly 10-minute intervals of chain time, but they arrive in bursts, and each burst unzips into signature checks that saturate one fast core while the rest idle. NVMe matters because block files, the chainstate database and undo data all churn through small random reads and writes. ECC memory, standard on every plan here, keeps random bit-flips out of a ledger that is supposed to be the reference.
Pruning changes the storage bill without changing the verification work. A pruned node keeps a rolling window of raw block data — prune=550 is the common floor — and discards the rest after validation, turning a multi-hundred-gigabyte archive into roughly 20 GB of working directory. The chainstate index stays whole, so payment queries still answer locally. What you give up is cheap re-indexing and serving historical blocks to strangers, which a payment box rarely needs to do.
The mistake to avoid is pruning in the wrong direction: enabling prune after the disk is already nearly full, when the write of the last burst fails mid-transaction and the node refuses to restart. Set the option before the first launch and the directory grows only to its window and stops. The second error is moving chainstate onto network block storage; latency on small random reads turns an overnight sync into a multi-day one, and df -h only tells you about capacity, not speed, so you discover it late.
Operational honesty also means watching memory during the worst hour, because the UTXO set lives in RAM cache and the kernel fights for page cache beside it. free -m on a 16 GB box mid-sync is more informative than any sizing chart. Start the pass in the evening, let the verification math run while you sleep, and sizing turns into arithmetic instead of faith — which is exactly where the next section picks up.
Sizing the box by workload, not by marketing
The practical floor surprises people used to small web stacks: a pruned node plus BTCPay plus a modest checkout wants 6 vCPU and 16 GB before it feels roomy. Validation is single-thread-hot, the invoice service wants its own headroom, and the kernel needs page cache for the chainstate database. Plans that grow RAM faster than they grow core count match this workload far better than the reverse, which is the quiet logic behind the whole ladder.
Pick Warden at $19.00/mo list — $15.20 on the annual-eff rate — as the honest entry: 6 vCPU, 16 GB of DDR5 ECC and 240 GB of local NVMe absorb a pruned node, the checkout and index headroom together. The 2 GB tier at the bottom of the catalog cannot hold node plus application without swapping during bursts, and swap on NVMe during validation is how a one-night sync becomes a one-week outage. Ranger's extra 2 cores sit idle for this role; Warden is where the arithmetic lands.
Above that line the choice becomes consolidation rather than necessity. Vanguard's 8 cores and 24 GB carry Lightning routing beside the node, or a small storefront in the same cell with peaks that do not collide. Order the Vault at $68.50/mo when the plan is a full archival chain plus routing on 2 x 1 TB of NVMe with no pruning and no neighbors. The upgrade path is a reinstall away either way, so start honest and small rather than aspirational and swapping.
Where sizing breaks in practice is the modpack instinct: buying 12 slow cores to solve what is arithmetically a heap problem, or starving the kernel by setting application limits to the full plan. On a 16 GB box, cap the memory-hungry consumers so at least 2 GB stays kernel-side, then watch free -m and vmstat 1 during the sync's worst hour — si and so columns that move mean swapping you will feel as invoice latency later.
Capacity math, concretely: pruning to prune=550 leaves a working directory near 20 GB once chainstate and indexes are counted, so the 120 GB tier fits with room and 240 GB is generous. The 40 GB floor of the entry tier is the only genuine squeeze. Check real usage monthly with du -x on the data directory and keep 25 percent free — and that closes capacity, while the disk's speed, next, decides how long you wait.
Why the disk, not the CPU, decides the sync story
Operators budget CPU for a sync and then wait on the disk. Block validation alternates between short bursts of signature checks and long stretches of small-file churn into chainstate, and the second half is where slow storage shows. Local NVMe keeps a pruned initial download in the range of hours; contended network volumes turn the same job into days. The local-NVMe rule on every plan here is not a luxury line — it is the difference between an evening and a lost weekend.
The pattern to watch is write latency under burst. While blocks arrive, the node writes block files sequentially, rewrites chunks of chainstate randomly, then flushes; if the volume shares a busy neighbor, those random writes queue behind traffic that is not yours. You can see it live: iostat -x 1 columns for await and util tell you whether the device or the validator is the constraint. On local NVMe both stay boring; on contended storage await climbs into tens of milliseconds.
Where this breaks is the snapshot recovery path. Restoring a node from a snapshot onto the same class of local disk works and re-verifies quickly; restoring onto a slower volume replays the queue you paid to avoid, and the node sits at 100 percent utilization for days while invoices queue behind it. Keep the data directory on the local NVMe the plan ships with and treat any cross-volume move as a re-sync event, never a copy job.
Backups interact with pruning in a way people discover late. A pruned node cannot serve the blocks it discarded, so the old trick of seeding a new node by copying block files from the old one only covers the retained window. For a payment box the right backup set is configuration, database dumps and wallet state — well under 1 GB — shipped off-box nightly with rsync -a, plus the seed phrases you already guard offline on paper.
None of this is exotic; it is arithmetic you run once and revisit yearly. Capacity settled, speed settled, the next surface is the network: which doors the checkout actually needs open, and why the answer is shorter than most deployment guides imply. That drawing — three ports and a hostname — is what the next section covers, and it takes minutes rather than evenings.
Ports, TLS and the domain the checkout deserves
The network setup is smaller than people fear: 80 and 443 for the checkout, 8333 if you want the node to accept inbound peers, and a hostname pointed at the box. Everything else can stay closed from day one. The counter-intuitive part is that opening 8333 helps the Bitcoin network more than it helps you — inbound peers make your node useful to strangers, while the checkout itself never depends on a single one of them.
Point the A record before the first launch. The Docker deployment runs a certificate companion that issues and renews certificates against whatever hostname resolves there, so the checkout shows a padlock with no manual certbot ritual. Inside the firewall, services bind to the Docker bridge and localhost only, and ss -tlnp should print a short list — anything listening on 0.0.0.0 that you did not plan is a finding to fix, not a feature to keep.
Where this breaks is the bare-IP checkout. A certificate cannot automate against an address, browsers flag the connection, and customers read the URL bar more carefully than any copy on your site — a shared hostname or a raw IP quietly undoes the trust the whole self-hosted setup exists to build. The second failure is applying the moved-SSH-port instinct to 443; some corporate proxies and payment terminals misbehave on odd ports, and you pay for that in abandoned checkouts.
Firewall discipline closes the loop: default-deny inbound, the three service ports opened, and the admin surface reached over SSH rather than exposed to the world. The hardening checklist covers that ordering step by step. Keep the rules in a written file under version control, because the firewall is the one component you will edit at 2 a.m. and want to diff in the morning with coffee instead of dread.
Doors drawn and names resolved, the deployment is reachable and private in the ways that matter at the transport layer. The remaining question is speed of settlement, because a self-custodial rail that makes customers wait a confirmation too long loses them at the door — and that is the argument for Lightning, next.
Lightning changes the arithmetic, not the checklist
Instant settlement is why most shops add Lightning, and the requirements are narrower than the folklore: a stable IPv4, port 9735 reachable, and a node that does not vanish mid-channel. The public IPv4 every plan ships with satisfies the first two by default. What Lightning actually taxes is uptime and disk latency, because every channel update is consensus between two nodes, and a lost write becomes a force-close that puts routing capital in limbo for hundreds of blocks.
LND and Core Lightning both install as BTCPay plugins, and the LND and CLN projects document their own backup rituals — the static channel backup file above everything else. You can run either on Warden-class hardware for a shop clearing 100 invoices a day; routing larger payment graphs wants the cores and RAM of the tiers above, and mostly for the channel database, not for the cryptography itself.
The failure mode that costs real money is a restore without channel state: the wallet recovers, the channels force-close on-chain, and capital sits locked for the timelock while you explain delays to nobody. Copy the channel backup off-box the same day you open the first channel and after every batch afterward — a 30-second habit against a multi-week recovery. The second avoidable loss is running channels on a box you reinstall casually; migrations are fine, reinstalls are force-closes by another name.
Routing load shows up as database growth rather than CPU spikes, which is why it sneaks up. A busy node's channel graph, backup files and peering data grow through the year, and du -sh on the Lightning directory every 90 days tells you when the next tier is arithmetic instead of anxiety. Keep an eye on pending payments after restarts too — a node that comes back healthy answers queries; one that replays silently does not.
With settlement speed handled, the remaining question is how private the whole rail is end to end — the node, the invoices, the box they live on, and the currency the hosting itself bills in. That is where the hosting choices stop being commodity and start being part of the payment design, which is the next section's subject.
The privacy rail under the payment rail
A self-custodial checkout that runs on a host demanding passports recreates the middleman one layer up. The platform-side facts that matter here: signup is an inbox plus a 12-character password or a bare token, billing settles in crypto across 19 coins, there are no payload or guest traffic logs, and connection metadata is held at most 24 hours. None of that requires a sales conversation, and the VPS boots about 60 seconds after the invoice credits.
Paying for the box in the same coins the checkout accepts keeps the rail consistent end to end. A BTC invoice confirms with 2 confirmations inside 10 to 60 minutes; XMR credits after 10 blocks; TRC-20 USDT clears at 19 blocks, often 1 to 3 minutes in practice. XRP needs 3 ledgers and no destination tag, DOGE 3 blocks, BNB 15, DASH and ADA 3 each — and 7 flavors of USDT cover the stablecoin side if you bill in fiat units.
Where privacy breaks, it usually breaks in your config rather than at the host: the node announces transactions on the public network, so observers can tie the box's IP to relayed payments. Tor-ifying outbound node traffic is a documented option — proxy=127.0.0.1:9050 in the node's settings — and running the checkout itself as a hidden service is a further step the onion service guide walks through. No hosting tier fixes a config file you never edited.
Jurisdiction is the last lever, and it is priced openly: facilities sit in 40 cities, Iceland and Norway carry a 20 percent premium, Switzerland 35 percent, Luxembourg, Cyprus and Panama 15 percent, and the rest bill at list. Abuse complaints route under the facility's jurisdiction, and the AUP bans CSAM, phishing, spam and network attacks — a payments box doing payments work sits comfortably inside that line. That covers the quiet risks; the loud one is sharing the box, next.
The storefront question, answered with arithmetic
Co-locating the storefront with the node is fine until the two workloads meet, and they meet exactly twice: chain sync and index rebuild. During those windows the node's random IO and RAM appetite spike together, and a storefront sharing 16 GB feels it as slow pages for hours. The rest of the month the combination is quieter than a typical WordPress-plus-plugins stack, which is why the honest answer is measure rather than never.
Measure instead of guessing: free -m during a sync and again during peak store hours tells you which workload owns the ceiling. Cache aggressively at the storefront layer — a 60-second page cache absorbs most retail spikes — and let the node have its bursts undisturbed. The mistake to avoid is OOM-killer roulette, where the kernel picks whichever process annoys it; set explicit memory limits on the storefront containers so a node spike cannot evict the checkout mid-payment.
If the store grows real traffic, split by workload rather than by sentiment: storefront on its own cell, node and checkout on another, with the public IPv4 only where it earns its keep. The filtering guide covers what the network layer absorbs when the checkout gets loud. You keep answering at 443 during an attack, which is worth more than the consolidated invoice you gave up by running two cells.
The Monero question lands here too: community plugins let BTCPay accept XMR, and the honest note is that plugin maturity moves slower than core. If you expect XMR to pass 50 percent of invoices, test the plugin in staging first, because a payment plugin that misbehaves costs settlements rather than page views. Keep the BTC and Lightning flow primary until the numbers argue otherwise, not the other way around.
Every thread so far — account, sizing, disk, ports, Lightning, privacy, consolidation — assembles into one sequence. The last section is that sequence laid out in the order you would actually run it, from an empty checkout screen to a rehearsed, boring, self-custodial payment box.
The BTCPay server, in the order to run it
Start at the account: an inbox plus a 12-character password, or a bare token if you keep email out of it, then fund the invoice in whichever of the 19 coins suits the day. The VPS boots about 60 seconds after the credit posts. Choose the location by jurisdiction preference — 40 cities on the map, with Iceland and Norway at a 20 percent premium and Switzerland at 35 — and take the 6 vCPU, 16 GB tier as the working floor for node plus checkout.
Second, draw the doors: default-deny firewall, then 80, 443 and optionally 8333 opened, with admin traffic through SSH only. Third, point the A record and let the certificate companion issue against the hostname before any customer sees the checkout. Fourth, clone btcpayserver-docker, set the prune level in the environment before the first launch, and keep the exported variables in version control so re-runs stay reproducible.
Fifth, start the sync in the evening and let it run; on local NVMe a pruned download measures in hours, and the node verifies every block itself rather than trusting a snapshot. Watch free -m during the worst burst, then walk away. Sixth, once the chain is current, add Lightning if the shop needs instant settlement, open 9735, and copy the channel backup off-box the same day you open the first channel.
Seventh, rehearse the failure paths while nothing is on fire: restore the wallet backup into a scratch directory, re-read the firewall rules, and confirm the checkout issues a small invoice end to end with a real coin. Eighth, schedule the boring rhythm — monthly du -x on the data directory, quarterly restore tests, a glance at memory each time you add a plugin. Run this way, the box stays boring, and boring is the highest compliment a settlement rail can earn.
What plan should I pick for BTCPay?
Treat 6 vCPU and 16 GB as the floor for a pruned node plus the web checkout, and 8 cores plus 24 GB as the comfortable point once Lightning carries real volume. Storage-shaped plans are the wrong tool: BTCPay wants fast small-file NVMe and steady single-thread CPU, not bulk capacity.
Do I need my own domain?
Yes. The TLS certificate automates against a hostname, and customers read the checkout URL before they read anything else on the page — a bare IP or a shared name undermines the trust the whole self-custodial setup exists to build. Point the A record before the first launch.
Can customers pay in Monero through BTCPay?
Through community plugins, yes, though the core flow is BTC and Lightning. If XMR invoices become the majority of your volume, test the plugin in staging first — plugin maturity moves slower than core, and a failing payment plugin costs you settlements rather than page views.
Can the box also run the storefront?
It can, and 16 GB carries the combination except during chain sync and index rebuilds, when node IO and memory spike together. Set explicit limits on storefront containers so the kernel never picks the victim, and treat a separate cell for the storefront as the upgrade that is cheaper than an outage.
How fast is a checkout live after ordering?
The VPS boots about 60 seconds after the crypto invoice credits, and a BTC payment confirms with 2 confirmations in roughly 10 to 60 minutes. The remaining variable is chain sync — hours on local NVMe with pruning set before the first launch — so same-day is a realistic target.
Primary sources
More ways to pay
Pay with Bitcoin · Pay with Monero · Pay with USDT (TRC-20/ERC-20/SPL) · Pay with XRP · Pay with TRX · Pay with Dogecoin · Pick the jurisdiction first.
Ready to launch?
Build the box — VPS, storage or bare metal — create the password, pay the invoice that follows.