Backup & restore
This guide goes into detail about how to backup and restore your OpsChain resources, how OpsChain is designed to facilitate backups and how you can use a backup tool to backup and restore your OpsChain resources.
Introduction
OpsChain is designed to be backup and restore friendly, ensuring you can easily export and import your OpsChain instance in different environments. Most of the settings and configurations used by OpsChain reside encrypted within its database, making it the most important resource to back up on a regular basis.
There are two things to back up: your deployment configuration (the values.yaml file) and the OpsChain database. OpsChain can back the database up for you on a schedule. A database backup comes in one of two forms — a physical backup (a complete copy of the database files) or a logical backup (a portable export of its contents) — each suited to a different situation, as explained below. To restore an instance you need both the values.yaml file and a database backup.
Backup
Configuration file
Make sure you have a backup of the values.yaml file used to deploy OpsChain, ideally one by deployment date, such that you can restore to a specific version of OpsChain. This file contains the exact configuration for OpsChain resources and services, such as its database, vault, LDAP server, etc., as well as the encryption secrets that were used to encrypt all your sensitive data. Without this file, restoring your OpsChain instance will not be possible.
Keys to keep with your backups
A database backup can only be restored with the encryption keys and passwords the installation used. Keep these values from your values.yaml with your backups, stored separately from them.
You also need the backup private key (opschain-backup-key) to decrypt a backup. Keep it offline, apart from the cluster and the backups. Its public key is the db.backup.encryption.publicKey value in your values.yaml.
Database
OpsChain can back up its database automatically — see automated backups below to enable and configure scheduled and pre-deploy backups. A few things to keep in mind:
- The backup must always be performed on the primary database cluster - if you're not running in a high availability setup, this is the only cluster you have. Backing up from replica clusters will create unusable backups.
- The backup should ideally be performed when no changes, workflows or agents are running. If any of these are running, it's possible that they will be in an inconsistent state when restoring from the backup.
- When in a high availability setup, replica database clusters will need to be recreated after the backup is restored.
Backing up the database is limited to the data that is stored in the database. While this includes change and step states, it will not include the actual long-running agents, change deployments or their respective resources.
Automated backups
OpsChain can run scheduled database backups for you, and can also take a backup automatically before each upgrade. These backups run inside the cluster using a backup utility built into the OpsChain database image, so your data never leaves your local network.
Automated backups only run on the primary database cluster. In a high availability setup, backup resources are not created on replica clusters, and the backup utility refuses to run if it detects the target database is a replica.
Configure automated backups
Automated backups are controlled by the db.backup section of your values.yaml. Scheduled backups are enabled by default, and need a backup encryption key:
db:
backup:
encryption:
publicKey: ssh-ed25519 AAAA... opschain-backup
periodic:
enabled: true
schedule: '0 2 * * *'
Backups are written to a dedicated opschain-db-backup persistent volume claim. Two backup methods are available:
physical— apg_basebackupof the database, including the write-ahead log (WAL) written while it runs. This is the recommended method for scheduled backups and is restored using the database recovery steps.logical— apg_dump(custom format) export. This is portable across database versions and is recommended for the pre-deploy backup, as it can be restored even after an upgrade changes the database schema or major version.
A physical backup is limited to the transfer rate set in db.backup.maxRate (100M by default).
A physical backup fails if the database writes more WAL during the backup than the server keeps (wal_keep_size). The job then reports an error and does not save the backup. A lower db.backup.maxRate makes the backup slower, so this is more likely on a busy database.
Use physical backups for your regular scheduled backups and for disaster recovery — they are a complete, fast-to-restore copy of the database. Use a logical backup around upgrades — it can be restored into a newer PostgreSQL major version if needed. Keeping both a recent physical backup and a pre-upgrade logical backup covers the common cases.
With the default local storage, the backup volume lives on a single node and shares a failure domain with that node. Copy your backups to a safe location off the host — for example a file server on your local network — regularly. The backup volume should be treated as a staging area, not as your only copy. See the copying backups off the host section for available options.
Backup encryption
Backups are compressed with zstd and encrypted with age. OpsChain holds only the public key, so it can write backups but cannot read them. While scheduled or pre-deploy backups are enabled, you must set db.backup.encryption.publicKey to install or upgrade OpsChain.
Generate the key pair with ssh-keygen on a machine outside the cluster:
ssh-keygen -t ed25519 -N "" -C opschain-backup -f opschain-backup-key
This creates the private key opschain-backup-key and the public key opschain-backup-key.pub. The key has no passphrase so the restore steps can read it without a prompt. Keep the file protected. Set publicKey to the contents of opschain-backup-key.pub in your values.yaml:
db:
backup:
encryption:
publicKey: ssh-ed25519 AAAA... opschain-backup
opschain-backup-key is the private key. Keep it offline and separate from your backups. Do not copy it into the cluster. You need it to restore any backup.
In a high availability setup, set the same public key on every cluster. Any cluster can become the primary, and the primary takes the backups.
To change the key, generate a new key pair and set the new public key on every cluster. New backups use the new key. Keep the old private key for as long as you keep backups encrypted with it.
Backup reliability
Each backup is written to a temporary location and checked while it is written. It is only saved — and counted towards retention — once it has completed and passed that check, so an interrupted or corrupt backup is never mistaken for a good one and never displaces a valid backup. A failed check fails the job.
Each completed backup directory contains:
- a physical backup:
base.tar.zst.age, holding the base backup, its WAL and itsbackup_manifest - a logical backup:
globals.sql.ageandopschain.dump.age - either method:
SHA256SUMSand theopschain-backup.jsonmarker. Directories without the marker are considered incomplete.
Retention (db.backup.retention.count and days) is applied to completed backups only, and to each trigger independently. Size the volume (db.backup.storage.size) for roughly your largest expected database multiplied by the retention count, with headroom — with local-path/hostPath storage the volume cannot be resized in place, so err on the generous side. This doesn't apply if you're using Longhorn instead: its storage class supports volume expansion, so an undersized volume can be grown later rather than requiring a fresh one.
The opschain-db-backup volume is preserved when a cluster changes role (primary to replica, or back) during failover or failback, and is intentionally retained when OpsChain is uninstalled — see persistent data. Only the primary cluster runs backups, but the volume and its history stay in place so they remain available after a failover.
Copying backups off the host
In the host filesystem, create a directory to store the backups. Because the K3s data directory /limepoint is root-owned, create this directory once as root and assign it to the opschain group created during the installation process, so the unprivileged installation user can write backups into it without sudo (the setgid bit keeps new files in the opschain group):
mkdir -p /limepoint/backups
chown root:opschain /limepoint/backups
chmod 2775 /limepoint/backups
To extract the backups from the PVC, you can either copy it directly from the volume or use the recovery helper pod. Which one applies depends on your storage class — see the note after each.
-
Copying directly from the volume: only works with K3s's default
local-pathstorage, where the PV is a plain directory on the host. For those, this is the most efficient option and is easy to schedule (for example a cron job that copies to a file server on your local network).Privileged access requiredYou must run the commands below as sudo or root user. The volume's directory and the backup files in it are readable only by root and the database's own user.
The last command gives the
opschaingroup read access to the copies, so the installation user can use them later, for example to copy a backup into the recovery helper pod:VOL=$(kubectl -n ${KUBERNETES_NAMESPACE} get pvc opschain-db-backup -o jsonpath='{.spec.volumeName}')DIR=$(kubectl get pv "$VOL" -o jsonpath='{.spec.local.path}')cp -r "${DIR:?The backup volume is not a local directory, use the recovery helper pod instead}"/. /limepoint/backups/chmod -R g+rX /limepoint/backupsDoes not work under Longhorn, or any other non-host-path storage classA Longhorn-backed PV has no
.spec.local.pathat all, soDIRcomes back empty and the command silently becomescp -r "/"/* /limepoint/backups/— it copies the node's root filesystem rather than failing outright. If you're on Longhorn (or any storage class that doesn't expose the PV as a host directory), use the recovery helper pod below instead. -
Using the recovery helper pod, which mounts the backup volume read-only. This works with any storage class, including Longhorn. Start the pod, copy the backups out of it, then stop it again:
kubectl scale deployment opschain-db-recovery --replicas=1 -n ${KUBERNETES_NAMESPACE}kubectl rollout status deployment opschain-db-recovery -n ${KUBERNETES_NAMESPACE}POD=$(kubectl -n ${KUBERNETES_NAMESPACE} get pod -l app=opschain-db-recovery -o jsonpath='{.items[0].metadata.name}')kubectl cp ${KUBERNETES_NAMESPACE}/$POD:/opt/backup /limepoint/backups/kubectl scale deployment opschain-db-recovery --replicas=0 -n ${KUBERNETES_NAMESPACE}
To check that a copy is intact, run this command in its backup directory. You do not need the private key:
sha256sum -c SHA256SUMS
Scheduled backups
When db.backup.periodic.enabled is true, a CronJob named opschain-db-backup runs on the configured schedule (a standard cron expression). Older backups are pruned according to db.backup.retention.
To pause the schedule without redeploying:
kubectl patch cronjob opschain-db-backup -p '{"spec":{"suspend":true}}' -n ${KUBERNETES_NAMESPACE}
To resume it:
kubectl patch cronjob opschain-db-backup -p '{"spec":{"suspend":false}}' -n ${KUBERNETES_NAMESPACE}
The next deploy sets the schedule back to what db.backup.periodic.enabled says.
You can trigger a backup at any time without waiting for the schedule, including while it is paused. The $(date +%Y%m%d-%H%M%S) suffix gives each run a unique name so repeated runs never clash:
kubectl create job --from=cronjob/opschain-db-backup opschain-db-backup-manual-$(date +%Y%m%d-%H%M%S) -n ${KUBERNETES_NAMESPACE}
A manual job counts towards the CronJob's job history, so later runs remove it like any other finished job. To remove it sooner, delete it by name:
kubectl delete job opschain-db-backup-manual-<suffix> -n ${KUBERNETES_NAMESPACE}
Scheduled backups run while OpsChain is online and do not stop the API or workers. The database backup itself is consistent, but any changes, workflows or agents that are mid-execution may be captured in an inconsistent state and could resume inconsistently after a restore. To avoid that possibility, scale the API and workers down first, take the backup, then scale them back up:
kubectl scale deployment opschain-api opschain-api-worker --replicas=0 -n ${KUBERNETES_NAMESPACE}
kubectl scale deployment opschain-api opschain-api-worker --replicas=1 -n ${KUBERNETES_NAMESPACE}
If you set api.replicas or apiWorker.replicas in your values.yaml, scale each back to that number instead.
Pre-deploy backups
Set db.backup.preDeploy.enabled to true to take a backup automatically before every helm upgrade. It is skipped while the database is stopped. This runs as a Helm pre-upgrade hook: if the backup fails, the upgrade is aborted, so you always have a fresh backup before changes are applied. A logical backup is used by default so that it remains restorable across database version upgrades.
The pre-deploy backup writes to the same opschain-db-backup volume, which must already exist. Enable scheduled backups (or deploy once with backups enabled) before turning on the pre-deploy backup.
Kubernetes resources (optional)
Besides the database, you can also opt to use a Kubernetes backup tool to backup and restore OpsChain's Kubernetes resources, such as its deployments, services, etc. Kubernetes backup options include Velero, Kasten K10 and Portworx. These tools are outside the scope of this guide and you should refer to their documentation for more information on how to use them.
Creating a snapshot of your OpsChain resources
Prior to backing up your resources, we recommend stopping OpsChain. See the stopping and starting guide for more information on how to stop OpsChain.
It is recommended that you backup the entire opschain namespace so that in an unlikely event of a failure, you can get OpsChain up and running after the recovery and restore process. Using the backup tool of your choice, make a snapshot of the opschain resources. In addition to the resources in the opschain namespace, the OpsChain persistent volumes that fulfil the persistent volume claims (in the opschain namespace) need to be backed up as well (e.g. database, Git repos, LDAP, step data). Once the snapshot has been created, you can restart OpsChain.
Recovery
Restoring an instance has two parts: redeploy with the backed-up values.yaml file, then restore the database from a backup. Follow the path that matches the backup you hold — a physical restore (scheduled backups use the physical method by default) or a logical restore (pre-deploy backups use the logical method by default). The opschain-backup.json marker in each backup directory records the backup method. In a high availability setup, also review the extra requirements for high availability clusters.
Configuration file
Recover the values.yaml file for the version of OpsChain you're recovering from and use it to redeploy OpsChain. Copy it to the OpsChain's host filesystem and keep a note of its location to use it in the next steps.
Database recovery
This section restores a physical backup (a pg_basebackup) by replacing the database's data files. For a logical backup (a pg_dump, such as a pre-deploy backup), follow restoring a logical backup instead.
To restore OpsChain from a database backup, first uninstall it as described in the uninstall guide. Uninstalling keeps the opschain-db-backup volume, so its backups stay available to the restore. If you also delete the namespace, copy your backups off the host first.
Set db.backup.preDeploy.enabled to false in your values.yaml before starting a recovery.
If you're restoring from a backup in a high availability setup, check the extra requirements for high availability clusters when restoring from a backup.
Then, do a regular installation of OpsChain as described in the installation guide, but with the additional --set stopped=true and --set stopDatabase=true flags in the Helm command, as described in the deploy OpsChain in stopped mode and hibernating the database sections.
--wait while OpsChain is stoppedRemove the --wait flag from the Helm command for every deploy in this guide that uses --set stopped=true. While OpsChain is stopped, the volumes of its stopped components are never attached and the secret vault cannot become ready, so Helm waits for the full --timeout and then marks the release as failed. Follow progress with kubectl get pods -n ${KUBERNETES_NAMESPACE} instead. Once OpsChain is started again, use --wait as usual.
You may try to restore the backup to a newer version of OpsChain, but beware of breaking changes between the versions. It's recommended to first restore the backup to the same version of OpsChain as the backup was created with and then upgrade to the latest version. The backup's opschain-backup.json records that version as opschain_version.
The steps below use a helper pod, opschain-db-recovery, that mounts your backup volume read-only at /opt/backup. It is not running by default, and each restore below shows how to start it. If you are recovering into a fresh namespace where the backup volume is empty, copy your off-host backup into the pod first (shown below), then follow the same extract steps.
The helper pod includes age and zstd. During the restore, you paste the backup private key into its shell. The key is never stored in the cluster.
While the database is hibernated, the OpsChain secret vault cannot reach the database, so it logs errors and may restart. This is expected, and it recovers once the database is running again.
Step 1: Ensure the database pod has been initialised
Right after deploying OpsChain in stopped mode and with a hibernated database, a few pods will be created to initialise the database and will disappear once the initialisation is complete. You can check the status of the pods by running the following command:
kubectl get pods -n ${KUBERNETES_NAMESPACE}
If the database pod is not present in the list and there are no initdb pods present in the list, it's likely that the database has already been initialised and you can continue with the next step.
If you encounter errors or are unsure of the database status, you can always delete the database cluster and redeploy OpsChain, this will recreate and initialise the database again.
Step 2: Start the recovery helper pod
Now that the data volume exists, start the recovery helper pod with the database's data volume mounted. In your values.yaml set:
db:
backup:
recovery:
enabled: true
mountDataVolume: true
Then redeploy with --set stopped=true --set stopDatabase=true, keeping the database stopped and hibernated.
Wait for the pod to start, then exec into it:
kubectl rollout status deployment opschain-db-recovery -n ${KUBERNETES_NAMESPACE}
kubectl exec -it deploy/opschain-db-recovery -n ${KUBERNETES_NAMESPACE} -- bash
Your backups are available read-only under /opt/backup. List the physical backups and choose one to restore:
ls -lah /opt/backup/periodic
Each physical backup directory contains base.tar.zst.age. If a backup directory has no such file, see backups taken before encryption.
If the backup volume is empty — for example when recovering into a fresh namespace — copy your off-host backup into the pod from another terminal session instead, then use that path in the next step:
POD=$(kubectl get pod -n ${KUBERNETES_NAMESPACE} -l app=opschain-db-recovery -o jsonpath='{.items[0].metadata.name}')
for file in $(find /path/to/your/backup/20210101_120000 -type f); do
kubectl cp $file ${KUBERNETES_NAMESPACE}/${POD}:/tmp/$(basename $file)
done
Then check the copy in the recovery pod shell. Every file should report OK:
cd /tmp && sha256sum -c SHA256SUMS
Step 3: Recover the database files
In the recovery pod shell, set a variable to the backup you chose (a directory under /opt/backup/periodic, or /tmp if you copied one in):
BACKUP_DIR=/opt/backup/periodic/20210101_120000
Clean up the existing PostgreSQL files in the database directory:
rm -rf /var/lib/postgresql/data/pgdata/*
Load the backup private key into the shell. Run the command below and paste the whole contents of opschain-backup-key. The pasted text is not shown. Then press Enter and Ctrl-D:
stty -echo; BACKUP_KEY=$(cat); stty echo
Decrypt and extract the backup:
age -d -i <(printf '%s\n' "$BACKUP_KEY") "$BACKUP_DIR/base.tar.zst.age" | zstd -dc | tar -x -C /var/lib/postgresql/data/pgdata/
Then remove the key from the shell:
unset BACKUP_KEY
The recovery pod runs as the postgres user (UID 26), so the extracted files already have the correct ownership.
Step 4: Verify the database recovery
Check the extracted files against the backup's manifest:
pg_verifybackup /var/lib/postgresql/data/pgdata
This should print backup successfully verified. If it reports errors, restart from step 3, or choose another backup.
Exit the recovery pod shell. Step 5 removes the pod.
Step 5: Release the data volume and resume the database
The recovery pod holds the database's data volume, so it must be released before the database can start. In your values.yaml turn the recovery helper back off:
db:
backup:
recovery:
enabled: false
mountDataVolume: false
Then redeploy without the --set stopDatabase=true flag (so the database resumes), keeping --set stopped=true for now so you can verify the database before the application starts.
Wait for the database to resume. Follow the creation and the logs of the database pod to ensure it successfully loaded the backup. You can follow the logs by running the following command:
kubectl logs -f pod/<cluster-name>-1 -n ${KUBERNETES_NAMESPACE}
Where <cluster-name> is your db.cnpg.clusterName (opschain-db by default).
If the database pod runs normally for a few minutes and the logs show no errors, it's likely that the database recovery was successful.
Step 6: Start OpsChain
You can now start OpsChain by redeploying without the --set stopped=true flag. Follow the API and worker logs to ensure they start successfully.
In a high availability setup, reset sites and work on this deploy.
If you're using OpsChain's internal LDAP server, you'll need to recreate the users and groups in that server before you can sign in to OpsChain. Refer back to the creating your first user guide on how to create LDAP users again.
Step 7: Clean up
The recovery pod was disabled in step 5, so there is nothing further to remove. Do not delete the backups from the opschain-db-backup volume — they remain your restore point. If you copied a backup into the pod's /tmp for the recovery, it was discarded automatically when the pod was removed.
Restoring a logical backup
A logical backup — such as a pre-deploy backup — is a pg_dump export that is loaded into a running database, rather than by replacing the data directory. Unlike a physical restore, you do not need to hibernate the database — it must be running to accept the restore.
Prepare as described at the start of database recovery: uninstall OpsChain, set db.backup.preDeploy.enabled to false, and leave out --wait while OpsChain is stopped. If you can, restore to the OpsChain version the backup was taken with. Skip --set stopDatabase=true and the recovery helper's mountDataVolume. Only a physical restore needs them.
A logical restore must target a freshly initialised, empty opschain database. Restoring on top of a database that OpsChain has already populated is unreliable. In practice, this means starting from a clean cluster: deploy into an empty namespace (or delete and recreate the database cluster), and keep OpsChain stopped so the application does not create its schema before you restore.
Step 1: Prepare an empty database and start the recovery helper
Deploy with the application stopped using the --set stopped=true flag, without the --wait flag, as Helm cannot finish waiting while OpsChain is stopped.
This starts the database but keeps the API and workers scaled to zero, so CloudNativePG bootstraps an empty opschain database. If the cluster already contains data, first remove it (see the uninstall guide) and redeploy, so a fresh, empty database is created.
Wait for the database to be ready, where <cluster-name> is your db.cnpg.clusterName (opschain-db by default):
kubectl wait --for=condition=Ready cluster/<cluster-name> -n ${KUBERNETES_NAMESPACE} --timeout=15m
Then start the recovery helper pod:
kubectl scale deployment opschain-db-recovery --replicas=1 -n ${KUBERNETES_NAMESPACE}
kubectl rollout status deployment opschain-db-recovery -n ${KUBERNETES_NAMESPACE}
Step 2: Restore the dump
Exec into the recovery helper — its environment is already configured to connect to the database (host, credentials and TLS):
kubectl exec -it deploy/opschain-db-recovery -n ${KUBERNETES_NAMESPACE} -- bash
Your backups are available read-only under /opt/backup. Pre-deploy backups are in /opt/backup/pre-deploy, and scheduled logical backups are in /opt/backup/periodic. Each logical backup directory contains globals.sql.age and opschain.dump.age.
If the backup volume is empty, for example in a fresh namespace, copy your off-host backup into the pod from another terminal session. Then use /tmp as the backup directory below:
POD=$(kubectl get pod -n ${KUBERNETES_NAMESPACE} -l app=opschain-db-recovery -o jsonpath='{.items[0].metadata.name}')
for file in $(find /path/to/your/backup/20210101_120000 -type f); do
kubectl cp $file ${KUBERNETES_NAMESPACE}/${POD}:/tmp/$(basename $file)
done
Then check the copy in the recovery pod shell. Every file should report OK:
cd /tmp && sha256sum -c SHA256SUMS
Choose the dump to restore and load the backup private key into the shell. Run the command below and paste the whole contents of opschain-backup-key. The pasted text is not shown. Then press Enter and Ctrl-D:
BACKUP_DIR=/opt/backup/pre-deploy/20210101_120000
stty -echo; BACKUP_KEY=$(cat); stty echo
Restore the global objects (roles), then the database. Errors about roles that already exist can be ignored:
age -d -i <(printf '%s\n' "$BACKUP_KEY") "$BACKUP_DIR/globals.sql.age" | psql -d postgres
age -d -i <(printf '%s\n' "$BACKUP_KEY") "$BACKUP_DIR/opschain.dump.age" | pg_restore -d opschain --clean --if-exists --no-owner
The restore runs in a single process.
Then remove the key from the shell:
unset BACKUP_KEY
Step 3: Start OpsChain
If you use the OpsChain secret vault, restart its pods so they read the restored database:
kubectl delete pod -l app.kubernetes.io/name=openbao -n ${KUBERNETES_NAMESPACE}
Then redeploy without the --set stopped=true flag to start the application against the restored database. The deploy also stops the recovery helper pod.
In a high availability setup, reset sites and work on this deploy.
Testing a logical backup (non-destructive)
To confirm a dump is complete and restorable without touching any database, decode it inside the recovery helper. Start the helper and open a shell in it:
kubectl scale deployment opschain-db-recovery --replicas=1 -n ${KUBERNETES_NAMESPACE}
kubectl rollout status deployment opschain-db-recovery -n ${KUBERNETES_NAMESPACE}
kubectl exec -it deploy/opschain-db-recovery -n ${KUBERNETES_NAMESPACE} -- bash
pg_restore -f reads every object in the archive. Load the key into BACKUP_KEY as above, then run:
BACKUP_DIR=/opt/backup/pre-deploy/20210101_120000
# list the archive's contents
age -d -i <(printf '%s\n' "$BACKUP_KEY") "$BACKUP_DIR/opschain.dump.age" | pg_restore -l | head
age -d -i <(printf '%s\n' "$BACKUP_KEY") "$BACKUP_DIR/opschain.dump.age" | pg_restore -f /dev/null && echo 'archive fully decodes'
unset BACKUP_KEY
If it completes without error the dump is intact and restorable. This is the safest way to routinely verify your backups. To check a copy without the private key, use sha256sum -c SHA256SUMS.
Exit the shell and stop the helper when you are done:
kubectl scale deployment opschain-db-recovery --replicas=0 -n ${KUBERNETES_NAMESPACE}
Backups taken before encryption
Backups taken by an earlier OpsChain version are not encrypted and have no SHA256SUMS file. Follow the same steps, skipping the checksum checks and the private key, and use these commands to extract and verify.
For a physical backup:
tar xzf "$BACKUP_DIR/base.tar.gz" -C /var/lib/postgresql/data/pgdata/
tar xzf "$BACKUP_DIR/pg_wal.tar.gz" -C /var/lib/postgresql/data/pgdata/pg_wal
To verify it, point pg_verifybackup at the manifest beside the tar files:
pg_verifybackup -m "$BACKUP_DIR/backup_manifest" /var/lib/postgresql/data/pgdata
For a logical backup:
psql -d postgres -f "$BACKUP_DIR/globals.sql"
pg_restore -d opschain --clean --if-exists --no-owner "$BACKUP_DIR/opschain.dump"
After you verify your first encrypted backup, delete the old unencrypted backups, including any copies off the host. Retention also removes old backups from the backup volume over time.
Resetting sites and work after a recovery
In a high availability setup, a backup brings back the site registrations, cordons and active work of the topology it was taken from. A restored site can come up cordoned, and sites that no longer exist stay listed with work that never runs. To clear them, add this flag to the deploy that starts OpsChain (step 6 of a physical restore or step 3 of a logical restore):
--set db.backup.recovery.resetAfterRecovery=true
Before OpsChain starts, a job runs the database migrations and then:
- cancels active changes, workflow runs and background tasks, and pending changes already assigned to a site.
- marks running agents as Failed. Start them again once OpsChain is running.
- removes every site registration and cordon. Live sites register again, uncordoned, within about a minute.
- deletes the background jobs queued for sites, pods and work that no longer exist.
The job's log is removed when it succeeds. To see its progress and summary, follow it from another shell as the deploy starts:
until kubectl logs -f job/opschain-recovery-reset -n ${KUBERNETES_NAMESPACE} 2>/dev/null; do sleep 1; done
Removed sites and failed agents are recorded in the audit history.
The job has 15 minutes. If it times out, run the same deploy again and it continues where it stopped. The flag is only accepted while OpsChain is stopped and the database is running, and has no effect on a replica site. Leave it out of later deploys.
Kubernetes resources recovery
Follow your backup tool's restore procedures if you need to restore a snapshot of your OpsChain Kubernetes resources.