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.
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
# Intro
|
||||
|
||||
> Русская версия: [README.ru-RU.md](README.ru-RU.md)
|
||||
|
||||
[Bitcoin Core](https://bitcoincore.org/) 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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
```sh
|
||||
docker run --rm -v btcdata:/data/.bitcoin registry.bitdeals.org/bitcoind -reindex
|
||||
```
|
||||
|
||||
## build and publish
|
||||
|
||||
```sh
|
||||
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`:
|
||||
|
||||
```sh
|
||||
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.1` only, or not at all — inside a compose project other services
|
||||
reach it over the internal network by service name. `BITCOIND_RPCALLOWIP`
|
||||
defaults to `0.0.0.0/0` because 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-cli` inside
|
||||
the container reads them from `cli.conf` instead, 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 recursive `chown` would be added to every restart.
|
||||
- **An ElectrumX in front of this node needs two indexes.** Set both
|
||||
`BITCOIND_TXINDEX=1` and `BITCOIND_TXOSPENDERINDEX=1`: ElectrumX 2.x asks
|
||||
`getindexinfo` at startup and exits naming the one that is missing — first
|
||||
`txindex`, then, once that is fixed, `txospenderindex`. Both default to `0`
|
||||
here because they cost disk and most other users do not need them.
|
||||
- **Pruning is incompatible with ElectrumX.** An indexer needs the whole chain;
|
||||
`-prune` through `BITCOIND_EXTRA_ARGS` will make it fail at some later,
|
||||
much less obvious point.
|
||||
- **Core 29 and later have no legacy wallets.** `createwallet` produces a
|
||||
descriptor wallet, and a private key is imported with `importdescriptors`, not
|
||||
`importprivkey`. Code that pins `descriptors=false` fails 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 -c` rather 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.
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
# Общие сведения
|
||||
|
||||
> English version: [README.md](README.md)
|
||||
|
||||
[Bitcoin Core](https://bitcoincore.org/) — эталонная реализация Bitcoin. `bitcoind` — её демон: хранит полную копию цепочки, ретранслирует транзакции и предоставляет интерфейс JSON-RPC.
|
||||
|
||||
Bitcoin Core, работающий демоном в docker-контейнере, настраивается переменными окружения.
|
||||
|
||||
Репозиторий описывает только развёртывание в docker. Двоичные файлы — официальная сборка релиза, проверяемая по SHA-256, зафиксированному в Dockerfile.
|
||||
|
||||
# Использование
|
||||
|
||||
У контейнера два порта, и они не взаимозаменяемы. Порт **RPC** (8332 для main,
|
||||
18332 для test, 18443 для regtest) управляет кошельком и не имеет TLS: держите
|
||||
его на loopback или во внутренней сети docker. Порт **P2P** (8333 / 18333 /
|
||||
18444) — протокол цепочки: опубликуйте его, чтобы принимать входящие соединения,
|
||||
или не публикуйте, и тогда узел работает только на исходящих.
|
||||
|
||||
Сеть выбирается переменной `BITCOIND_CHAIN`, она же задаёт RPC-порт по
|
||||
умолчанию. Неизвестное значение останавливает контейнер, а не откатывается к
|
||||
mainnet.
|
||||
|
||||
## docker-compose
|
||||
|
||||
```yaml
|
||||
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 # оба нужны ElectrumX; уберите,
|
||||
- BITCOIND_TXOSPENDERINDEX=1 # если узел никто не индексирует
|
||||
ports:
|
||||
- 127.0.0.1:18443:18443 # RPC — только loopback
|
||||
volumes:
|
||||
- btcdata:/data/.bitcoin
|
||||
|
||||
volumes:
|
||||
btcdata:
|
||||
```
|
||||
|
||||
## docker cli
|
||||
|
||||
```sh
|
||||
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
|
||||
```
|
||||
|
||||
Всё, что указано после имени образа, добавляется к аргументам демона, поэтому
|
||||
разовая служебная операция не требует нового образа:
|
||||
|
||||
```sh
|
||||
docker run --rm -v btcdata:/data/.bitcoin registry.bitdeals.org/bitcoind -reindex
|
||||
```
|
||||
|
||||
## сборка и публикация
|
||||
|
||||
```sh
|
||||
docker build . --file docker/Dockerfile --tag registry.bitdeals.org/bitcoind
|
||||
docker push registry.bitdeals.org/bitcoind
|
||||
```
|
||||
|
||||
Другая версия Core задаётся аргументом сборки, и вместе с ней меняется
|
||||
контрольная сумма — оба значения берутся из
|
||||
`https://bitcoincore.org/bin/bitcoin-core-<версия>/SHA256SUMS`:
|
||||
|
||||
```sh
|
||||
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
|
||||
```
|
||||
|
||||
# Параметры
|
||||
|
||||
Образы контейнера настраиваются параметрами, передаваемыми при запуске.
|
||||
|
||||
|Параметр|Назначение|
|
||||
|:--------|:-------|
|
||||
|-p 127.0.0.1:8332|Порт RPC. Внутри контейнера демон слушает то, что задано `BITCOIND_RPCBIND`, поэтому доступность определяет то, что опубликовано. RPC полностью управляет кошельком — см. «Замечания»|
|
||||
|-p 8333|P2P-порт. Необязательный: без него узел всё равно подключается к пирам сам, просто к нему подключиться нельзя. Зависит от сети: 8333 для main, 18333 для test, 18444 для regtest|
|
||||
|-v /data/.bitcoin|Каталог данных: цепочка, индекс блоков и кошельки. Без него обновление контейнера означает повторную загрузку цепочки|
|
||||
|-e BITCOIND_CHAIN|Сеть: `main`, `test`, `signet` или `regtest`. Любое другое значение останавливает контейнер. По умолчанию: `main`|
|
||||
|-e BITCOIND_USER|Пользователь RPC. По умолчанию: `user` — измените|
|
||||
|-e BITCOIND_PASSWORD|Пароль RPC. По умолчанию: `pass` — измените|
|
||||
|-e BITCOIND_PORT|Порт RPC. По умолчанию: стандартный порт выбранной сети (`8332`/`18332`/`38332`/`18443`). Задайте явно, чтобы URL RPC не менялся при смене сети|
|
||||
|-e BITCOIND_RPCBIND|Интерфейс, на котором RPC слушает внутри контейнера. По умолчанию: `0.0.0.0`, чтобы другие контейнеры обращались по имени сервиса|
|
||||
|-e BITCOIND_RPCALLOWIP|Кому разрешены вызовы RPC. По умолчанию: `0.0.0.0/0` — закрытость контейнера обеспечивается тем, что опубликовано, а не этим параметром|
|
||||
|-e BITCOIND_FALLBACKFEE|Комиссия (BTC/kvB) на случай, когда в цепочке нет истории для оценки. По умолчанию: `0` (выключено). Свежей regtest-цепочке параметр необходим, например `0.00001`|
|
||||
|-e BITCOIND_TXINDEX|Строить полный индекс транзакций. По умолчанию: `0`. Требуется для ElectrumX. Изменение на существующем каталоге данных вызывает переиндексацию|
|
||||
|-e BITCOIND_TXOSPENDERINDEX|Строить индекс «какая транзакция потратила этот выход». По умолчанию: `0`. Требуется для ElectrumX 2.x. Изменение на существующем каталоге данных вызывает переиндексацию|
|
||||
|-e BITCOIND_EXTRA_ARGS|Дополнительные аргументы `bitcoind`, разделяемые пробелами и добавляемые последними — то есть они перекрывают всё вышеперечисленное|
|
||||
|
||||
# Замечания
|
||||
|
||||
- **RPC полностью управляет кошельком и не имеет TLS.** Публикуйте его только на
|
||||
`127.0.0.1` или не публикуйте вовсе — внутри compose-проекта другие сервисы
|
||||
обращаются к нему по имени сервиса во внутренней сети. Значение
|
||||
`BITCOIND_RPCALLOWIP` по умолчанию `0.0.0.0/0`, потому что только оно работает
|
||||
для обращений между контейнерами; закрытость даёт публикация портов.
|
||||
- **Учётные данные RPC видны в списке процессов контейнера.** Это аргументы
|
||||
демона, и иначе нельзя: сетевые параметры Core в конфигурационном файле
|
||||
действуют только внутри секции `[chain]`. Внутри контейнера `bitcoin-cli`
|
||||
берёт их из `cli.conf`, поэтому проверка состояния второй копии не создаёт.
|
||||
- **Каталог данных из другого образа требует однократного `chown`.** Демон
|
||||
работает под uid 1000, точка входа исправляет владельца самого каталога, но
|
||||
намеренно не обходит его содержимое: синхронизированный mainnet — сотни
|
||||
гигабайт, и рекурсивный `chown` добавлялся бы к каждому перезапуску.
|
||||
- **ElectrumX перед этим узлом требует двух индексов.** Задайте одновременно
|
||||
`BITCOIND_TXINDEX=1` и `BITCOIND_TXOSPENDERINDEX=1`: ElectrumX 2.x при старте
|
||||
запрашивает `getindexinfo` и завершается, назвав недостающий — сначала
|
||||
`txindex`, а после его включения `txospenderindex`. По умолчанию оба выключены,
|
||||
потому что занимают место, а большинству других применений не нужны.
|
||||
- **Обрезка цепочки (pruning) несовместима с ElectrumX.** Индексатору нужна вся
|
||||
цепочка; `-prune` через `BITCOIND_EXTRA_ARGS` приведёт к отказу позже и в куда
|
||||
менее очевидном месте.
|
||||
- **В Core 29 и новее нет legacy-кошельков.** `createwallet` создаёт
|
||||
дескрипторный кошелёк, приватный ключ импортируется через `importdescriptors`,
|
||||
а не `importprivkey`. Код, жёстко задающий `descriptors=false`, падает уже при
|
||||
создании кошелька.
|
||||
- **Смена версии — это и смена контрольной суммы.** Сборка скачивает архив
|
||||
релиза и проверяет его в том же слое; изменение одной лишь версии приводит к
|
||||
ошибке `sha256sum -c`, а не к выпуску непроверенного образа.
|
||||
- Контейнер становится healthy, как только отвечает RPC, — задолго до
|
||||
синхронизации цепочки. Это сделано намеренно: состояние «unhealthy трое суток
|
||||
начальной загрузки блоков» приводило бы к перезапускам узла всем, что следит
|
||||
за состоянием контейнеров.
|
||||
@@ -0,0 +1,28 @@
|
||||
services:
|
||||
bitcoind:
|
||||
build:
|
||||
# Repository root, not ./docker: the Dockerfile copies ./docker/run.sh
|
||||
# and ./docker/healthy_check.sh, and those paths are resolved against the
|
||||
# build context.
|
||||
context: .
|
||||
dockerfile: ./docker/Dockerfile
|
||||
image: registry.bitdeals.org/bitcoind
|
||||
environment:
|
||||
- BITCOIND_CHAIN=regtest
|
||||
- BITCOIND_USER=CHANGE_ME
|
||||
- BITCOIND_PASSWORD=CHANGE_ME
|
||||
# A fresh regtest chain has no fee history, so an unqualified
|
||||
# sendtoaddress is refused until this is set. 0.00001 BTC/kvB = 1 sat/vB.
|
||||
- BITCOIND_FALLBACKFEE=0.00001
|
||||
# Both are off by default and both are required by an ElectrumX in front
|
||||
# of this node -- drop them if nothing indexes it.
|
||||
- BITCOIND_TXINDEX=1
|
||||
- BITCOIND_TXOSPENDERINDEX=1
|
||||
ports:
|
||||
# RPC controls the wallet and has no TLS -- loopback only.
|
||||
- 127.0.0.1:18443:18443
|
||||
volumes:
|
||||
- btcdata:/data/.bitcoin
|
||||
|
||||
volumes:
|
||||
btcdata:
|
||||
@@ -0,0 +1,90 @@
|
||||
# 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"]
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Healthy = the RPC answers. Not "the chain is synced": an initial block
|
||||
# download takes days on mainnet, and a container that reports unhealthy for all
|
||||
# of it would be restarted by every orchestrator that watches health.
|
||||
#
|
||||
# The credentials come from cli.conf, written by run.sh, so they stay out of the
|
||||
# process list -- unlike the daemon's own arguments, which cannot avoid them.
|
||||
|
||||
set -eu
|
||||
|
||||
exec gosu bitcoin bitcoin-cli \
|
||||
-datadir="${BITCOIN_DATA:-/data/.bitcoin}" \
|
||||
-conf="${BITCOIN_DATA:-/data/.bitcoin}/cli.conf" \
|
||||
getblockchaininfo > /dev/null
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
export BITCOIND_CHAIN="${BITCOIND_CHAIN:-main}"
|
||||
export BITCOIND_USER="${BITCOIND_USER:-user}"
|
||||
export BITCOIND_PASSWORD="${BITCOIND_PASSWORD:-pass}"
|
||||
export BITCOIND_FALLBACKFEE="${BITCOIND_FALLBACKFEE:-0}"
|
||||
export BITCOIND_RPCBIND="${BITCOIND_RPCBIND:-0.0.0.0}"
|
||||
export BITCOIND_RPCALLOWIP="${BITCOIND_RPCALLOWIP:-0.0.0.0/0}"
|
||||
export BITCOIND_TXINDEX="${BITCOIND_TXINDEX:-0}"
|
||||
export BITCOIND_TXOSPENDERINDEX="${BITCOIND_TXOSPENDERINDEX:-0}"
|
||||
export BITCOIND_EXTRA_ARGS="${BITCOIND_EXTRA_ARGS:-}"
|
||||
|
||||
# The chain name decides the default RPC port, so an unknown value must stop the
|
||||
# container rather than fall through to a default: "-chain=testnet" (the name
|
||||
# Core does not use -- it wants "test") would otherwise be a mainnet node
|
||||
# holding a wallet the caller believes is worthless.
|
||||
case "$BITCOIND_CHAIN" in
|
||||
main) default_port=8332 ;;
|
||||
test) default_port=18332 ;;
|
||||
signet) default_port=38332 ;;
|
||||
regtest) default_port=18443 ;;
|
||||
*)
|
||||
echo "BITCOIND_CHAIN must be one of main, test, signet, regtest (got '$BITCOIND_CHAIN')" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
export BITCOIND_PORT="${BITCOIND_PORT:-$default_port}"
|
||||
case "$BITCOIND_PORT" in
|
||||
'' | *[!0-9]*)
|
||||
echo "BITCOIND_PORT must be a positive integer" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# A named volume starts out owned by root. Non-recursive on purpose: the only
|
||||
# case that needs fixing is the empty datadir, and a synced mainnet chain is
|
||||
# hundreds of gigabytes -- walking it on every start would add minutes to each
|
||||
# restart. A datadir moved here from an image with another uid must be chowned
|
||||
# by hand, once (see README, "Notes").
|
||||
mkdir -p "$BITCOIN_DATA"
|
||||
if [ "$(stat -c %u "$BITCOIN_DATA")" != "$USER_UID" ]
|
||||
then
|
||||
chown "$USER_UID:$USER_GID" "$BITCOIN_DATA"
|
||||
fi
|
||||
|
||||
# Credentials for bitcoin-cli, so the health check does not have to repeat them
|
||||
# on a command line. Written before the daemon starts and readable only by the
|
||||
# daemon's user. rpcport is deliberately at the top level: bitcoin-cli is
|
||||
# invoked without -chain and therefore reads the mainnet section, whatever chain
|
||||
# the daemon runs -- what matters is that the number matches.
|
||||
cli_conf="${BITCOIN_DATA}/cli.conf"
|
||||
umask 077
|
||||
cat > "$cli_conf" <<EOF
|
||||
rpcconnect=127.0.0.1
|
||||
rpcport=${BITCOIND_PORT}
|
||||
rpcuser=${BITCOIND_USER}
|
||||
rpcpassword=${BITCOIND_PASSWORD}
|
||||
EOF
|
||||
chown "$USER_UID:$USER_GID" "$cli_conf"
|
||||
umask 022
|
||||
|
||||
# Everything goes on the command line rather than into bitcoin.conf. Core treats
|
||||
# rpcport, port and bind as network-specific options: in a config file they only
|
||||
# apply inside the matching [main]/[test]/[regtest] section, while on the
|
||||
# command line they apply to the chain in force. One list that behaves the same
|
||||
# on every network is worth more here than a config file nobody edits.
|
||||
set -- \
|
||||
-chain="$BITCOIND_CHAIN" \
|
||||
-datadir="$BITCOIN_DATA" \
|
||||
-server=1 \
|
||||
-printtoconsole=1 \
|
||||
-rpcport="$BITCOIND_PORT" \
|
||||
-rpcuser="$BITCOIND_USER" \
|
||||
-rpcpassword="$BITCOIND_PASSWORD" \
|
||||
-rpcbind="$BITCOIND_RPCBIND" \
|
||||
-rpcallowip="$BITCOIND_RPCALLOWIP" \
|
||||
-txindex="$BITCOIND_TXINDEX" \
|
||||
-txospenderindex="$BITCOIND_TXOSPENDERINDEX" \
|
||||
-fallbackfee="$BITCOIND_FALLBACKFEE" \
|
||||
"$@"
|
||||
|
||||
# Unquoted on purpose: this is the one variable whose whole point is to be split
|
||||
# into separate arguments.
|
||||
# shellcheck disable=SC2086
|
||||
set -- "$@" $BITCOIND_EXTRA_ARGS
|
||||
|
||||
exec gosu bitcoin bitcoind "$@"
|
||||
Reference in New Issue
Block a user