Inglés

bitcoin, ln, btcpay, raspberry, debian

Guía instalación BTCPay Server en Raspberry 5

19 / 12 / 2025

Índice


NOTA: Este documento está pendiente de revisar, pues está hecho con "ayuda" de una IA, que no anda muy bien de la cabeza

Entorno de referencia

  • Raspberry Pi 5 (8 GB RAM)
  • SSD NVMe instalado mediante base oficial o adaptador PCIe
  • Debian Trixie ARM64 instalado directamente en el NVMe
  • Arranque sin microSD
  • Acceso SSH operativo
  • Conectividad a Internet estable
  • Dominio disponible para asignar al servicio (ejemplo: btcpay.example.com)

La preparación del NVMe y la instalación de Debian Trixie sobre él no se cubren en este documento. Se proporcionará una guía independiente para ese procedimiento.

Perfil del lector

Este documento está dirigido a usuarios con un nivel básico o intermedio de administración de sistemas, familiarizados con el uso del shell y con nociones generales de Linux. No se requieren conocimientos avanzados, pero sí cierta comodidad trabajando en consola y siguiendo procedimientos técnicos.

Objetivo

Desplegar un servidor BTCPay completamente operativo sobre Debian Trixie ARM64, incluyendo:

  • Configuración inicial de seguridad del sistema
  • Instalación de Docker
  • Despliegue de BTCPay Server
  • Configuración de dominio y TLS
  • Habilitación de Tor
  • Configuración de Bitcoin Core y CLN
  • Optimización para Raspberry Pi 5
  • Integración con aplicaciones externas mediante API

1. Actualización del sistema

  ^apt update apt full-upgrade -y reboot

2. Configuración inicial de seguridad (SSH) ^

Cambiar el puerto del SSH

nano /etc/ssh/sshd_config
Port 22222
PermitRootLogin prohibit-password
PasswordAuthentication yes
systemctl restart ssh

3. Configuración del firewall (UFW) ^

apt install ufw -y

ufw default deny incoming
ufw default allow outgoing

ufw allow 22222/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 9735/tcp
ufw allow 8333/tcp

ufw enable
ufw status verbose

El puerto 8333 es utilizado por Bitcoin Core para recibir conexiones entrantes desde otros nodos. Aunque no es estrictamente necesario para que BTCPay funcione, abrirlo mejora la sincronización y la conectividad del nodo.


4. Instalación de Docker en Debian Trixie ^

apt install ca-certificates curl gnupg -y
install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/debian/gpg | \
  gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  > /etc/apt/sources.list.d/docker.list

apt update
apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
systemctl enable docker
systemctl start docker

5. Configuración recomendada de Docker ^

nano /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  }
}
systemctl restart docker

6. Crear usuario btcpay y preparar /opt ^

adduser --disabled-password --gecos " btcpay
usermod -aG sudo btcpay
chown btcpay:btcpay /opt

7. Clonar el repositorio btcpayserver-docker ^

su - btcpay
cd /opt
git clone https://github.com/btcpayserver/btcpayserver-docker.git

8. Crear y revisar el archivo .env ^

cd /opt/btcpayserver-docker
nano .env
BTCPAY_BASE_DIRECTORY="/opt/btcpayserver-docker"
BTCPAY_ENV_FILE="/opt/btcpayserver-docker/.env"

BTCPAYGEN_CRYPTO1=btc
BTCPAYGEN_CRYPTO2=
BTCPAYGEN_CRYPTO3=

BTCPAYGEN_REVERSEPROXY=nginx
BTCPAYGEN_LIGHTNING=clightning

BTCPAYGEN_ADDITIONAL_FRAGMENTS=
BTCPAYGEN_EXCLUDE_FRAGMENTS=

9. Cargar el .env correctamente ^

set -a
. .env
set +a

10. Limpiar restos de instalaciones previas ^

sudo rm -f /etc/profile.d/btcpay-env.sh
sudo rm -f /etc/systemd/system/btcpayserver.service
sudo systemctl daemon-reload

11. Ejecutar el instalador de BTCPay ^

cd /opt/btcpayserver-docker
set -a
. .env
set +a
sudo -E su -
git config --global --add safe.directory /opt/btcpayserver-docker
cd /opt/btcpayserver-docker
. btcpay-setup.sh -i

12. Verificar servicio systemd ^

systemctl status btcpayserver

13. Verificar contenedores ^

docker ps

14. Configurar dominio (archivo .env) ^

Editar:

nano /opt/btcpayserver-docker/.env

Añadir o modificar:

BTCPAY_HOST=btcpay.example.com

15. Añadir subdominio (DNS) ^

Esto se configura en tu proveedor DNS, no en el servidor:

test.example.com   A   IP_DE_TU_RASPI

16. Activar Tor (archivo .env) ^

nano /opt/btcpayserver-docker/.env
BTCPAYGEN_ADDITIONAL_FRAGMENTS=opt-add-tor

17. Optimizar Bitcoin Core (bitcoin.conf) ^

Editar el archivo dentro del volumen Docker:

nano /var/lib/docker/volumes/generated_bitcoin_datadir/_data/bitcoin.conf
server=1
txindex=0
prune=550
dbcache=300
maxconnections=40

18. Optimizar CLN (config) ^

Editar el archivo dentro del volumen Docker:

nano /var/lib/docker/volumes/generated_clightning_datadir/_data/config
log-level=info
alias=MiNodoLN
fee-base=0
fee-per-satoshi=1
cltv-delta=40

19. Acceso web inicial ^

https://btcpay.example.com

20. Configuración inicial en la interfaz web de BTCPay ^

Una vez accesible la interfaz web, es necesario realizar la configuración mínima para que el sistema pueda generar facturas y aceptar pagos.

1. Crear el usuario administrador

  • Acceder a https://btcpay.example.com
  • Registrar el primer usuario (este usuario será administrador del sistema)

2. Crear una Store

  • Ir a Stores → Create a new store
  • Asignar un nombre identificativo

3. Configurar el método de pago Bitcoin

  • Entrar en la Store recién creada
  • Ir a Settings → On-chain payment methods
  • Seleccionar Bitcoin
  • Elegir uno de los siguientes modos:
    • Use internal node (recomendado si Bitcoin Core está gestionado por BTCPay)
    • Use an xpub (si se desea usar una wallet externa)

4. Configurar Lightning (CLN)

  • Ir a Settings → Lightning payment methods
  • Seleccionar Core Lightning
  • Confirmar la conexión con el nodo interno

5. Generar una API Key para integraciones externas

  • Ir a Account → Manage API Keys
  • Crear una nueva API Key
  • Asignar permisos mínimos necesarios (por ejemplo: Create invoices)
  • Guardar el token para integraciones externas

Una vez completados estos pasos, la Store queda operativa y puede generar facturas Bitcoin y Lightning.


21. Requisitos para aceptar pagos Lightning ^

Para que la Store pueda recibir pagos Lightning es necesario que el nodo CLN disponga de al menos un canal activo con liquidez entrante. Sin liquidez entrante, el nodo puede enviar pagos pero no puede recibirlos.

21.1. Liquidez entrante

La liquidez entrante representa la capacidad del nodo para recibir satoshis a través de un canal Lightning. Cuando este nodo abre un canal, todo el saldo inicial queda en el lado local, lo que proporciona liquidez saliente pero cero liquidez entrante. En estas condiciones no es posible recibir pagos Lightning.

Formas de obtener liquidez entrante:

  • Canal entrante: otro nodo abre un canal hacia este nodo.
  • Servicios de inbound liquidity: proveedores que abren canales entrantes a cambio de una comisión.
  • Swaps: enviar satoshis por Lightning y recibirlos on-chain, desplazando saldo hacia el lado remoto del canal.
  • Push al abrir el canal: el nodo remoto transfiere parte del saldo al abrir el canal.

21.2. Liquidez saliente

La liquidez saliente representa la capacidad del nodo para enviar satoshis. Cuando este nodo abre un canal y aporta los fondos iniciales, todo el saldo queda en el lado local, generando liquidez saliente.

La liquidez saliente permite:

  • Realizar pagos Lightning
  • Realizar swaps para obtener liquidez entrante
  • Participar en la red Lightning como nodo activo

La liquidez saliente se obtiene automáticamente al abrir un canal desde este nodo. No requiere pasos adicionales.

21.3. Abrir un canal desde la interfaz web de BTCPay

Para abrir un canal desde el nodo CLN integrado en BTCPay, acceder a la interfaz de administración del nodo Lightning:

  1. Entrar en https://btcpay.example.com.
  2. Ir a Server Settings → Services.
  3. Localizar el servicio Core Lightning (CLN).
  4. Pulsar en Core Lightning para abrir la interfaz integrada.
  5. En el menú lateral, seleccionar Channels.
  6. Pulsar Open Channel.
  7. Introducir la dirección del nodo remoto (Node URI).
  8. Indicar la cantidad a aportar al canal (por ejemplo, 300000 sats).
  9. Confirmar la apertura del canal.

Una vez confirmada la transacción on-chain, el canal aparecerá como active y podrá utilizarse para pagos Lightning.

Ejemplo de nodo remoto bien conectado (ACINQ):

03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f@3.33.236.230:9735

¿De dónde salen estas direcciones?

  • Son públicas: todos los nodos Lightning publican su nodeid para permitir conexiones.
  • Se pueden consultar en sitios como:
    • 1ML (listado de nodos Lightning)
    • LightningNetwork+ (LN+)
    • Amboss.space
  • No es necesario pedirlas a nadie: son datos públicos.

Formato de un Node URI:

nodeid@ip:9735

El puerto estándar de Lightning es 9735.

21.4. Ejemplo práctico: obtener liquidez entrante mediante un swap

Este ejemplo muestra un procedimiento sencillo para obtener liquidez entrante sin depender de que otros nodos abran canales hacia este nodo. El proceso consiste en enviar satoshis por Lightning a un servicio de swap y recibir la misma cantidad en una dirección on-chain.

Pasos:

  1. Abrir un canal desde este nodo hacia un nodo bien conectado (por ejemplo, ACINQ) con una cantidad moderada, por ejemplo 300000 sats.
  2. Esperar a que el canal esté confirmado y operativo.
  3. Acceder a un servicio de swap, por ejemplo https://boltz.exchange.
  4. Seleccionar la opción Lightning → On-chain.
  5. Introducir una dirección Bitcoin propia para recibir los fondos on-chain.
  6. El servicio generará una factura Lightning.
  7. Pagar esa factura desde el nodo CLN integrado en BTCPay.

Resultado:

  • El nodo envía, por ejemplo, 100000 sats por Lightning (liquidez saliente).
  • Boltz envía 100000 sats on-chain a la dirección indicada.
  • El canal queda con 100000 sats en el lado remoto.
  • El nodo dispone ahora de 100000 sats de liquidez entrante.

Con esta liquidez entrante, la Store puede recibir pagos Lightning sin necesidad de que otros nodos abran canales hacia este nodo.

22. Integración con tienda PHP externa ^

 0.0001,
  'currency'    => 'BTC',
  'orderId'     => 'pedido-123',
  'notificationEmail' => 'cliente@example.com'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $btcpayUrl . '/api/v1/stores/' . $storeId . '/invoices');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'Content-Type: application/json',
  'Authorization: token ' . $apiKey
]);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$invoice = json_decode($response, true);
echo 'URL de pago: ' . $invoice['checkoutLink'];

Conclusión ^

El sistema queda desplegado con BTCPay Server, Bitcoin Core, CLN, dominio con TLS automático, Tor y capacidad de integración mediante API. La Raspberry Pi 5 ofrece un rendimiento adecuado para este tipo de despliegues, especialmente con Debian Trixie instalado directamente en NVMe.

FAQ (Preguntas frecuentes) ^

¿Cuánto tarda en sincronizar Bitcoin Core?

En una Raspberry Pi 5 con NVMe suele tardar entre 6 y 12 horas. Es normal que el proceso sea lento al principio.

¿Puedo usar BTCPay sin Lightning?

Sí. Puedes aceptar pagos on-chain sin necesidad de Lightning ni canales.

¿Necesito abrir un canal para recibir pagos Lightning?

Sí. Para recibir pagos Lightning necesitas al menos un canal con liquidez entrante.

¿Qué pasa si abro un canal pero no puedo recibir pagos?

Probablemente tienes liquidez saliente pero no entrante. Consulta la sección 21.1.

¿Puedo usar una wallet externa para la Store?

Sí. Puedes usar un xpub externo en lugar del nodo interno.

¿BTCPay funciona sin dominio?

Sí, pero no tendrás HTTPS automático y algunas funciones pueden fallar. Se recomienda usar dominio.

¿Puedo usar Tor solamente?

Sí. BTCPay soporta Tor nativamente mediante el fragmento opt-add-tor.

¿Puedo migrar mi BTCPay a otro servidor?

Sí. Solo necesitas copiar los volúmenes Docker y el archivo .env.

¿Qué hardware mínimo necesito?

Raspberry Pi 4 funciona, pero la Pi 5 con NVMe es mucho más rápida y estable.

Problemas comunes y soluciones ^

BTCPay no arranca después de la instalación

Revisar logs con:

docker logs btcpayserver

Bitcoin Core no sincroniza

  • Comprobar conexión a Internet
  • Verificar que el puerto 8333 no está bloqueado
  • Revisar logs del contenedor bitcoind

Lightning no conecta con otros nodos

  • Verificar que el puerto 9735 está abierto en el router
  • Comprobar que el nodo remoto está activo
  • Revisar la URI del nodo remoto

No puedo recibir pagos Lightning

Causa más común: falta de liquidez entrante. Consulta la sección 21.1.

El certificado HTTPS no se genera

  • Verificar que el dominio apunta a la IP correcta
  • Comprobar que el puerto 80 está abierto
  • Esperar unos minutos: Let's Encrypt puede tardar

La Store no genera facturas

  • Revisar que Bitcoin y Lightning están configurados
  • Verificar que la API Key tiene permisos
  • Comprobar que el nodo Lightning está activo

El contenedor de CLN se reinicia constantemente

  • Revisar logs del contenedor clightning
  • Comprobar permisos del volumen generated_clightning_datadir

Bitcoin Core no arranca y el contenedor entra en bucle “Restarting (1)”

Síntomas:

  • BTCPay muestra “Your node is synching…”
  • NBXplorer se queda en headers height: 0
  • El contenedor btcpayserver_bitcoind aparece como:
    Restarting (1)
  • En los logs aparece algo como:
    ReadBlockUndo: Deserialize or I/O error
    Corrupted block database detected.
    Please restart with -reindex or -reindex-chainstate to recover.

Causa habitual: apagado brusco de la Raspberry Pi, lo que provoca corrupción en la base de datos de Bitcoin Core (archivos undo o blk).

✅ Solución: forzar un reindex manual

El reindex desde BTCPay no funciona porque Bitcoin Core se apaga antes de leer el bitcoin.conf. Hay que forzarlo manualmente:

docker run -it --rm \
  -v generated_bitcoin_datadir:/data \
  btcpayserver/bitcoin:29.1 \
  bitcoind -datadir=/data -reindex

Este comando:

  • fuerza el reindex aunque la base de datos esté corrupta
  • reconstruye toda la blockchain
  • puede tardar horas (normal)
  • deja el volumen limpio y funcional

Cuando termine:

cd /opt/btcpayserver-docker
. btcpay-up.sh

NBXplorer volverá a sincronizar y BTCPay funcionará normalmente.

✅ Prevención

  • Usar fuente de alimentación estable (5V 5A o la oficial de Raspberry Pi 5)
  • Evitar apagados bruscos
  • Asegurar buena ventilación si usas NVMe
  • Comprobar errores de disco con:
    dmesg | grep -i error

Consejos de rendimiento ^

  • Usar NVMe en lugar de microSD (mejora enorme en I/O).
  • Reducir dbcache en Bitcoin Core si tienes poca RAM.
  • Limitar logs de Docker para evitar llenar el disco.
  • Evitar usar swap en Raspberry Pi (ralentiza el sistema).
  • Reiniciar contenedores periódicamente si notas degradación.
  • Desactivar servicios que no uses en BTCPay (Lightning alternativo, altcoins, etc.).

Buenas prácticas de seguridad ^

  • Usar SSH en puerto no estándar.
  • Desactivar acceso root por SSH.
  • Activar UFW con reglas estrictas.
  • Usar contraseñas fuertes o claves SSH.
  • Hacer backup del archivo .env y guardarlo offline.
  • Actualizar el sistema regularmente.
  • Evitar exponer puertos innecesarios.
  • Usar Tor si necesitas privacidad adicional.

Cómo hacer backup de BTCPay Server ^

Para migrar o restaurar tu BTCPay Server, necesitas conservar:

  • El archivo .env
  • Los volúmenes Docker:
    • generated_bitcoin_datadir
    • generated_clightning_datadir
    • generated_nginx
    • generated_btcpayserver

Backup rápido:

docker volume ls
docker run --rm -v generated_bitcoin_datadir:/data -v $(pwd):/backup alpine tar czf /backup/bitcoin_backup.tar.gz /data

Restauración:

docker run --rm -v generated_bitcoin_datadir:/data -v $(pwd):/backup alpine tar xzf /backup/bitcoin_backup.tar.gz -C /

Checklist de salud del stack BTCPay/NBXplorer/bitcoind

Usa esta sección para verificar en segundos el estado de los servicios críticos en tu Raspberry Pi.

1. Estado de contenedores

Todos deben estar en Up (no Restarting ni Exited).

docker ps --format "table {{.Names}}\t{{.Status}}"

2. Contador de reinicios

Si algún número sube constantemente, ese servicio está en bucle.

docker inspect --format='{{.Name}} {{.RestartCount}}' $(docker ps -q)

3. Logs de bitcoind (actividad de bloques)

Debes ver actividad normal: UpdateTip, new block, etc.

docker logs --tail=30 btcpayserver_bitcoind

4. Logs de NBXplorer (estado Ready)

Busca Node state changed: … => Ready. Si ves 401 Unauthorized o Connection refused, revisa credenciales RPC o conexión.

docker logs --tail=30 generated_nbxplorer_1

5. Logs de BTCPay Server

Si se repite Waiting for NBXplorer, NBXplorer no está listo.

docker logs --tail=30 generated_btcpayserver_1
Script rápido: informe combinado

Copia este script como btcpay-health.sh y ejecútalo para ver un informe compacto.

#!/usr/bin/env bash
set -e

echo "=== Estado de contenedores ==="
docker ps --format "table {{.Names}}\t{{.Status}}"

echo
echo "=== Contador de reinicios ==="
for c in $(docker ps -q); do
  name=$(docker inspect --format='{{.Name}}' "$c" | sed 's#^/##')
  restarts=$(docker inspect --format='{{.RestartCount}}' "$c")
  printf "%-30s %s
" "$name" "$restarts"
done

echo
echo "=== bitcoind (últimas 30 líneas) ==="
docker logs --tail=30 btcpayserver_bitcoind || true

echo
echo "=== NBXplorer (últimas 30 líneas) ==="
docker logs --tail=30 generated_nbxplorer_1 || true

echo
echo "=== BTCPay Server (últimas 30 líneas) ==="
docker logs --tail=30 generated_btcpayserver_1 || true

echo
echo "=== Diagnóstico rápido ==="
# Heurística simple
nbx_last=$(docker logs --tail=50 generated_nbxplorer_1 2>/dev/null | tail -n 50)
if echo "$nbx_last" | grep -q "Node state changed: .* => Ready"; then
  echo "NBXplorer: Ready ✅"
elif echo "$nbx_last" | grep -qi "Unauthorized"; then
  echo "NBXplorer: Error de credenciales RPC ❌"
elif echo "$nbx_last" | grep -qi "Connection refused"; then
  echo "NBXplorer: Nodo no accesible (RPC/Red) ⚠️"
else
  echo "NBXplorer: Consultar logs arriba ℹ️"
fi

btc_last=$(docker logs --tail=50 btcpayserver_bitcoind 2>/dev/null | tail -n 50)
if echo "$btc_last" | grep -qiE "UpdateTip|block"; then
  echo "bitcoind: Procesando bloques ✅"
else
  echo "bitcoind: Sin actividad visible, comprobar configuración ⚠️"
fi

btcp_last=$(docker logs --tail=50 generated_btcpayserver_1 2>/dev/null | tail -n 50)
if echo "$btcp_last" | grep -qi "Waiting for NBXplorer"; then
  echo "BTCPay: Esperando NBXplorer ⚠️"
else
  echo "BTCPay: Activo ✅"
fi

Consejo: añade esta sección a tu documentación y guarda el script en /usr/local/bin/btcpay-health.sh para lanzarlo en cualquier momento.

Configurar subdominio test.example.com con PHP y MariaDB

Este procedimiento permite servir una web independiente en test.example.com, con soporte PHP y acceso a una base de datos MariaDB cuyos datos se almacenan fuera de Docker.

Prerrequisitos

  • BTCPay Server ya desplegado con proxy automático (nginx-gen + letsencrypt).
  • DNS configurado: test.example.com apuntando a la misma IP que btcpay.example.com.
  • Directorio para la web: /var/www/test.example.com.
  • Directorio para la base de datos: /var/lib/mysql/test.

1. Crear directorio de la web

mkdir -p /var/www/test.example.com
echo "" > /var/www/test.example.com/index.php

2. Crear directorio para la base de datos

sudo mkdir -p /var/lib/mysql/test
sudo chown -R 999:999 /var/lib/mysql/test

El UID/GID 999:999 corresponde al usuario mysql dentro del contenedor oficial de MariaDB.

3. Levantar contenedor MariaDB

docker run -d \
  --name test-mariadb \
  -e MYSQL_ROOT_PASSWORD=superseguro \
  -e MYSQL_DATABASE=test \
  -e MYSQL_USER=testuser \
  -e MYSQL_PASSWORD=testpass \
  -v /var/lib/mysql/test:/var/lib/mysql \
  mariadb:11

4. Levantar contenedor web (Nginx + PHP)

Se puede usar una imagen que combine Nginx y PHP-FPM, por ejemplo webdevops/php-nginx:

docker run -d \
  --name testsite \
  -v /var/www/test.example.com:/var/www/html \
  -e VIRTUAL_HOST=test.example.com \
  -e LETSENCRYPT_HOST=test.example.com \
  -e LETSENCRYPT_EMAIL=tuemail@example.com \
  --link test-mariadb:db \
  webdevops/php-nginx:8.4

El proxy automático detectará el contenedor y generará el vhost con HTTPS para test.example.com.

5. Conexión PHP a MariaDB

Ejemplo de código PHP en /var/www/test.example.com/index.php:

connect_error) {
    die("Error de conexión: " . $mysqli->connect_error);
}

$mysqli->query("CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50))");
$mysqli->query("INSERT INTO users (name) VALUES ('Froilán')");
$result = $mysqli->query("SELECT * FROM users");

while ($row = $result->fetch_assoc()) {
    echo $row['id'] . " - " . $row['name'] . "
"; } ?>

6. Notas y prevención

  • Los datos de la web están en /var/www/test.example.com, fuera de Docker.
  • Los datos de la base de datos están en /var/lib/mysql/test, fuera de Docker.
  • Esto permite cambiar de versión de PHP o MariaDB sin perder datos.
  • Hacer backups periódicos de ambos directorios.
  • No montar el mismo volumen en dos contenedores a la vez.

Con estos elementos puedes restaurar tu BTCPay en cualquier servidor compatible.