This guide covers deploying BTPay on a Linux server with Gunicorn, Nginx, and systemd.
# Create system user
sudo useradd -r -s /bin/false -d /opt/btpay btpay
# Clone and set up
sudo mkdir -p /opt/btpay
sudo chown btpay:btpay /opt/btpay
cd /opt/btpay
sudo -u btpay git clone https://github.com/user/btpay.git .
sudo -u btpay python3 -m venv .venv
sudo -u btpay .venv/bin/pip install --require-hashes -r requirements.lock
sudo -u btpay .venv/bin/pip install -e . --no-deps
Create a production config file:
sudo -u btpay cat > /opt/btpay/config.py << 'EOF'
# Generate secrets with: python3 -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY = 'your-64-char-hex-string-here'
DEV_MODE = False
# Reference number encryption keys
# Generate with: python3 -c "import secrets; print(secrets.token_hex(16))"
REFNUM_AES_KEY = 'your-32-char-hex-string'
REFNUM_HMAC_KEY = 'your-32-char-hex-string'
# JWT secrets (one per purpose)
JWT_SECRETS = {
'admin': 'your-random-secret-1',
'login': 'your-random-secret-2',
'api': 'your-random-secret-3',
'invite': 'your-random-secret-4',
}
# Optional: SMTP for email notifications
SMTP_CONFIG = {
'host': 'smtp.example.com',
'port': 587,
'username': 'your-smtp-user',
'password': 'your-smtp-password',
'from_email': 'payments@yourdomain.com',
'from_name': 'Your Business',
}
# Optional: Tor privacy
# SOCKS5_PROXY = 'socks5h://127.0.0.1:9050'
EOF
chmod 600 /opt/btpay/config.py
# Generate all required secrets at once
python3 -c "
import secrets
print(f'SECRET_KEY = \"{secrets.token_hex(32)}\"')
print(f'REFNUM_AES_KEY = \"{secrets.token_hex(16)}\"')
print(f'REFNUM_HMAC_KEY = \"{secrets.token_hex(16)}\"')
for purpose in ['admin', 'login', 'api', 'invite']:
print(f' \"{purpose}\": \"{secrets.token_hex(24)}\",')
"
cd /opt/btpay
sudo -u btpay .venv/bin/flask --app app user-create \
--email admin@yourdomain.com \
--first-name Admin \
--last-name User
You’ll be prompted for a password.
The included config at deploy/gunicorn.conf.py is production-ready:
# Test it
sudo -u btpay .venv/bin/gunicorn -c deploy/gunicorn.conf.py wsgi:app
Key settings:
BTPAY_THREADS)Override via environment:
| Env Var | Default | Description |
|---|---|---|
BTPAY_BIND |
127.0.0.1:5000 |
Bind address |
BTPAY_THREADS |
4 |
Worker threads |
BTPAY_LOG_LEVEL |
info |
Log level |
Install the service:
sudo cp /opt/btpay/deploy/btpay.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable btpay
sudo systemctl start btpay
Check status:
sudo systemctl status btpay
sudo journalctl -u btpay -f
The service includes security hardening:
ProtectSystem=strict — read-only filesystem except data dirPrivateTmp=yes — isolated temp directoryNoNewPrivileges=yes — no privilege escalationProtectHome=yes — no access to home directoriesEdit deploy/nginx.conf and replace:
btpay.example.com with your domainnginx.confInstall:
sudo cp /opt/btpay/deploy/nginx.conf /etc/nginx/sites-available/btpay
sudo ln -s /etc/nginx/sites-available/btpay /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d btpay.yourdomain.com
For maximum privacy, route all external connections through Tor:
sudo apt install tor
sudo systemctl enable tor
config.py:
SOCKS5_PROXY = 'socks5h://127.0.0.1:9050'
/etc/tor/torrc:
HiddenServiceDir /var/lib/tor/btpay/
HiddenServicePort 80 127.0.0.1:5000
.onion address:
sudo cat /var/lib/tor/btpay/hostname
For faster BIP32 key derivation, build the C library:
cd /tmp
git clone https://github.com/bitcoin-core/secp256k1.git
cd secp256k1
./autogen.sh
./configure --enable-module-recovery
make
sudo make install
sudo ldconfig
BTPay automatically detects and uses it if available. Without it, a pure Python fallback is used.
BTPay auto-saves data every 60 seconds and keeps the last 5 backups. Data is stored in data/.
sudo -u btpay .venv/bin/flask --app app db-backup
Back up the entire data/ directory:
# Simple rsync backup
rsync -av /opt/btpay/data/ /backup/btpay/
# Or with a cron job
echo "0 */6 * * * btpay cd /opt/btpay && .venv/bin/flask --app app db-backup" | sudo tee /etc/cron.d/btpay-backup
sudo systemctl stop btpay
sudo -u btpay cp /backup/btpay/*.json /opt/btpay/data/
sudo systemctl start btpay
curl http://localhost:5000/health
Returns {"status": "ok"} when the server is running.
# Follow live logs
sudo journalctl -u btpay -f
# Recent errors only
sudo journalctl -u btpay --since "1 hour ago" -p err
In production, logs are JSON-formatted for easy parsing by log aggregators.
sudo -u btpay .venv/bin/flask --app app db-stats
cd /opt/btpay
sudo -u btpay git pull
sudo -u btpay .venv/bin/pip install --require-hashes -r requirements.lock
sudo -u btpay .venv/bin/pip install -e . --no-deps
sudo systemctl restart btpay
BTPay works on FreeBSD. Key differences:
pkg install python3 py3-pip instead of aptrc.d scripts instead of systemd (or install sysutils/py-supervisor)pkg install libsecp256k1pkg install torServer won’t start:
journalctl -u btpay -n 50python3 -c "exec(open('config.py').read())"ls -la /opt/btpay/data/Data lost after restart:
systemctl stop for clean shutdownRate limited:
Exchange rates not updating:
curl https://api.coingecko.com/api/v3/pingSOCKS5_PROXY