# 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-/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"]