Índice
- 1. Actualización del sistema
- 2. Configuración inicial de seguridad
- 3. Configuración del firewall
- 4. Instalación de Docker
- 5. Configuración recomendada de Docker
- 6. Crear usuario btcpay
- 7. Clonar el repositorio
- 8. Crear y revisar .env
- 9. Cargar .env
- 10. Limpiar instalaciones previas
- 11. Ejecutar instalador
- 12. Verificar servicio systemd
- 13. Verificar contenedores
- 14. Configurar dominio
- 15. Configurar DNS
- 16. Activar Tor
- 17. Optimizar Bitcoin Core
- 18. Optimizar CLN
- 19. Acceso web inicial
- 20. Configuración inicial en la interfaz web
- 21. Lightning
- 22. Integración con tienda externa
- FAQ
- Problemas comunes
- Consejos de rendimiento
- Buenas prácticas de seguridad
- Cómo hacer backup de BTCPay Server
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:
- Entrar en
https://btcpay.example.com. - Ir a Server Settings → Services.
- Localizar el servicio Core Lightning (CLN).
- Pulsar en Core Lightning para abrir la interfaz integrada.
- En el menú lateral, seleccionar Channels.
- Pulsar Open Channel.
- Introducir la dirección del nodo remoto (Node URI).
- Indicar la cantidad a aportar al canal (por ejemplo, 300000 sats).
- 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:
- Abrir un canal desde este nodo hacia un nodo bien conectado (por ejemplo, ACINQ) con una cantidad moderada, por ejemplo 300000 sats.
- Esperar a que el canal esté confirmado y operativo.
- Acceder a un servicio de swap, por ejemplo
https://boltz.exchange. - Seleccionar la opción Lightning → On-chain.
- Introducir una dirección Bitcoin propia para recibir los fondos on-chain.
- El servicio generará una factura Lightning.
- 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_bitcoindaparece 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
dbcacheen 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
.envy 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_datadirgenerated_clightning_datadirgenerated_nginxgenerated_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.comapuntando a la misma IP quebtcpay.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.
