Also re-triggers the build, which is what this commit is for right now: the first run built the image and then stopped at docker login, because the registry secrets did not exist for this repository yet. workflow_dispatch earns its place beyond that — the image content depends on BITCOIN_VERSION, a build argument, so a new upstream release is a reason to rebuild without a commit to point at.
Intro
Русская версия: README.ru-RU.md
Bitcoin Core is the reference Bitcoin implementation. bitcoind is its daemon: it keeps a full copy of the chain, relays transactions and serves a JSON-RPC interface.
Bitcoin Core running as a daemon in a docker container, configured by environment variables.
This repository covers the docker deployment only. The binaries are the official release build, checked against a SHA-256 pinned in the Dockerfile.
Usage
The container has two ports and they are not interchangeable. The RPC port (8332 main, 18332 test, 18443 regtest) controls the wallet and has no TLS: keep it on loopback, or on an internal docker network. The P2P port (8333 / 18333 / 18444) is the chain protocol: publish it to accept inbound peers, leave it unpublished to stay outbound-only.
The chain is selected by BITCOIND_CHAIN, and it also sets the default RPC
port. An unknown value stops the container instead of falling back to mainnet.
docker-compose
services:
bitcoind:
build:
context: https://git.bitdeals.org/private/bitcoind.git
dockerfile: ./docker/Dockerfile
image: registry.bitdeals.org/bitcoind
environment:
- BITCOIND_CHAIN=regtest
- BITCOIND_USER=CHANGE_ME
- BITCOIND_PASSWORD=CHANGE_ME
- BITCOIND_FALLBACKFEE=0.00001
- BITCOIND_TXINDEX=1 # both required by ElectrumX,
- BITCOIND_TXOSPENDERINDEX=1 # drop them if nothing indexes this node
ports:
- 127.0.0.1:18443:18443 # RPC — loopback only
volumes:
- btcdata:/data/.bitcoin
volumes:
btcdata:
docker cli
docker run -d \
-e BITCOIND_CHAIN=regtest \
-e BITCOIND_USER=CHANGE_ME \
-e BITCOIND_PASSWORD=CHANGE_ME \
-e BITCOIND_FALLBACKFEE=0.00001 \
-p 127.0.0.1:18443:18443 \
-v btcdata:/data/.bitcoin \
registry.bitdeals.org/bitcoind
Anything after the image name is appended to the daemon's own arguments, so a one-off maintenance run needs no new image:
docker run --rm -v btcdata:/data/.bitcoin registry.bitdeals.org/bitcoind -reindex
build and publish
A push to main builds and publishes the image (.gitea/workflows/build.yaml),
tagging it three ways: <version>.<sha7> to deploy by, <version> to read, and
latest for compose and Watchtower. By hand, when the registry credentials are
at hand:
docker build . --file docker/Dockerfile --tag registry.bitdeals.org/bitcoind
docker push registry.bitdeals.org/bitcoind
A different Core version is a build argument, and the checksum must move with
it — take both from https://bitcoincore.org/bin/bitcoin-core-<version>/SHA256SUMS:
docker build . --file docker/Dockerfile \
--build-arg BITCOIN_VERSION=31.1 \
--build-arg BITCOIN_SHA256_X86_64=b80d9c3e04da78fb6f0569685673418cf686fadba9042d926d13fb87ff503f9e \
--tag registry.bitdeals.org/bitcoind:31.1
Parameters
Container images are configured using parameters passed at runtime.
| Parameter | Function |
|---|---|
| -p 127.0.0.1:8332 | RPC port. The daemon binds what BITCOIND_RPCBIND says, so what you publish decides who reaches it. It has full control of the wallet — see "Notes" |
| -p 8333 | P2P port. Optional: without it the node still connects out to peers, it just cannot be connected to. Follows the chain: 8333 main, 18333 test, 18444 regtest |
| -v /data/.bitcoin | Data directory: the chain, the block index and the wallets. Without it a container update means downloading the chain again |
| -e BITCOIND_CHAIN | Network: main, test, signet or regtest. Anything else stops the container. Default: main |
| -e BITCOIND_USER | RPC user. Default: user — change it |
| -e BITCOIND_PASSWORD | RPC password. Default: pass — change it |
| -e BITCOIND_PORT | RPC port. Default: the standard port of the selected chain (8332/18332/38332/18443). Pin it to keep one RPC URL across networks |
| -e BITCOIND_RPCBIND | Interface the RPC listens on inside the container. Default: 0.0.0.0, so other containers reach it by service name |
| -e BITCOIND_RPCALLOWIP | Who may call the RPC. Default: 0.0.0.0/0 — the container is expected to be closed off by what you publish, not by this |
| -e BITCOIND_FALLBACKFEE | Fee rate (BTC/kvB) used when the chain has no fee history to estimate from. Default: 0 (disabled). A fresh regtest chain needs it, e.g. 0.00001 |
| -e BITCOIND_TXINDEX | Build the full transaction index. Default: 0. Required by ElectrumX. Changing it on an existing datadir forces a reindex |
| -e BITCOIND_TXOSPENDERINDEX | Build the index of which transaction spent each output. Default: 0. Required by ElectrumX 2.x. Changing it on an existing datadir forces a reindex |
| -e BITCOIND_EXTRA_ARGS | Extra bitcoind arguments, split on whitespace and appended last — so they override everything above |
Notes
- The RPC has full control of the wallet and no TLS. Publish it to
127.0.0.1only, or not at all — inside a compose project other services reach it over the internal network by service name.BITCOIND_RPCALLOWIPdefaults to0.0.0.0/0because that is the only value that works for container-to-container calls; the port mapping is what keeps it private. - The RPC credentials are visible in the container's process list. They are
daemon arguments, which is how Core takes network-specific options that a
config file would apply only inside a
[chain]section.bitcoin-cliinside the container reads them fromcli.confinstead, so the health check does not add a second copy. - A datadir from another image needs one manual
chown. The daemon runs as uid 1000 and the entrypoint fixes ownership of the data directory itself, but it deliberately does not walk it: a synced mainnet datadir is hundreds of gigabytes and a recursivechownwould be added to every restart. - An ElectrumX in front of this node needs two indexes. Set both
BITCOIND_TXINDEX=1andBITCOIND_TXOSPENDERINDEX=1: ElectrumX 2.x asksgetindexinfoat startup and exits naming the one that is missing — firsttxindex, then, once that is fixed,txospenderindex. Both default to0here because they cost disk and most other users do not need them. - Pruning is incompatible with ElectrumX. An indexer needs the whole chain;
-prunethroughBITCOIND_EXTRA_ARGSwill make it fail at some later, much less obvious point. - Core 29 and later have no legacy wallets.
createwalletproduces a descriptor wallet, and a private key is imported withimportdescriptors, notimportprivkey. Code that pinsdescriptors=falsefails at wallet creation. - Bumping the version means bumping the checksum. The build downloads the
release tarball and verifies it in the same layer; a version bump on its own
fails at
sha256sum -crather than shipping something unverified. - The container turns healthy as soon as the RPC answers, which is long before the chain is synced. That is on purpose: "unhealthy for three days of initial block download" would get the node restarted by anything watching health.