Backup and restore

From canasta

Canasta includes backup and restore support powered by restic. Backups can be stored on any restic-compatible backend (AWS S3, Google Cloud Storage, Azure Blob Storage, Backblaze B2, local filesystem, SFTP, and more).

What gets backed up

Each backup includes:

  • Database β€” a full MariaDB dump of each wiki's database
  • Configuration β€” the config/ directory (Caddyfile, settings, wikis.yaml)
  • Extensions and skins β€” the extensions/ and skins/ directories
  • Uploaded files β€” the images/ directory
  • Public assets β€” the public_assets/ directory
  • Environment and overrides β€” .env, docker-compose.override.yml, and my.cnf (if present)

Setup

1. Configure a storage backend

Set the RESTIC_REPOSITORY and RESTIC_PASSWORD variables β€” plus any backend credentials β€” in your instance's .env file. Use canasta config set rather than editing .env by hand: it writes the values in the exact form restic expects (see the quoting warning below). Add -i/--id to target a specific instance.

The RESTIC_PASSWORD is yours to choose, and it encrypts the whole repository. Generate a strong random value and record it somewhere durable before you use it β€” a password manager, and your GitOps secrets if the instance is GitOps-managed. restic has no password reset or recovery mechanism: if you lose this value, every snapshot in the repository is permanently unreadable, even though you still control the storage bucket and its credentials. Generate one with:

openssl rand -hex 24

AWS S3:

canasta config set \
    RESTIC_REPOSITORY=s3:s3.amazonaws.com/your-bucket-name \
    RESTIC_PASSWORD=your-restic-password \
    AWS_ACCESS_KEY_ID=your-access-key \
    AWS_SECRET_ACCESS_KEY=your-secret-key

Azure blobs:

canasta config set \
    RESTIC_REPOSITORY=azure:your-container-name:/your-path \
    RESTIC_PASSWORD=your-restic-password \
    AZURE_ACCOUNT_NAME=your-account-name \
    AZURE_ACCOUNT_KEY=your-secret-key

Local filesystem:

canasta config set \
    RESTIC_REPOSITORY=/path/to/backup/repo \
    RESTIC_PASSWORD=your-restic-password

SFTP:

canasta config set \
    RESTIC_REPOSITORY=sftp:user@host:/path/to/repo \
    RESTIC_PASSWORD=your-restic-password

See the restic documentation for the full list of supported backends and their URL formats.

Do not quote values in .env. The entire .env file is handed to the restic container with docker --env-file, which β€” unlike canasta config get β€” does not strip surrounding quotes or a trailing carriage return. A value written as RESTIC_PASSWORD="secret", or a file saved with Windows (CRLF) line endings, reaches restic with the quotes or \r attached and surfaces as an opaque "wrong password" error. canasta config set writes values correctly; if you must hand-edit .env, leave values unquoted and keep Unix line endings.
Kubernetes instances: Only remote backup repositories (S3, SFTP, etc.) are supported. Local filesystem paths cannot be used as backup targets with the Kubernetes orchestrator.

2. Initialize the repository

This must be done once before creating any backups:

canasta backup init

Common operations

Creating a backup

canasta backup create -t before-upgrade

The -t flag assigns a descriptive tag to the backup. Tags help identify backups later β€” use names like before-upgrade, weekly-2024-03-15, or pre-migration.

Listing backups

canasta backup list

This displays all snapshots in the repository with their IDs, timestamps, and tags.

Restoring a backup

canasta backup restore -s abc123

Replace abc123 with the snapshot ID from canasta backup list. By default, a safety backup is taken before restoring. To skip this:

canasta backup restore -s abc123 --skip-safety-backup

Restoring a single wiki

To restore only one wiki from a backup without affecting the rest of the instance:

canasta backup restore -s abc123 -w wiki2

This restores the wiki's database, per-wiki settings (config/settings/wikis/{id}/), images (images/{id}/), and public assets (public_assets/{id}/). Shared files like global config, extensions, and skins are left untouched.

The wiki ID must exist in the current instance's wikis.yaml.

Migrating a wiki to a new instance: single-wiki restore is a convenient way to split one wiki out of a farm onto its own instance, but it copies only that wiki's data β€” it does not carry instance-level configuration. It leaves behind .env (including secrets such as OAuth client credentials), docker-compose.override.yml (on Docker Compose the web service only receives a fixed set of variables, so any custom variable a wiki reads via getenv() must be mapped in through the override file), and any GitOps configuration (env.template, wikis.yaml.template, custom-keys.yaml, hosts.yaml) β€” so the target will not be GitOps-managed. After a single-wiki migration, reapply these on the new instance: copy the needed .env values and docker-compose.override.yml from the source, canasta restart, and β€” if you want the new instance under GitOps β€” give it its own repository with canasta gitops init. A wiki split out onto its own instance is standalone, so it needs a new, separate repo β€” not canasta gitops join, which attaches another host to an existing instance's repo. A missing OAuth secret, for instance, surfaces at login as "Unable to initiate communication with OAuth provider".

Restoring a backup to a different instance

You can restore a full backup to a different Canasta instance. This is useful for cloning an instance, migrating to a new server, or inspecting the contents of a backup without affecting your running instance.

First, create a new instance and configure it with the same backup repository credentials in its .env file. Then restore:

canasta backup restore -s abc123

The target instance will receive all files and databases from the snapshot, replacing its current contents.

A restored snapshot keeps the original instance's domain (the snapshot includes its .env and config/). If the new instance should use a different domain, change it after restoring with a single command:

canasta config set MW_SITE_FQDN=newdomain.example.com

That one command cascades everything — it rewrites MW_SITE_SERVER and the url fields in config/wikis.yaml (only for wikis on the old domain), regenerates the Caddyfile, updates the Kubernetes Ingress host where applicable, and restarts the instance. See Changing the domain name for the full details, including wiki farms, GitOps-managed instances, and changing the scheme or port.

Disaster recovery: rebuilding on a new host

If a host is lost, rebuild its instance on a fresh host from a backup. This works like restoring to a different instance above, with two extra concerns: the backup repository must be reachable from the new host (for true disaster recovery keep backups off the host — an S3/cloud RESTIC_REPOSITORY, not a local path that dies with the server), and on Kubernetes the new host needs its cluster and storage stood up first.

The commands below assume you are running them on the recovery host itself. To drive the rebuild from a separate controller instead, add -H <newhost> to each command.

Docker Compose: create the instance, point it at the backup repository, and restore:

canasta create -i mysite -w main -n mysite.example.com
canasta config set -i mysite \
    RESTIC_REPOSITORY=s3:s3.amazonaws.com/your-bucket-name \
    RESTIC_PASSWORD=your-restic-password \
    AWS_ACCESS_KEY_ID=your-access-key \
    AWS_SECRET_ACCESS_KEY=your-secret-key
canasta backup restore -i mysite -s abc123

Kubernetes: stand up the cluster and storage first, then create, configure, and restore:

# 1. stand up the cluster and storage
canasta install k8s-cp
canasta storage setup nfs --install-server --share /srv/nfs/canasta

# 2. create a fresh instance, point it at the backup repo, restore
canasta create -i mysite -o kubernetes -w main -n mysite.example.com \
    --storage-class nfs --access-mode ReadWriteMany
canasta config set -i mysite \
    RESTIC_REPOSITORY=s3:s3.amazonaws.com/your-bucket-name \
    RESTIC_PASSWORD=your-restic-password \
    AWS_ACCESS_KEY_ID=your-access-key \
    AWS_SECRET_ACCESS_KEY=your-secret-key
canasta backup restore -i mysite -s abc123

The credential variables shown are for AWS S3; for another backend, supply that backend's variables instead of AWS_* (see Configure a storage backend).

The restore brings back all databases, uploads, extensions, skins, secrets, and settings, preserving the source instance's database password. The instance keeps its original domain; change it afterward with canasta config set MW_SITE_FQDN=… if needed (see Changing the domain name).

GitOps-managed instances

If the instance is GitOps-managed, do not set the backup credentials by hand — the repository already carries them. After creating the instance, re-attach the new host to the existing repository as a new environment — rather than re-initializing it — then restore the data:

canasta gitops join -i mysite --name newhost --repo <repo> --key <deploy-key> \
    --git-user-name "Your Name" --git-user-email you@example.com
canasta backup restore -i mysite -s abc123

gitops join adds the new host as its own environment under hosts/<name>/ in the shared repository and wires up its Argo CD Application and deploy key, leaving the existing environments untouched. For a self-hosted (non-forge) git host, it also seeds the host key Argo CD needs to verify the repository, so the new environment syncs without manual setup.

Inspecting a backup

To see what files are in a specific snapshot:

canasta backup files -s abc123

Comparing two backups

canasta backup diff -s abc123 --snapshot2 def456

Deleting a backup

canasta backup delete -s abc123

Purging old backups

Remove backups that exceed a retention policy:

# Keep only backups from the last 30 days
canasta backup purge --keep-within 30d

# Keep only the 10 most recent backups
canasta backup purge --keep-last 10

# Combine both: keep at least the last 5, and anything within 14 days
canasta backup purge --keep-within 14d --keep-last 5

The retention flags mirror restic's forget options: --keep-last, --keep-hourly, --keep-daily, --keep-weekly, --keep-monthly, --keep-yearly, --keep-within, and --keep-tag.

Use --dry-run to preview what would be removed without actually deleting anything:

canasta backup purge --keep-within 30d --dry-run

Scheduling recurring backups

Set up automatic backups using a cron expression:

# Daily at 2:00 AM
canasta backup schedule set "0 2 * * *"

# Every 6 hours
canasta backup schedule set "0 */6 * * *"

A scheduled backup is a crontab entry on the host where the instance runs β€” the local host, the remote host of a remote Docker Compose instance, or the control-plane node for Kubernetes (the same host the instance's other commands run on). That entry runs canasta backup create, so canasta must be installed on that host. For a local instance it already is; for a remote or Kubernetes instance, install it on the instance's host first:

canasta install canasta -H <host>

canasta backup schedule set checks for this and stops with the same instruction if canasta is missing on the target host.

To automatically purge old backups after each scheduled backup, add --purge-older-than:

# Daily backups, keeping the last 30 days
canasta backup schedule set --purge-older-than 30d "0 2 * * *"

If you reschedule with a new expression, the existing schedule is replaced. To schedule multiple times, combine them in one expression (e.g., "0 0 * * 2,5" for Tuesdays and Fridays).

Backup output is logged to backup.log in the instance directory. The log is automatically rotated when it exceeds 10 MB β€” the previous log is kept as backup.log.1.

To view the current schedule:

canasta backup schedule list

To remove a scheduled backup:

canasta backup schedule remove

Note: The schedule and its purge/retention settings are stored as config/backup-schedule.yml in the instance's configuration, which is captured by your backups. Restoring a snapshot re-creates the schedule on the restored host β€” including when restoring onto a new host or cluster β€” so you do not need to set it again by hand after a rebuild. For Docker Compose instances managed with gitops, canasta gitops pull also re-applies the schedule when it changes in the repository.

Repository maintenance

Check the repository for errors:

canasta backup check

If a backup operation was interrupted and left the repository locked:

canasta backup unlock

Recovering a lost RESTIC_PASSWORD

The RESTIC_PASSWORD encrypts the repository, and restic has no way to reset or recover it. If you lose it, the existing snapshots cannot be decrypted β€” having the storage bucket and its credentials (for example AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) is not enough on its own.

Before giving up on existing backups, look for the old password where it may still exist: a password manager or your GitOps secrets, the history of a GitOps repository (even if the current files no longer contain it), an .env backup, controller shell history, or the environment of a still-running container:

docker exec <web-container> printenv | grep RESTIC

If the password is truly gone, those snapshots cannot be recovered, but you can start a fresh repository and resume backups. Either point RESTIC_REPOSITORY at a new, empty location, or empty the current one first β€” restic init refuses to run against a repository that already exists.

To reuse the same repository location, delete the old repository's objects, then set a new password and re-initialize:

# 1. Delete the old repository's contents. This permanently destroys the old,
#    unreadable snapshots. For S3, empty exactly the repository's prefix:
aws s3 rm s3://your-bucket-name/your-prefix/ --recursive

# 2. Generate a new password, SAVE it, then set it (no restart needed β€”
#    only the backup container reads it):
openssl rand -hex 24            # copy this value into your password manager
canasta config set --id mysite --no-restart RESTIC_PASSWORD=<paste-the-value>

# 3. Initialize the fresh repository and take a clean backup:
canasta backup init   --id mysite
canasta backup create --id mysite -t fresh-start
canasta backup list   --id mysite

To keep the old snapshots around in case the password later turns up, skip the deletion and instead set RESTIC_REPOSITORY to a different bucket path before running canasta backup init.

Save the new password durably this time, and β€” if you have not already β€” set up a recurring schedule so you are never relying on a single snapshot.

Further reading