Complete Technical Documentation for Self-Hosted Bitcoin Payment Processing
Until recently, a parent would return home with an actual pay packet containing cash. Financial privacy didn't need protection because it existed naturally in the system.
Today, every financial transaction is monitored, conversations are scanned, and virtual profiles are built for marketing and political manipulation. This comprehensive technical guide helps you reclaim your privacy and accept Bitcoin payments securely using your own infrastructure.
Remember: Every journey begins with a single step. Don't let perfect be the enemy of good. Technology is a tool for freedom, not oppression.
Option A: Direct Home Server (Simpler)
Option B: VPS Proxy + Home Server (Recommended for Privacy)
Purchase a Google Pixel phone (6 Pro or newer) that supports GrapheneOS. Check compatibility at https://grapheneos.org/faq#supported-devices
Install GrapheneOS following the official guide at https://grapheneos.org
Purchase an eSIM with phone number using non-KYC Bitcoin via Silent Link. This provides SMS verification for services without revealing your identity.
Subscribe to Mullvad VPN using Bitcoin. Mullvad is excellent for privacy with no logging policy.
Alternative: PIA (Private Internet Access) is decent for easy installation across multiple devices if you prefer convenience over maximum privacy.
Create a new email with proton.me. Verify your account with SMS to your new eSIM number. Use this email exclusively for your website setup initially, then expand to daily use once your domain is configured.
Purchase a domain from Orange Website. They accept Bitcoin Lightning payments and provide excellent service. Get RapidSSL Standard for the green certificate lock.
Skip this step if using Option A (Direct Home Server)
For privacy protection, use a VPS to hide your home IP. After testing multiple providers, 1984.hosting is excellent at $6/month with Bitcoin payments.
Critical for Option B: Unlike home servers protected by router NAT, your VPS is directly exposed to the internet and needs firewall protection.
Set up your daily email address using your new domain. Configure Thunderbird for email on both phone and PC with SSL encryption enabled in SMTP settings.
Contact your ISP for a static IP, then configure router port forwarding:
Configure router to forward custom port to avoid ISP blocking:
Use a dedicated always-on computer running Linux. Ubuntu Server LTS, Linux Mint LTS, or Debian are recommended. The server must remain powered on 24/7 to process payments.
Visit https://parmanode.com/install to learn about the installation process. Parmanode is the easiest way to get a complete Bitcoin stack:
From the Parmanode menu, install in this order:
Use this configuration if you chose Option A (Direct Home Server)
# Option A: Direct home server configuration
# Protocol and WebSocket mapping
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name btcpay.yourdomain.com;
# ACME challenge for SSL certificates
location ~ /.well-known/acme-challenge/ {
allow all;
root /var/www/html;
try_files $uri =444;
}
# Redirect all other traffic to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Server
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name btcpay.yourdomain.com;
# SSL certificates (will be added by certbot)
ssl_certificate /etc/letsencrypt/live/btcpay.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/btcpay.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Security headers for protection against common attacks
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
location / {
proxy_pass http://127.0.0.1:23001; # BTCPay Server (3003 on Umbrel)
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
# Default server block
server {
listen 80 default_server;
listen [::]:80 default_server;
return 444;
}
Use this configuration if you chose Option B (VPS Proxy)
# Option B: Home server behind VPS proxy
# Protocol and WebSocket mapping
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Primary server - accepts traffic from VPS only
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name btcpay.yourdomain.com;
# Security: Only allow connections from your VPS
allow YOUR_VPS_IP;
deny all;
location / {
proxy_pass http://127.0.0.1:23001; # BTCPay Server (3003 on Umbrel)
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
# Default catch-all
server {
listen 80;
listen [::]:80;
server_name _;
return 444;
}
Configure this on your VPS if you chose Option B
# VPS nginx configuration
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# HTTPS Server - SSL termination happens here
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name btcpay.yourdomain.com;
# SSL certificates (will be added by certbot)
ssl_certificate /etc/letsencrypt/live/btcpay.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/btcpay.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Security headers for protection against common attacks
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
location / {
proxy_pass http://YOUR_HOME_IP:15080; # Your home server
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
# HTTP redirect
server {
listen 80;
listen [::]:80;
server_name btcpay.yourdomain.com;
location ~ /.well-known/acme-challenge/ {
allow all;
root /var/www/html;
try_files $uri =444;
}
location / {
return 301 https://$host$request_uri;
}
}
# Default block
server {
listen 80 default_server;
listen [::]:80 default_server;
return 444;
}
Generate SSL certificates on your home server:
Generate SSL certificates on your VPS only:
Save with Ctrl+X, then Y, then Enter. Restart BTCPay Server:
To receive Lightning payments, you need inbound liquidity. This is the amount of Bitcoin that can be sent TO your node through Lightning channels.
1. Exchange Method (Tested):
2. Lightning Service Providers (LSPs):
3. Direct Channel Opening:
https://btcpay.yourdomain.com
Skip this section if using Option A (Direct Home Server)
Create SSH keys for automated VPS access from your home server:
Create this script on your home server. Unlike traditional scripts that update constantly, this only connects to your VPS when your IP actually changes - providing massive privacy improvement:
#!/bin/bash
# Privacy-enhanced IP monitoring script
# Only updates VPS when IP actually changes (99.99% exposure reduction)
LOG_FILE="/var/log/ip-update.log"
VPS_USER="your_vps_username"
VPS_HOST="YOUR_VPS_IP"
VPS_PORT="22" # Use custom port if configured
SSH_KEY="/home/yourusername/.ssh/vps_key"
LOCK_FILE="/tmp/ip-monitor.lock"
# Function to log with timestamp
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}
# Prevent multiple simultaneous runs
if [ -f "$LOCK_FILE" ]; then
exit 0
fi
touch "$LOCK_FILE"
# Cleanup on exit
trap 'rm -f "$LOCK_FILE"' EXIT
# Get current public IP
NEW_IP=$(curl -s --max-time 10 ifconfig.me)
if [ -z "$NEW_IP" ]; then
log "ERROR: Failed to get public IP"
exit 1
fi
# Get current IP from VPS nginx config
OLD_IP=$(ssh -i $SSH_KEY -p $VPS_PORT -o StrictHostKeyChecking=no $VPS_USER@$VPS_HOST "grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+:15080' /etc/nginx/sites-available/btcpay | head -1 | cut -d: -f1" 2>/dev/null)
if [ -z "$OLD_IP" ]; then
log "ERROR: Failed to get current IP from VPS"
exit 1
fi
# Check if IP changed - ONLY UPDATE IF DIFFERENT
if [ "$NEW_IP" != "$OLD_IP" ]; then
log "IP changed from $OLD_IP to $NEW_IP - updating VPS"
# Update VPS nginx config with backup/rollback protection
UPDATE_RESULT=$(ssh -i $SSH_KEY -p $VPS_PORT -o StrictHostKeyChecking=no $VPS_USER@$VPS_HOST "
# Backup original config
sudo cp /etc/nginx/sites-available/btcpay /etc/nginx/sites-available/btcpay.backup &&
# Replace IP in config
sudo sed -i 's/proxy_pass http:\/\/.*:15080/proxy_pass http:\/\/$NEW_IP:15080/g' /etc/nginx/sites-available/btcpay &&
# Test nginx config and reload, or rollback on failure
if sudo nginx -t; then
sudo systemctl reload nginx && echo 'SUCCESS'
else
echo 'NGINX_TEST_FAILED - Rolling back'
sudo cp /etc/nginx/sites-available/btcpay.backup /etc/nginx/sites-available/btcpay &&
sudo nginx -t && sudo systemctl reload nginx
echo 'ROLLBACK_COMPLETED'
fi
" 2>&1)
if [[ $UPDATE_RESULT == *"SUCCESS"* ]]; then
log "VPS updated successfully to $NEW_IP"
# Test if the new IP is reachable
if timeout 10 curl -s http://$NEW_IP:15080 > /dev/null; then
log "New IP $NEW_IP is reachable"
else
log "WARNING: New IP $NEW_IP may not be reachable"
fi
elif [[ $UPDATE_RESULT == *"ROLLBACK_COMPLETED"* ]]; then
log "ERROR: Nginx test failed, rolled back to $OLD_IP"
else
log "ERROR: Failed to update VPS - $UPDATE_RESULT"
fi
else
# IP unchanged - exit silently (no log spam)
exit 0
fi
Issue: "502 Bad Gateway" error
sudo systemctl status btcpayserver
Issue: SSL certificate not working
Issue: Can't access BTCPay externally
Issue: VPS can't connect to home server (Option B)
tail /var/log/ip-update.log
Issue: IP monitor script not updating
sudo systemctl status cron
sudo /usr/local/bin/ip-monitor.sh
rm -f /tmp/ip-monitor.lock
If you've followed this guide completely, you now have:
Remember: Technology is a tool for freedom, not oppression. You've taken a significant step toward financial privacy and independence.
Financial Privacy is a Human Right.