diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh index dcc47cc..058223d 100755 --- a/docker_entrypoint.sh +++ b/docker_entrypoint.sh @@ -136,10 +136,6 @@ sed \ -e "s|\${ZMQ_BLOCK}|${ZMQ_BLOCK}|g" \ "${TEMPLATE}" > "${CONF}" -echo "kamado-entrypoint: starting ckpool (solo, ${BITCOIND_VARIANT}) on port ${STRATUM_PORT}, loglevel ${CKPOOL_LOGLEVEL}" -/usr/local/bin/ckpool --btcsolo --config "${CONF}" --sockdir "${SOCKET_DIR}" --log-shares -l "${CKPOOL_LOGLEVEL}" & -CKPOOL_PID=$! - # DB_PATH must live on the persisted main volume; the default # /var/lib/kamado/kamado.db is ephemeral container storage. KAMADO_DATA_DIR=/root/.kamado/data @@ -161,6 +157,42 @@ echo "kamado-entrypoint: starting kamado-api" /usr/local/bin/kamado-api & API_PID=$! +# wait_for_bitcoind blocks until bitcoind responds to getblockchaininfo. +# Called before each ckpool start so we don't launch ckpool into a wall. +wait_for_bitcoind() { + local url="http://${BITCOIN_RPC_USER}:${BITCOIN_RPC_PASSWORD}@${BITCOIN_RPC_HOST}:${BITCOIN_RPC_PORT}" + local backoff=2 + while true; do + if curl -sf --max-time 5 \ + -d '{"jsonrpc":"1.0","method":"getblockchaininfo","params":[]}' \ + -H 'Content-Type: application/json' \ + "${url}" >/dev/null 2>&1; then + return 0 + fi + echo "kamado-entrypoint: waiting for bitcoind (retry in ${backoff}s)..." + sleep "${backoff}" + backoff=$(( backoff < 30 ? backoff * 2 : 30 )) + done +} + +# Supervised ckpool restart loop. When ckpool exits (killed by the API +# on bitcoind failure, or crashed), we wait for bitcoind to be reachable +# again before restarting. This keeps ckpool alive when bitcoind is +# healthy and lets miners failover when it's not — without restart- +# looping the entire container. +run_ckpool_loop() { + while true; do + wait_for_bitcoind + echo "kamado-entrypoint: starting ckpool (solo, ${BITCOIND_VARIANT}) on port ${STRATUM_PORT}, loglevel ${CKPOOL_LOGLEVEL}" + /usr/local/bin/ckpool --btcsolo --config "${CONF}" --sockdir "${SOCKET_DIR}" --log-shares -l "${CKPOOL_LOGLEVEL}" + EXIT_CODE=$? + echo "kamado-entrypoint: ckpool exited (code ${EXIT_CODE}), will restart after bitcoind is reachable" + sleep 2 + done +} +run_ckpool_loop & +CKPOOL_LOOP_PID=$! + # Optional TLS stratum via stunnel sidecar. STUNNEL_PID="" if [[ "${TLS_ENABLED}" == "enabled" ]]; then @@ -305,16 +337,20 @@ fi term() { echo "kamado-entrypoint: SIGTERM — shutting down" - kill -TERM "${API_PID}" "${CKPOOL_PID}" ${STUNNEL_PID:-} 2>/dev/null || true - wait "${API_PID}" "${CKPOOL_PID}" ${STUNNEL_PID:-} 2>/dev/null || true + kill -TERM "${API_PID}" "${CKPOOL_LOOP_PID}" ${STUNNEL_PID:-} 2>/dev/null || true + # Kill any running ckpool process inside the loop. + pkill -TERM -f '/usr/local/bin/ckpool' 2>/dev/null || true + wait "${API_PID}" "${CKPOOL_LOOP_PID}" ${STUNNEL_PID:-} 2>/dev/null || true exit 0 } trap term TERM INT -# shellcheck disable=SC2086 -wait -n ${CKPOOL_PID} ${API_PID} ${STUNNEL_PID:-} +# The API is the critical process — if it exits, the container should +# restart. The ckpool loop manages its own lifecycle independently. +wait "${API_PID}" EXIT_CODE=$? -echo "kamado-entrypoint: a supervised process exited (${EXIT_CODE}), stopping the rest" -kill -TERM "${API_PID}" "${CKPOOL_PID}" ${STUNNEL_PID:-} 2>/dev/null || true +echo "kamado-entrypoint: kamado-api exited (${EXIT_CODE}), stopping the rest" +kill -TERM "${CKPOOL_LOOP_PID}" ${STUNNEL_PID:-} 2>/dev/null || true +pkill -TERM -f '/usr/local/bin/ckpool' 2>/dev/null || true wait || true exit "${EXIT_CODE}"