Bootstrapping with an Existing etcd Cluster ​
This guide shows how to create a new etcd-druid managed Etcd resource that joins an already-running etcd cluster instead of starting as a standalone cluster.
The source cluster may be managed by etcd-druid or externally managed. In both cases, the source member names, peer URLs, and client endpoints that you put into the target spec must already be valid from the target's network.
For background and lifecycle semantics, see Bootstrap with an Existing etcd Cluster. This guide uses "source cluster", "target Etcd resource", and "join phase" as defined in that document's Terminology section.
Prerequisites ​
Before creating the target Etcd resource, ensure:
- The source cluster is healthy and reachable from the target's network. For each source member you list in
.spec.etcd.bootstrapWithExistingCluster.members, its peer URL must be dialable from the target pods, and the member must be responsive.etcdctl member list --write-out=tablereportingstartedfor the member is necessary but not sufficient — confirm the member also responds toetcdctl endpoint healthbefore you list it. - The source peer URLs in the target spec are reachable from the target pods. Only the URLs listed under
.spec.etcd.bootstrapWithExistingCluster.members[*].peerUrlsneed to be reachable from the target network. - At least one source client endpoint is reachable from the target backup-restore sidecar. These endpoints go into
.spec.etcd.bootstrapWithExistingCluster.clientEndpoints. - The source and target use compatible TLS trust roots. The target's peer/client TLS configuration is used when dialing source peer/client endpoints. See the TLS trust model.
- The target
Etcdresource does not already exist.bootstrapWithExistingClusteris create-only and cannot be added to an existingEtcdresource. - Source and target run the same etcd minor version. The etcd version
etcd-druiddeploys on the target depends on theUpgradeEtcdVersionfeature gate:- With
UpgradeEtcdVersionenabled,etcd-druiddeploys etcd3.5.xon the target; the source must also be on3.5.x. - With
UpgradeEtcdVersiondisabled (current default),etcd-druiddeploys etcd3.4.xon the target; the source must also be on3.4.x.
- With
Step 1: Inspect the source cluster ​
Run etcdctl member list against the source cluster from a location that can reach its client endpoint:
etcdctl --endpoints=<source-client-endpoint> \
--cacert=<source-ca-path> \
--cert=<source-client-cert-path> \
--key=<source-client-key-path> \
member list -w tableIf the source cluster does not use TLS, omit the TLS flags.
Example output:
+------------------+---------+---------------+-----------------------------------------------------------------------------------+---------------------------------------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+---------------+-----------------------------------------------------------------------------------+---------------------------------------------------------+
| abc... | started | etcd-source-0 | https://etcd-source-0.etcd-source-peer.source-ns.svc:2380,https://10.0.0.1:2380 | https://etcd-source-client.source-ns.svc:2379 |
| def... | started | etcd-source-1 | https://etcd-source-1.etcd-source-peer.source-ns.svc:2380,https://10.0.0.2:2380 | https://etcd-source-client.source-ns.svc:2379 |
| 012... | started | etcd-source-2 | https://etcd-source-2.etcd-source-peer.source-ns.svc:2380,https://10.0.0.3:2380 | https://etcd-source-client.source-ns.svc:2379 |
+------------------+---------+---------------+-----------------------------------------------------------------------------------+---------------------------------------------------------+Record:
- each source member
NAME, - the source peer URLs that are reachable from the target pods,
- one or more source client endpoints reachable from the target backup-restore sidecar.
IMPORTANT
The PEER ADDRS shown above are same-network examples. If source and target run in different network domains, replace them with peer URLs that the source cluster advertises and the target can dial.
For an etcd-druid managed source, configure .spec.etcd.additionalAdvertisePeerURLs on the source if extra reachable peer URLs are needed. For an externally managed source, prepare the advertised peer URLs outside etcd-druid. The target spec must use the source member names and peer URLs as advertised by the source member list.
Step 2: Create the target Etcd manifest ​
Add spec.etcd.bootstrapWithExistingCluster when creating the target resource:
apiVersion: druid.gardener.cloud/v1alpha1
kind: Etcd
metadata:
name: etcd-target
namespace: target-ns
spec:
# replicas is the target-side member count only. The joined cluster ends up with
# (replicas + number of source members listed below) etcd members in total.
replicas: 3
etcd:
image: <etcd-wrapper-image>
bootstrapWithExistingCluster:
members:
- name: etcd-source-0
peerUrls:
- https://etcd-source-0.etcd-source-peer.source-ns.svc:2380
- https://10.0.0.1:2380
- name: etcd-source-1
peerUrls:
- https://etcd-source-1.etcd-source-peer.source-ns.svc:2380
- https://10.0.0.2:2380
- name: etcd-source-2
peerUrls:
- https://etcd-source-2.etcd-source-peer.source-ns.svc:2380
- https://10.0.0.3:2380
clientEndpoints:
- https://etcd-source-client.source-ns.svc:2379
peerUrlTls:
tlsCASecretRef:
name: ca-etcd-peer-combined
namespace: target-ns
serverTLSSecretRef:
name: etcd-target-server
namespace: target-ns
clientUrlTls:
tlsCASecretRef:
name: ca-etcd-client-combined
namespace: target-ns
serverTLSSecretRef:
name: etcd-target-server-client
namespace: target-ns
clientTLSSecretRef:
name: etcd-target-client
namespace: target-ns
backup: {}
# ... other fields as neededUse http:// URLs when TLS is not enabled for the corresponding peer/client side. Admission validation rejects URL schemes that do not match .spec.etcd.peerUrlTls or .spec.etcd.clientUrlTls.
Step 3: Apply the manifest ​
kubectl apply -f etcd-target.yamletcd-druid then reconciles the target resources:
- the target ConfigMap
initial-clusterincludes both target members and source members, - the target backup-restore
--service-endpointsincludes the target client service and the configured sourceclientEndpoints, - the target pods start and join the existing source cluster.
Step 4: Watch join progress ​
Check the dedicated condition:
kubectl -n target-ns get etcd etcd-target \
-o jsonpath='{.status.conditions[?(@.type=="BootstrappedWithExistingCluster")]}{"\n"}'While the join is still in progress, the condition is False:
{"type":"BootstrappedWithExistingCluster","status":"False","reason":"BootstrapInProgress","message":"Not all members have joined the cluster yet"}During this phase .status.bootstrapWithExistingCluster is absent. To inspect per-target-member readiness, use .status.members.
After the join succeeds, the condition becomes True:
{"type":"BootstrappedWithExistingCluster","status":"True","reason":"BootstrapSucceeded","message":"All members have successfully joined the existing cluster"}The success condition is sticky. Once true, pod restarts or transient readiness changes do not reset the historical bootstrap result.
Step 5: Verify the combined cluster ​
The etcd-wrapper container is distroless and does not ship a shell or etcdctl. Use an ephemeral debug container with etcdctl instead of copying TLS secrets to your workstation:
kubectl -n target-ns debug -it pod/etcd-target-0 \
--image=europe-docker.pkg.dev/sap-se-gcp-k8s-delivery/releases-public/europe-docker_pkg_dev/gardener-project/releases/gardener/ops-toolbelt:latestList the combined membership:
etcdctl member list -w tableExpected: source members and target members are present, and all are started.
Verify that source and target endpoints report the same cluster ID:
etcdctl endpoint status \
--endpoints=<source-client-endpoint>,https://localhost:2379 \
-w tableThe CLUSTER ID column must match for source and target endpoints. If the IDs differ, the target formed or contacted a different cluster instead of joining the source cluster.
NOTE
If ephemeral debug containers are not allowed in your environment, use a short-lived pod in the same namespace with etcdctl and mount the required client TLS secrets as volumes. Avoid copying certificate/key material to a workstation.
Step 6: Inspect recorded source-member status ​
After the join succeeds, etcd-druid writes the source-member inventory:
kubectl -n target-ns get etcd etcd-target \
-o jsonpath='{.status.bootstrapWithExistingCluster}{"\n"}'Example:
{
"joinedAt": "2026-06-04T10:11:00Z",
"members": [
{"name":"etcd-source-0","peerUrls":["https://etcd-source-0.etcd-source-peer.source-ns.svc:2380","https://10.0.0.1:2380"]},
{"name":"etcd-source-1","peerUrls":["https://etcd-source-1.etcd-source-peer.source-ns.svc:2380","https://10.0.0.2:2380"]},
{"name":"etcd-source-2","peerUrls":["https://etcd-source-2.etcd-source-peer.source-ns.svc:2380","https://10.0.0.3:2380"]}
]
}This status records source members that were present when the target completed bootstrap. joinedAt is a single timestamp for the whole snapshot — the moment etcd-druid recorded the successful bootstrap.