-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalues.yaml
More file actions
365 lines (336 loc) · 17.7 KB
/
Copy pathvalues.yaml
File metadata and controls
365 lines (336 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# Bitcoin node — Knots or Core, as a hardened StatefulSet.
#
# A Bitcoin node is a single, heavily-stateful process: one StatefulSet, one big
# PVC, no replicas, no scaling. That is genuinely all it needs.
#
# bitcoind (StatefulSet, 1 replica)
# ├─ P2P :8333 peers on the Bitcoin network (in + out)
# ├─ RPC :8332 ClusterIP only — NEVER expose this publicly
# └─ ZMQ :28332 block notifications for subscribers (a mining pool)
# datadir /home/bitcoin/.bitcoin ── the PVC (the whole blockchain)
#
# Defaults below are a mainnet archival node. See the README for the values to
# change for a pruned, signet, or regtest node.
# ── Which implementation ───────────────────────────────────────────────────────
node:
# knots | core | custom
#
# This selects two things: the default image (see `image` below) and which
# config *dialect* the safety guards assume. It does NOT gate config injection —
# node.config, node.configList and node.extraConfig are rendered verbatim on
# every implementation.
#
# knots Core plus extra policy/relay options and a different default policy
# stance. The default image is the pinned Knots preset.
# core the reference implementation. The render FAILS if a Knots-only
# option (consensusrules, spkreuse, corepolicy) is set, because Core
# exits on an unknown option and would crash-loop.
# custom bring your own bitcoind-compatible image (set `image.*` below).
# There is no preset and no dialect assumption — the Knots-only guard
# does not run, so you own the config entirely. The memory, grace-
# period and digest-pinning guards still apply.
implementation: knots
# main | test | signet | regtest. Network-scoped options below are emitted into
# the matching [section] of bitcoin.conf, which is what bitcoind expects.
network: main
# bitcoin.conf, as data.
#
# Every key here becomes a `key=value` line. Any bitcoind option works without
# touching the chart — that is the point. Options bitcoind expects more than
# once (addnode, onlynet, bind…) go in `configList` below instead.
#
# The two that matter most:
# dbcache in-memory UTXO cache (MiB). The single biggest lever on
# initial block download speed. It is also the single easiest
# way to destroy a node: if bitcoind is OOM-killed it never
# flushes this cache to disk, and the chainstate is corrupted.
# The chart REFUSES to install unless
# resources.limits.memory >= dbcache + safety.memoryHeadroom.
# prune 0 = full archival node (needs the full storage.size below).
# Otherwise the target size of the pruned block store in MiB
# (20000 ≈ 20 GB). A pruned node cannot serve historic blocks
# to peers or rescan old wallet history.
config:
dbcache: 4096
maxconnections: 64
listen: 1
prune: 0
# Options bitcoind accepts repeatedly. Each list item becomes its own line.
# addnode: [seed1.example.com, seed2.example.com]
# onlynet: [onion]
configList: {}
# Raw bitcoin.conf lines, appended verbatim. An escape hatch for anything the
# two maps above cannot express (section headers, comments).
extraConfig: []
# ── Image ──────────────────────────────────────────────────────────────────────
# Pinned by digest, never by tag alone: a tag is a mutable pointer, a digest is
# the artifact. The chart refuses to render an unpinned image unless you opt out
# explicitly (see safety.allowUnpinnedImage).
#
# These presets are what `node.implementation` selects between. Renovate keeps
# the digests current; do not reformat these three lines, its regex matches them.
images:
knots:
repository: bitcoinknots/bitcoin
tag: "29.3.knots20260508"
digest: "sha256:7005235c5728c867abbd85c43e05725c06b451e26c31a40f4955e3f949d54669"
core:
repository: bitcoin/bitcoin
tag: "31.1"
digest: "sha256:da25cedc66b1daefff9f412ee196c901a899c3fa68a33b20849c3e08b5c40d63"
# Bring your own build. Leave repository empty to use the pinned preset for
# node.implementation; set it (usually together with node.implementation: custom)
# to run any bitcoind-compatible image. image.* is used as a UNIT — the chart
# never merges your repository with a preset's digest — so set the three together:
# repository registry URL + repo path, e.g. ghcr.io/you/bitcoin
# tag a version or commit reference, e.g. v29.3 or git-3f1a9c2
# digest sha256:… — the actual pin. A tag (even a commit tag) is a mutable
# pointer; the digest is required unless safety.allowUnpinnedImage.
image:
repository: ""
tag: ""
digest: ""
pullPolicy: IfNotPresent
# ── RPC (JSON-RPC / bitcoin-cli) ───────────────────────────────────────────────
# The Service is ClusterIP and stays ClusterIP. RPC is a full-control interface
# to the node — it can stop the process, and move coins if a wallet is loaded —
# authenticated with HTTP Basic, which means the password crosses the wire on
# every call.
#
# It CAN be published under a hostname with TLS via `networking.rpc` below, but
# only deliberately: that path refuses to render until you have narrowed
# allowSubnet and given it a certificate. Prefer publishing the pool's read-only
# stats API instead if that is what you actually want.
rpc:
enabled: true
username: bitcoin
port: 8332
# rpcallowip subnet permitted to reach the RPC port. Because the Service is
# ClusterIP-only, the broad default just needs to cover the pod network — and
# it is only safe for exactly as long as that stays true. Publishing RPC makes
# it untrue, which is why `networking.rpc` refuses to render while this is
# still 0.0.0.0/0.
allowSubnet: 0.0.0.0/0
# ── ZMQ (block notifications) ──────────────────────────────────────────────────
# bitcoind pushes new blocks over ZMQ so subscribers hear about them the instant
# they arrive instead of polling. A mining pool needs this: without it, miners
# keep grinding stale work for seconds after every new block.
zmq:
enabled: true
rawBlockPort: 28332
hashBlockPort: 28333
# ── P2P ────────────────────────────────────────────────────────────────────────
p2p:
port: 8333
service:
# ClusterIP is enough for outbound-only. Switch to LoadBalancer (or NodePort)
# and forward the port on your router to accept INBOUND peers, which is how
# you give back to the network rather than only taking from it.
type: ClusterIP
loadBalancerIP: ""
# ── Hostnames and certificates ─────────────────────────────────────────────────
# Off by default: every endpoint below starts with an empty `scopes` list, and an
# empty list means "not published". Nothing here renders until you opt in.
#
# HOW a name is published lives in `global.networking.scopes` — one map, shared
# by every chart in the release, so the domain and the issuers are written once.
# WHAT is published lives here, as a list of scope names per endpoint. Publishing
# something in two places at once is just a two-element list.
#
# Whether an endpoint gets an Ingress or only a DNS record is not a setting: it
# follows from the protocol. RPC is HTTP and can terminate TLS behind an Ingress.
# P2P is a raw TCP stream with no Host header and no SNI, so an ingress
# controller has nothing to route on — it gets a name pointing at its
# LoadBalancer address, and no certificate.
#
# The chart emits annotations. It does not install cert-manager or external-dns,
# and does not check that they are running.
networking:
# A stable name for the address peers connect to, so it survives the
# LoadBalancer IP changing. Requires p2p.service.type to be LoadBalancer or
# NodePort — there is nothing to publish about a ClusterIP.
#
# If you give P2P a public name you will usually also want bitcoind to
# announce it, which this chart does not do for you:
# node.extraConfig: ["externalip=node.example.com"]
p2p:
scopes: []
name: node
# Override the derived hostname for one scope:
# hosts: {external: node.example.net}
hosts: {}
# DANGEROUS. Read the note on `rpc` above first.
#
# Refuses to render unless rpc.allowSubnet has been narrowed away from
# 0.0.0.0/0 and every listed scope resolves a certificate. There is no
# plaintext RPC Ingress in this chart, on any scope.
rpc:
scopes: []
name: rpc
hosts: {}
# Bring your own certificate instead of issuing one, per scope:
# tlsSecrets: {internal: my-existing-tls-secret}
tlsSecrets: {}
# Ingress class. Empty uses the cluster's default IngressClass.
className: ""
# Extra annotations, merged over the generated ones.
annotations: {}
# ── Scopes: how a name gets published ──────────────────────────────────────────
# Under the umbrella chart this is set once at the top level and both subcharts
# read it. The defaults here exist so this chart also works installed on its own.
#
# `internal` and `external` are ordinary entries, not special names — rename
# them, drop one, or add a third.
global:
networking:
# The domain hostnames are derived from. Required once anything is published.
baseDomain: ""
scopes:
# A name that resolves on the LAN only. Note that this does NOT imply a
# self-signed certificate: an ACME issuer solving DNS-01 will issue a
# publicly-trusted cert for a host that resolves nowhere on the internet,
# because DNS-01 never connects to the endpoint.
internal:
subdomain: internal # -> rpc.internal.example.com
issuer: "" # cert-manager issuer; empty = no cert issued
issuerKind: ClusterIssuer # ClusterIssuer | Issuer
publishDns: false # let external-dns create the record
ttl: "300"
annotations: {} # provider extras, added to Ingresses only
external:
subdomain: "" # -> rpc.example.com
issuer: ""
issuerKind: ClusterIssuer
publishDns: false
ttl: "300"
# Provider-specific options go here, e.g. for Cloudflare:
# external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
annotations: {}
# ── The RPC credential ─────────────────────────────────────────────────────────
# Three ways to get it, none of which is "write it in values.yaml":
#
# generated the chart generates a strong password on first install and
# reuses it on every upgrade (it reads back the live Secret, so
# an upgrade does not rotate it and break the pool).
# existingSecret you created the Secret yourself; the chart just reads it.
# externalSecret synced from a real secret manager (OpenBao/Vault) by the
# External Secrets Operator. This is the right answer in prod.
secret:
provider: generated
existingSecret:
name: ""
key: rpc-password
externalSecret:
secretStore: openbao
storeKind: ClusterSecretStore
# Seed it once, before first sync:
# bao kv put secret/bitcoin-stack/node rpc-password=<strong password>
secretPath: bitcoin-stack/node
property: rpc-password
refreshInterval: 1h
# ── Storage ────────────────────────────────────────────────────────────────────
storage:
# A mainnet archival node is ~650-700GB in 2026 and grows ~60GB/year. Give it
# real headroom: running out of disk mid-write can corrupt the chainstate, and
# this volume cannot be grown by editing this value alone — volumeClaimTemplates
# are immutable. See docs/failure-modes.md before you change it.
size: 900Gi
# StorageClass for the datadir. Empty = the cluster's default StorageClass.
#
# This is the single biggest *hardware* lever on sync speed, and the easiest to
# get wrong. Initial block download is random-I/O bound, and a Bitcoin node is a
# single, fully re-syncable dataset with no wallet — so replicating it buys
# durability you do not need while its network data path throttles the sync.
# PREFER NODE-LOCAL DISK (local NVMe: a `local-path` provisioner, or a `local`
# PV). Replicated / network storage (Longhorn, Ceph, NFS, cloud block volumes)
# can turn a ~1-day sync into weeks, and can silently place the volume replica
# on a different node than the pod so every cache-miss read is a cross-node
# round trip. If you must use it, keep the data node-local (nodeSelector/affinity
# onto a node holding a replica) and give memory real headroom (below) so page
# cache survives. See docs/failure-modes.md #7 ("Slow or remote storage").
storageClass: ""
# Keep the PVC when the release is uninstalled. It holds hundreds of GB that
# take weeks to re-download; deleting it should be a deliberate, manual act.
keepOnDelete: true
accessMode: ReadWriteOnce
# ── Resources ──────────────────────────────────────────────────────────────────
# bitcoind is I/O-bound, not CPU-bound: during initial block download it is
# mostly waiting on the disk. Memory is the lever that matters — too little and
# the kernel evicts page cache the node needs back moments later, turning every
# eviction into another random read.
#
# limits.memory must be >= node.config.dbcache + safety.memoryHeadroom, or the
# chart refuses to install. This is not a style preference: an OOM kill during a
# chainstate flush corrupts the datadir.
resources:
requests:
cpu: 500m
memory: 6Gi
limits:
cpu: "4"
memory: 8Gi
# ── Safety rails ───────────────────────────────────────────────────────────────
# Each of these exists because of a real way a Bitcoin node gets destroyed.
# docs/failure-modes.md explains every one. Turning them off is allowed, and is
# very rarely a good idea.
safety:
# Peak RSS runs above dbcache (block index, mempool, peer buffers). This is the
# margin the chart demands between dbcache and limits.memory.
memoryHeadroom: 2Gi
# bitcoind must flush its dirty UTXO cache on SIGTERM or the datadir needs a
# slow reindex. The flush time scales with dbcache, so the chart requires a
# grace period that scales with it too.
enforceGracePeriod: true
# Refuse to render an image that is not pinned by digest.
allowUnpinnedImage: false
# The chart computes a minimum from dbcache when safety.enforceGracePeriod is on
# and fails if this is below it. Raise it, never lower it.
terminationGracePeriodSeconds: 900
# ── Recovery mode ──────────────────────────────────────────────────────────────
# Turn this on ONLY when the node needs to reindex or otherwise take a very long
# time to start.
#
# The startup probe gives bitcoind ~10 minutes to open its P2P port. A -reindex
# takes DAYS. Without this switch the kubelet would kill the node every 10
# minutes, forever, and it could never finish recovering — each kill making the
# damage worse. This drops the startup and liveness probes so recovery can run to
# completion. Turn it back off afterwards.
recovery:
enabled: false
# ── Hardening ──────────────────────────────────────────────────────────────────
# The official bitcoind images run as uid 101 and keep the datadir at
# /home/bitcoin/.bitcoin. Both Knots and Core share this layout.
podSecurityContext:
runAsUser: 101
runAsGroup: 101
runAsNonRoot: true
fsGroup: 101
seccompProfile:
type: RuntimeDefault
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
serviceAccount:
create: true
name: ""
# bitcoind never talks to the Kubernetes API. Do not hand it a token.
automountServiceAccountToken: false
# There is only ever one bitcoind, and two of them on one datadir is fatal. The
# PDB stops a node drain from evicting it without a graceful flush.
podDisruptionBudget:
enabled: true
# The datadir is the only thing bitcoind writes to. Everything else is read-only.
dataDir: /home/bitcoin/.bitcoin
# With node-local storage (see storage.storageClass), pin the pod to the node
# that holds the datadir so it schedules where its disk is: nodeSelector for a
# hostname, or affinity for something richer. Co-locating compute with its data is
# what keeps reads on the local disk instead of crossing the network — the biggest
# single win for sync speed. See docs/failure-modes.md #7 ("Slow or remote storage").
nodeSelector: {}
tolerations: []
affinity: {}
podAnnotations: {}
podLabels: {}