Files
bitdeals bf2c2d3508 feat: docker image for Bitcoin Core, configured by environment
Replaces lncm/bitcoind, which pins Core 26 — a version that still has legacy
wallets, while the code that talks to it (bt's BitcoindClient) is written for
the descriptor-only behaviour of 29 and later.

The binaries are the official release build, verified by SHA-256 in the same
layer that downloads them; a version bump that forgets the checksum fails the
build instead of shipping something unverified. uid 1000 and /data/.bitcoin are
kept from the image this replaces, so an existing named volume survives the
switch without a recursive chown of a synced chain.

Verified on testnet2: regtest node healthy in ~12 s, descriptor wallet, 101
blocks mined, sendtoaddress accepted — the last one being the check for
BITCOIND_FALLBACKFEE, without which a fresh chain refuses to send.
2026-08-06 11:56:52 +00:00

91 lines
3.9 KiB
Docker

# A container for the Bitcoin Core daemon
#
# The binaries are the official release build, taken from bitcoincore.org and
# checked against a SHA-256 pinned in this file. Building Core from source in
# CI would cost twenty minutes per image for a binary that upstream already
# publishes reproducibly; verifying the checksum is what makes taking it safe.
#
# Bumping BITCOIN_VERSION means bumping both checksums below. Get them from
# https://bitcoincore.org/bin/bitcoin-core-<version>/SHA256SUMS -- if you bump
# the version alone the build fails at `sha256sum -c`, which is the intent.
FROM debian:trixie-slim AS fetch
ARG BITCOIN_VERSION=31.1
ARG BITCOIN_SHA256_X86_64=b80d9c3e04da78fb6f0569685673418cf686fadba9042d926d13fb87ff503f9e
ARG BITCOIN_SHA256_AARCH64=dcf1873f2208ba4f962f3398d47e154c39c0084be8f4553e05c940d0ace3d004
RUN apt-get update \
&& apt-get install -yq --no-install-suggests --no-install-recommends \
ca-certificates wget \
&& rm -rf /var/lib/apt/lists/*
# One RUN, because the checksum must be verified in the same layer that
# downloads: a cached "download" layer paired with a later check would verify
# an artefact nobody fetched in this build.
RUN set -eu; \
arch="$(uname -m)"; \
case "$arch" in \
x86_64) sha="$BITCOIN_SHA256_X86_64" ;; \
aarch64) sha="$BITCOIN_SHA256_AARCH64" ;; \
*) echo "unsupported architecture: $arch" >&2; exit 1 ;; \
esac; \
tarball="bitcoin-${BITCOIN_VERSION}-${arch}-linux-gnu.tar.gz"; \
wget -q "https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}/${tarball}"; \
echo "${sha} ${tarball}" | sha256sum -c -; \
tar -xzf "$tarball" -C /tmp; \
mkdir -p /opt/bitcoin/bin; \
cp "/tmp/bitcoin-${BITCOIN_VERSION}/bin/bitcoind" \
"/tmp/bitcoin-${BITCOIN_VERSION}/bin/bitcoin-cli" /opt/bitcoin/bin/; \
/opt/bitcoin/bin/bitcoind -version | head -n 1
FROM debian:trixie-slim
# RPC. The daemon binds what BITCOIND_RPCBIND says (0.0.0.0 by default, because
# a container's peers reach it by service name); what you publish decides who
# outside can reach it, and the answer should stay "loopback only" -- the RPC
# controls the wallet and has no TLS.
EXPOSE 8332/tcp
# P2P. Publish it to accept inbound peers; without it the node still connects
# out. The port follows the chain: 8333 main, 18333 test, 18444 regtest.
EXPOSE 8333/tcp
# uid/gid 1000 and /data/.bitcoin are not arbitrary: they are what the image
# this one replaces (lncm/bitcoind) used, so an existing named volume keeps
# working across the switch without a recursive chown of a synced chain.
ENV USER_UID=1000
ENV USER_GID=1000
ENV HOME=/data
ENV BITCOIN_DATA=/data/.bitcoin
COPY --from=fetch /opt/bitcoin/bin/ /usr/local/bin/
COPY ./docker/run.sh /usr/local/bin/
COPY ./docker/healthy_check.sh /usr/local/bin/
RUN apt-get update \
&& apt-get install -yq --no-install-suggests --no-install-recommends gosu \
&& rm -rf /var/lib/apt/lists/*
# Both are ENTRYPOINT/HEALTHCHECK targets in exec form, so the bit has to be
# set here: a clone on a filesystem that does not carry it would otherwise
# build an image that cannot start.
RUN chmod +x /usr/local/bin/run.sh /usr/local/bin/healthy_check.sh
# groupadd, not addgroup: the slim images dropped the adduser package.
RUN groupadd --gid $USER_GID bitcoin \
&& useradd --uid $USER_UID --gid $USER_GID --skel /dev/null --create-home --home-dir $HOME bitcoin
VOLUME ${BITCOIN_DATA}
WORKDIR ${HOME}
# ENTRYPOINT, not CMD: everything after the image name is appended to bitcoind's
# own arguments, so `docker run … -reindex` does what it looks like.
ENTRYPOINT ["/usr/local/bin/run.sh"]
# The daemon answers RPC long before the chain is synced, so this reports "can I
# be talked to", not "am I caught up". On a fresh mainnet datadir the first
# answer still waits for the block index to load, hence the start period.
HEALTHCHECK --interval=15s --timeout=10s --start-period=120s --retries=3 \
CMD ["/usr/local/bin/healthy_check.sh"]