This guide walks you through installing the operator, deploying your first blockchain node, and performing common operations.
- Kubernetes cluster 1.26+ (Kind, EKS, GKE, AKS, etc.)
kubectlconfigured to access your cluster- (Optional) Helm 3 for Helm-based deployment
- (Optional) MinIO instance for snapshot bootstrapping (set
MINIO_ENDPOINTenv var on the operator)
Blockchain nodes can take days or weeks to sync from genesis. The operator supports snapshot bootstrap via MinIO (or any S3-compatible storage): the init container downloads a compressed snapshot before the node starts, reducing sync time to hours.
Set up MinIO with the bundled Helm subchart:
helm install chainplane ./charts/chainplane \
--namespace chainplane-system \
--create-namespace \
--set minio.enabled=true \
--set minio.rootUser=minioadmin \
--set minio.rootPassword=changeme \
--set snapshot.enabled=true \
--set snapshot.minio.endpoint=http://chainplane-minio:9000 \
--set snapshot.minio.accessKey=minioadmin \
--set snapshot.minio.secretKey=changemeOr use an existing MinIO / S3-compatible endpoint:
helm install chainplane ./charts/chainplane \
--namespace chainplane-system \
--create-namespace \
--set snapshot.enabled=true \
--set snapshot.minio.endpoint=http://minio.minio.svc:9000 \
--set snapshot.minio.existingSecret=minio-credentialsThe Secret must contain MINIO_ACCESS_KEY and MINIO_SECRET_KEY keys.
Populate snapshot buckets:
The operator looks for snapshots in buckets named snapshots-<chain> (e.g. snapshots-bsc, snapshots-ethereum). Upload compressed chaindata archives there. Public snapshot providers:
- Cosmos ecosystem: Polkachu —
snapshots-cosmos,snapshots-osmosis, etc. - BSC / Ethereum: ChainData, Snapshot Finder
- Solana: Triton, Stakewiz
Once a snapshot is in the bucket, enable it per-node:
spec:
snapshot:
disabled: false # default; omit this field to use snapshot
type: full # or "lite" for supported chains (e.g. TRON)To skip snapshot bootstrap for a specific node (sync from genesis):
spec:
snapshot:
disabled: trueBlockchain nodes require significant persistent storage with high IOPS. Ensure your cluster has a StorageClass that provisions SSDs. For production workloads, use a StorageClass backed by NVMe or SSD volumes (e.g., gp3 on AWS, pd-ssd on GCP, managed-premium on Azure).
# From source
git clone https://github.com/tazhate/chainplane.git
cd chainplane
make installOr apply the generated CRD manifests directly:
kubectl apply -f dist/install.yamlmake deploy IMG=ghcr.io/tazhate/chainplane:latestThis deploys the controller manager with RBAC, ServiceAccount, and CRDs into the chainplane-system namespace.
helm install chainplane ./charts/chainplane \
--namespace chainplane-system \
--create-namespaceGenerate a single install manifest:
make build-installer IMG=ghcr.io/tazhate/chainplane:latest
kubectl apply -f dist/install.yamlkubectl get pods -n chainplane-systemYou should see the chainplane-controller-manager pod in Running state.
Save the following as bitcoin-node.yaml:
apiVersion: chains.chainplane.io/v1alpha2
kind: ChainInstance
metadata:
labels:
app.kubernetes.io/name: chainplane
app.kubernetes.io/managed-by: kustomize
name: bitcoin-mainnet
spec:
chain: bitcoin
network: mainnet
nodeType: rpc
nodeGroup: medium
storage:
size: 600Gi
storageClass: fast-ssd
resources:
requests:
cpu: "2"
memory: 4Gi
limits:
cpu: "4"
memory: 8Gi
rpc:
enabled: true
port: 8332
health:
blockLagThreshold: 2Apply it:
kubectl apply -f bitcoin-node.yamlWatch the node status:
kubectl get chaininstances -wOutput columns: Chain, Network, Type, Phase, Height, Peers, Sync, ETA, Age.
Example output during sync:
NAME CHAIN NETWORK TYPE PHASE HEIGHT PEERS SYNC ETA AGE
bitcoin-mainnet bitcoin mainnet rpc Syncing 650000 8 72.5% 3h20m 15m
Once fully synced:
NAME CHAIN NETWORK TYPE PHASE HEIGHT PEERS SYNC ETA AGE
bitcoin-mainnet bitcoin mainnet rpc Healthy 886000 12 100% 2d
Check the details:
kubectl describe chaininstance bitcoin-mainnetCheck the managed resources:
# StatefulSet
kubectl get statefulset bitcoin-mainnet
# PVC
kubectl get pvc -l app.kubernetes.io/instance=bitcoin-mainnet
# Services
kubectl get svc -l app.kubernetes.io/instance=bitcoin-mainnet
# ConfigMap
kubectl get configmap bitcoin-mainnetThe operator creates a ClusterIP Service exposing the RPC port. To access it from within the cluster:
# Port-forward for local testing
kubectl port-forward svc/bitcoin-mainnet 8332:8332
# Test the RPC
curl -u rpc:rpc --data-binary \
'{"jsonrpc":"1.0","method":"getblockchaininfo","params":[]}' \
http://localhost:8332/apiVersion: chains.chainplane.io/v1alpha2
kind: ChainInstance
metadata:
name: ethereum-mainnet
spec:
chain: ethereum
network: mainnet
nodeType: rpc
client: nethermind
nodeGroup: heavy
storage:
size: 2Ti
storageClass: fast-ssd
resources:
requests:
cpu: "4"
memory: 16Gi
limits:
cpu: "8"
memory: 32Gi
rpc:
enabled: true
port: 8545
wsPort: 8546
health:
blockLagThreshold: 30apiVersion: chains.chainplane.io/v1alpha2
kind: ChainInstance
metadata:
name: solana-mainnet
spec:
chain: solana
network: mainnet
nodeType: rpc
nodeGroup: heavy
storage:
size: 2Ti
storageClass: fast-ssd
resources:
requests:
cpu: "8"
memory: 64Gi
limits:
cpu: "16"
memory: 128Gi
rpc:
enabled: true
port: 8899All sample manifests are available in config/samples/.
Set replicas: 0 to stop the node pod while preserving the PVC:
kubectl patch chaininstance bitcoin-mainnet --type merge -p '{"spec":{"replicas":0}}'Resume by setting replicas back to 1:
kubectl patch chaininstance bitcoin-mainnet --type merge -p '{"spec":{"replicas":1}}'Override the default image with a custom tag:
kubectl patch chaininstance bitcoin-mainnet --type merge -p '{
"spec": {
"image": {
"repository": "lncm/bitcoind",
"tag": "v29.0"
}
}
}'The operator updates the StatefulSet, which triggers a rolling restart.
kubectl patch chaininstance bitcoin-mainnet --type merge -p '{
"spec": {
"resources": {
"requests": {"cpu": "4", "memory": "8Gi"},
"limits": {"cpu": "8", "memory": "16Gi"}
}
}
}'kubectl patch chaininstance bitcoin-mainnet --type merge -p '{
"spec": {
"extraArgs": ["-maxmempool=300", "-mempoolexpiry=72"]
}
}'kubectl delete chaininstance bitcoin-mainnetThis deletes the StatefulSet, Services, and ConfigMap. The PVC is not deleted automatically -- you must remove it manually if you want to reclaim the storage:
kubectl delete pvc data-bitcoin-mainnet-0To sync from genesis instead of using a MinIO snapshot:
spec:
snapshot:
disabled: trueSymptoms: Phase remains Pending for more than a few minutes.
Check:
- Is the StatefulSet created?
kubectl get statefulset <name> - Is the pod scheduling?
kubectl describe pod <name>-0-- look for events about insufficient resources, missing StorageClass, or unschedulable nodes. - Is the PVC bound?
kubectl get pvc data-<name>-0-- if Pending, the StorageClass may not exist or the volume cannot be provisioned.
Symptoms: Phase is Syncing but block height is not advancing.
Check:
- Check pod logs:
kubectl logs <name>-0 - Check peer count in
kubectl get chaininstances-- zero peers means the node cannot connect to the network. - For chains with long startup (TRON, TON, Cardano), syncing can take hours or days. Check the
ETAcolumn. - The operator has stall detection with StallExempt logic for known slow phases (Ethereum pipeline stages, Stellar bucket apply, TON dump download). If the node is genuinely stalled, it will eventually transition to Degraded.
Symptoms: Phase is Degraded.
Check:
kubectl describe chaininstance <name>-- look at the Conditions section for details.- The node may have fallen behind the chain tip beyond the
blockLagThreshold. - The operator auto-restarts Degraded nodes after
degradedTimeoutMinutes(default 15 min). To disable: setspec.health.degradedTimeoutMinutes: 0. - Check pod logs for RPC errors or crash loops.
Symptoms: Pod is in Init:Error or Init:CrashLoopBackOff.
Check:
- Check init container logs:
kubectl logs <name>-0 -c snapshot-restore - Verify
MINIO_ENDPOINTis set on the operator deployment. - Verify the MinIO bucket
snapshots-<chain>exists and contains the snapshot. - To skip snapshots: set
spec.snapshot.disabled: true.
Check:
- Operator pod is running:
kubectl get pods -n chainplane-system - Operator logs:
kubectl logs -n chainplane-system deploy/chainplane-controller-manager - CRDs are installed:
kubectl get crd chaininstances.chains.chainplane.io
The operator emits Kubernetes events for important lifecycle transitions:
kubectl get events --field-selector involvedObject.name=bitcoin-mainnet