The dd61a204 bind hardening published Bitcoin RPC on the archy-net gateway 10.89.0.1 — but rootlessport binds in the HOST netns, where that address does not exist. First real deploy (.228, 2026-07-09) crash-looped bitcoin-knots AND bitcoin-core the moment the gate's stop→start regenerated the unit from the re-signed catalog: 'rootlessport listen tcp 10.89.0.1:8332: bind: cannot assign requested address', restart counter 132. Hand-edits to the unit don't survive — the orchestrator regenerates it from the signed catalog manifest within seconds. - New archipelago_container::manifest::host_can_bind_publish_ip(): empty/ wildcard/loopback accepted without probing, anything else ephemeral-bind probed. Applied at all three publish paths — quadlet from_manifest (PublishPort), podman API create (host_ip), and the legacy -p string table loop — each dropping the publish with a warn instead of taking the container down. This neutralizes the bad binds already in the SIGNED catalog, so nodes recover on binary deploy alone (no ceremony needed). - Remove the gateway publishes from bitcoin-knots/-core manifests and the legacy config.rs tables: verified on .228 that every in-node consumer (lnd, btcpay/nbxplorer, fedimint, mempool-api) dials the container's archy-net alias directly (bitcoin-knots:8332) and lnd uses RPC polling (no ZMQ) — the gateway publish had zero consumers. Loopback-only RPC/ZMQ (the approved LAN lockdown) stands; P2P 8333 stays public. Catalog still ships the gateway binds until the next signing ceremony regenerates it from these manifests; the guard makes that non-urgent. Tests: container crate 65/65 (2 new guard tests), quadlet 40/40. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
110 lines
4.4 KiB
YAML
110 lines
4.4 KiB
YAML
app:
|
|
id: bitcoin-knots
|
|
name: Bitcoin Knots
|
|
version: 28.1.0
|
|
description: Full Bitcoin Knots node with dynamic prune/full-mode startup based on host disk.
|
|
|
|
container_name: bitcoin-knots
|
|
|
|
container:
|
|
image: 146.59.87.168:3000/lfg2025/bitcoin-knots:latest
|
|
pull_policy: if-not-present
|
|
network: archy-net
|
|
entrypoint: ["sh", "-lc"]
|
|
custom_args:
|
|
# Sync-speed flags: -par=0 uses every core (was capped at 2 by
|
|
# --cpus=2, now removed for bitcoin/electrumx). -dbcache sized to
|
|
# the IBD sweet spot - 4GB on full nodes, 1GB on pruned. Container
|
|
# --memory=8g (config.rs::get_memory_limit) leaves headroom for
|
|
# mempool + connections.
|
|
#
|
|
# -printtoconsole=0: foreground bitcoind defaults console logging ON,
|
|
# which pushed every IBD "UpdateTip" line through conmon into journald
|
|
# (>1 GB/day on a fresh node). bitcoind still writes debug.log in the
|
|
# datadir (/var/lib/archipelago/bitcoin/debug.log, self-shrunk on
|
|
# restart) — use that for deep debugging; podman logs only carries
|
|
# entrypoint/startup errors.
|
|
- >-
|
|
BITCOIND="$(command -v bitcoind || true)";
|
|
if [ -z "$BITCOIND" ]; then
|
|
BITCOIND="$(find /opt -path '*/bin/bitcoind' -type f 2>/dev/null | sort | tail -n 1)";
|
|
fi;
|
|
if [ -z "$BITCOIND" ]; then
|
|
echo "bitcoind not found in image" >&2;
|
|
exit 127;
|
|
fi;
|
|
RPC_USER="$(printenv BITCOIN_RPC_USER)";
|
|
RPC_PASS="$(printenv BITCOIN_RPC_PASS)";
|
|
RPC_TXRELAY_AUTH="$(printenv BITCOIN_RPC_TXRELAY_RPCAUTH || true)";
|
|
DISK_GB_VALUE="$(printenv DISK_GB || true)";
|
|
RPC_HEADROOM="-rpcthreads=16 -rpcworkqueue=256";
|
|
RPC_TXRELAY_FLAGS="-rpcwhitelistdefault=0";
|
|
if [ -n "$RPC_TXRELAY_AUTH" ]; then
|
|
RPC_TXRELAY_FLAGS="$RPC_TXRELAY_FLAGS -rpcauth=$RPC_TXRELAY_AUTH -rpcwhitelist=txrelay:sendrawtransaction,submitpackage,testmempoolaccept,getmempoolinfo,getrawmempool,getmempoolentry,getnetworkinfo,getblockchaininfo,getblockcount,getblockhash,getblock,getblockheader,getrawtransaction,gettxout,gettxspendingprevout,decoderawtransaction,decodescript,estimatesmartfee,uptime,ping,getconnectioncount,getpeerinfo,getindexinfo,getdeploymentinfo,getchaintips";
|
|
fi;
|
|
if [ "${DISK_GB_VALUE:-0}" -lt 1000 ]; then
|
|
exec "$BITCOIND" -datadir=/home/bitcoin/.bitcoin -noconf -printtoconsole=0 -server=1 -prune=550 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0:8332 -listen=1 -bind=0.0.0.0:8333 -dbcache=2048 -par=0 -maxconnections=125 $RPC_HEADROOM $RPC_TXRELAY_FLAGS -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS";
|
|
else
|
|
exec "$BITCOIND" -datadir=/home/bitcoin/.bitcoin -noconf -printtoconsole=0 -server=1 -txindex=1 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0:8332 -listen=1 -bind=0.0.0.0:8333 -dbcache=4096 -par=0 -maxconnections=125 $RPC_HEADROOM $RPC_TXRELAY_FLAGS -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS";
|
|
fi
|
|
derived_env:
|
|
- key: DISK_GB
|
|
template: "{{DISK_GB}}"
|
|
secret_env:
|
|
- key: BITCOIN_RPC_PASS
|
|
secret_file: bitcoin-rpc-password
|
|
- key: BITCOIN_RPC_TXRELAY_RPCAUTH
|
|
secret_file: bitcoin-rpc-txrelay-rpcauth
|
|
data_uid: "100101:100101"
|
|
|
|
dependencies:
|
|
- storage: 500Gi
|
|
|
|
resources:
|
|
cpu_limit: 0
|
|
memory_limit: 8Gi
|
|
disk_limit: 500Gi
|
|
|
|
security:
|
|
capabilities: [CHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE]
|
|
readonly_root: false
|
|
network_policy: isolated
|
|
|
|
ports:
|
|
# RPC is auth-only: publish host-local ONLY - the LAN cannot reach
|
|
# nodeIP:8332. In-node consumers (lnd, fedimint, btcpay, mempool-api)
|
|
# dial the container's archy-net alias directly (bitcoin-knots:8332),
|
|
# which needs no publish at all. Do NOT bind the archy-net gateway
|
|
# (10.89.0.1): rootlessport binds in the HOST netns where that address
|
|
# does not exist, and the whole unit crash-loops (2026-07-09, .228).
|
|
# P2P 8333 stays public.
|
|
- host: 8332
|
|
container: 8332
|
|
protocol: tcp
|
|
bind: 127.0.0.1
|
|
- host: 8333
|
|
container: 8333
|
|
protocol: tcp
|
|
|
|
volumes:
|
|
- type: bind
|
|
source: /var/lib/archipelago/bitcoin
|
|
target: /home/bitcoin/.bitcoin
|
|
options: [rw]
|
|
|
|
environment:
|
|
- BITCOIN_RPC_USER=archipelago
|
|
|
|
health_check:
|
|
type: tcp
|
|
endpoint: localhost:8332
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
bitcoin_integration:
|
|
rpc_access: admin
|
|
sync_required: true
|
|
testnet_support: false
|
|
pruning_support: true
|