HashiCorp Consul¶
Published: 06 Nov. 2026 Last updated: 06 Nov. 2026
Summary¶
HashiCorp Consul is a service networking platform that provides service discovery, configuration management, and service mesh capabilities. When deployed on Kubernetes using the official Helm chart, Consul server pods maintain a replicated state store using the Raft consensus protocol, with all data persisted to PersistentVolumes.
Consul’s state store holds:
Key/Value (KV) data — application configurations, feature flags, and other structured data stored via the KV API.
Service catalog — registered services, health check definitions, and node information.
Intentions — service-to-service access control rules (connect/deny policies).
ACL tokens and policies — access control lists, roles, and authentication configuration.
Prepared queries and sessions — saved queries and distributed locking sessions.
This Application Note discusses how to use CloudCasa to properly back up and restore HashiCorp Consul running in containers under Kubernetes.
CloudCasa has been tested for this application note with a 3-node Consul server cluster deployed using the official HashiCorp Consul Helm chart. The information herein is expected to apply to more recent versions and to different cluster sizes as well.
Prerequisites¶
The following requirements must be met for a successful backup and restore of Consul with CloudCasa:
Consul server pods must use PersistentVolumes for data storage (
server.storagein the Helm chart). Consul deployments usingemptyDiror in-memory storage will lose all data when pods are restarted and cannot be backed up.The storage class used for Consul server PVs must support CSI volume snapshots. Verify that a
VolumeSnapshotClassexists for your storage provisioner.Consul servers should be deployed as a StatefulSet (this is the default when using the official Helm chart).
If Consul ACLs are enabled, a token with
operator:readpermission is required for the snapshotter.The
consul snapshot savecommand requires connectivity to a Consul server’s HTTP API (default port 8500). Ensure network policies allow the snapshotter pod to reach the Consul server service.
Note
Consul’s consul snapshot save command produces a point-in-time, Raft-consistent snapshot of all server state.
This is the recommended approach because a raw PV-level snapshot may capture the Raft write-ahead log and BoltDB
database in an inconsistent state. CloudCasa uses a pre-backup hook to invoke this command before taking the PV snapshot.
Configure the CloudCasa Consul Snapshotter¶
To perform a consistent backup of Consul, the consul snapshot save command is used to create a snapshot during the backup. CloudCasa requires a running Pod with access to the Consul HTTP API to execute the snapshot command. The following instructions create the Pod that CloudCasa will use.
Note
If Consul ACLs are enabled, the snapshotter requires a token with at minimum the operator:read permission (for non-ACL clusters, no token is needed). The steps below cover both configurations.
If Consul ACLs are enabled, create a snapshot policy and token. Skip this step if ACLs are not enabled:
# Get the Consul bootstrap token CONSUL_HTTP_TOKEN=$(kubectl -n consul get secret consul-bootstrap-acl-token \ -o jsonpath='{.data.token}' | base64 -d) # Create a policy for snapshots kubectl -n consul exec -i consul-server-0 -- sh -c " export CONSUL_HTTP_TOKEN=$CONSUL_HTTP_TOKEN consul acl policy create -name snapshot-policy -rules ' acl = \"read\" key_prefix \"\" { policy = \"read\" } node_prefix \"\" { policy = \"read\" } service_prefix \"\" { policy = \"read\" } session_prefix \"\" { policy = \"read\" } operator = \"read\" ' " # Create a token with this policy kubectl -n consul exec -i consul-server-0 -- sh -c " export CONSUL_HTTP_TOKEN=$CONSUL_HTTP_TOKEN consul acl token create -description 'CloudCasa snapshot token' \ -policy-name snapshot-policy -format=json "
Save the
SecretIDfrom the output. This will be used as the snapshot token.Create the namespace and a secret to hold the Consul snapshot token (if ACLs are enabled):
kubectl create namespace cc-consul-backup # Only if ACLs are enabled — replace <SNAPSHOT_TOKEN> with the SecretID from above kubectl -n cc-consul-backup create secret generic consul-snapshot-token \ --from-literal=token=<SNAPSHOT_TOKEN>
Create a PVC for the Consul snapshotter. Replace
STORAGE_CLASS_NAMEwith the name of your storage class:apiVersion: v1 kind: PersistentVolumeClaim metadata: name: cc-consul-snapshots namespace: cc-consul-backup labels: app: cloudcasa-consul-snapshotter spec: accessModes: [ReadWriteOnce] storageClassName: <STORAGE_CLASS_NAME> resources: requests: storage: 10Gi
Deploy the Consul snapshotter Pod. Adjust the
imageandCONSUL_HTTP_ADDRenvironment variable as necessary for your environment:If ACLs are enabled:
apiVersion: apps/v1 kind: Deployment metadata: name: cloudcasa-consul-snapshotter namespace: cc-consul-backup labels: app: cloudcasa-consul-snapshotter spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: cloudcasa-consul-snapshotter template: metadata: labels: app: cloudcasa-consul-snapshotter spec: containers: - name: snap image: hashicorp/consul:1.20 command: ["sleep", "infinity"] env: - name: CONSUL_HTTP_ADDR value: "http://consul-server.consul.svc.cluster.local:8500" - name: CONSUL_HTTP_TOKEN valueFrom: secretKeyRef: name: consul-snapshot-token key: token resources: requests: cpu: 50m memory: 64Mi limits: memory: 256Mi volumeMounts: - name: snap mountPath: /snapshots volumes: - name: snap persistentVolumeClaim: claimName: cc-consul-snapshots
If ACLs are not enabled:
apiVersion: apps/v1 kind: Deployment metadata: name: cloudcasa-consul-snapshotter namespace: cc-consul-backup labels: app: cloudcasa-consul-snapshotter spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: cloudcasa-consul-snapshotter template: metadata: labels: app: cloudcasa-consul-snapshotter spec: containers: - name: snap image: hashicorp/consul:1.20 command: ["sleep", "infinity"] env: - name: CONSUL_HTTP_ADDR value: "http://consul-server.consul.svc.cluster.local:8500" resources: requests: cpu: 50m memory: 64Mi limits: memory: 256Mi volumeMounts: - name: snap mountPath: /snapshots volumes: - name: snap persistentVolumeClaim: claimName: cc-consul-snapshots
Verify that the snapshot command works from within the Consul snapshotter Pod:
Open a shell in the Pod:
kubectl -n cc-consul-backup exec -it deploy/cloudcasa-consul-snapshotter -- sh
Create a test snapshot:
consul snapshot save /snapshots/consul-test.snap
Verify the snapshot:
consul snapshot inspect /snapshots/consul-test.snap
The output should show a valid snapshot with a non-zero index and the Raft term.
Delete the test snapshot:
rm -f /snapshots/consul-test.snap
Backup¶
The following instructions can be used to configure backup of HashiCorp Consul using the cloudcasa-consul-snapshotter.
From the CloudCasa UI, navigate to Configuration → App Hooks and click the “Add app hook” button.
Specify a name for the App Hook and select “Pre-backup” as the type. Set the pod selector to
app = cloudcasa-consul-snapshotterand the container tosnap. Add the following into the command field and then click “Save”:/bin/sh -c "consul snapshot save /snapshots/consul.snap && consul snapshot inspect /snapshots/consul.snap"
No post-backup hook is needed. The
consul snapshot savecommand is an atomic Raft operation that completes instantly and does not require any cleanup.Navigate to Clusters → Overview, select the cluster containing the Consul deployment, and then click “Define backup”.
In the App hooks section of the backup definition, add a pre-backup app hook. Select the
cc-consul-backupnamespace and select the App Hook created previously.Run a full cluster backup that includes both the
cc-consul-backupnamespace and theconsulnamespace. Enable the “Include all cluster-scoped resources” switch to ensure all CRDs are included.Run the backup and confirm the job completes.
Restore¶
Restoring Consul is a two-part operation. CloudCasa restores the Consul snapshot file from the snapshotter PVC. A Consul cluster is then deployed and the snapshot is loaded into it using the consul snapshot restore command.
Note
The consul snapshot restore command must be run against a running Consul cluster with an elected leader. The snapshot replaces all state in the target cluster.
Navigate to Clusters → Recovery Points, find the recovery point you want to restore from, and then click Actions → Restore.
Enable “Select namespaces” and select the
cc-consul-backupnamespace.Note
The namespace containing the Consul servers (typically
consul) does not need to be selected. The snapshot file produced by the App Hook is the recoverable copy of all Consul state. Restoring theconsulnamespace PVs directly is not recommended, as the Raft log on PVs may be in an inconsistent state.In the Destination section, select the target cluster for the restore.
Run the restore job and verify that the
cloudcasa-consul-snapshotterPod and its PVC were restored.Deploy a fresh Consul cluster on the target if one is not already running. Use the official Helm chart:
helm install consul hashicorp/consul \ --namespace consul --create-namespace \ --set global.name=consul \ --set server.replicas=3 \ --set server.storage=10Gi
Wait for the Consul cluster to be ready:
kubectl wait --for=condition=Ready pod -l app=consul,component=server \ -n consul --timeout=300s
Copy the snapshot from the restored snapshotter Pod and restore it into the new cluster:
# Copy the snapshot out POD=$(kubectl -n cc-consul-backup get pod -l app=cloudcasa-consul-snapshotter -o name) kubectl -n cc-consul-backup cp ${POD#pod/}:/snapshots/consul.snap ./consul.snap # Copy into a Consul server pod and restore kubectl -n consul cp ./consul.snap consul-server-0:/tmp/consul.snap kubectl -n consul exec consul-server-0 -- consul snapshot restore /tmp/consul.snap
Note
If ACLs are enabled on the target cluster, provide the bootstrap token when running the restore:
kubectl -n consul exec consul-server-0 -- sh -c \ "CONSUL_HTTP_TOKEN=<BOOTSTRAP_TOKEN> consul snapshot restore /tmp/consul.snap"
Verify the restored data. Confirm that KV entries, service registrations, and other state are present:
# Verify KV data kubectl -n consul exec consul-server-0 -- consul kv get -recurse # Verify service catalog kubectl -n consul exec consul-server-0 -- consul catalog services # Verify ACL tokens (if ACLs are enabled) kubectl -n consul exec consul-server-0 -- sh -c \ "CONSUL_HTTP_TOKEN=<BOOTSTRAP_TOKEN> consul acl token list"
What Gets Backed Up¶
The consul snapshot save command captures a point-in-time, Raft-consistent snapshot of all Consul server state, including:
Data Type |
Captured |
|---|---|
KV store entries |
Yes — all key/value pairs across all namespaces |
Service registrations (catalog) |
Yes — services registered via the catalog API |
Service intentions |
Yes — all connect authorize rules |
ACL tokens, policies, and roles |
Yes |
Prepared queries |
Yes |
Sessions |
Yes |
Raft configuration |
Yes — peer set and cluster metadata |
Agent-local service registrations |
No — services registered only via the local agent API on client pods are not part of the server snapshot. These are typically re-registered by the application on startup. |
Note
Consul Connect sidecar proxy configurations are typically managed by the connect-injector and are re-created when application pods restart. They do not need to be explicitly backed up.
See also
For more information on defining a restore see Cluster Restore Wizard.
For information on Consul snapshots, see Consul Snapshot.
For information on Consul architecture, see Consul Architecture.