Skip to content

Back up and restore Uniffy

Back up three things to restore Uniffy: PostgreSQL, object storage, and APP_MASTER_KEY. The database holds content and permissions. Object storage holds files. The master key makes stored secrets readable after a restore.

Store Holds Your job
PostgreSQL Every note, message, task, permission, and setting Back up continuously. This page.
APP_MASTER_KEY The key wrapping every tenant secret One copy, offline, once. Below.
Object storage Originals, retained versions, thumbnails, and completed video copies Protect the bucket with a backup or versioning and recovery policy. On a VM, back up the file store volume too.
Media scratch Temporary conversion outputs No backup. Disk backed emptyDir can be discarded when a pod is removed.
Meilisearch Search index Nothing. Rebuilt from Postgres by a background job.
Valkey Queues, caches, and edits accepted in the last few seconds that PostgreSQL does not hold yet Choose its persistence and keep its data volume on durable storage. Persistence decides what a crash costs; a file backup is optional and returns little. Below.

A database backup without the master key leaves stored credentials unreadable. Without object storage, file metadata can return while downloads and videos stay missing. Storage durability protects against hardware failure; it does not replace a recovery plan for deleted or overwritten objects.

Media scratch is disposable. Pending video work can restart from object storage after recovery reclaims an expired processing claim. Keep stored originals and completed copies in your file backup policy.

When someone types in a note, a task or an event description, the server accepts the edit into Valkey first and writes it to PostgreSQL about ten seconds after the first unsaved edit, later when the workers fall behind. Collaborators see the edit at once. The chip in the editor reads Synced while the edit waits and Saved once PostgreSQL holds it.

So losing Valkey’s data can cost the server its copy of the edits waiting for their save. Normally that is the last ten seconds or so of each document being edited, more while saves fall behind. Which events lose them depends on the persistence Valkey runs with. Uniffy reads that setting at startup and logs the loss window it implies, but never changes it.

Valkey persistence Clean restart or upgrade loses Crash or power cut loses
AOF with appendfsync always Nothing Nothing
AOF with appendfsync everysec Nothing Edits accepted in about the last second
AOF with appendfsync no Nothing Edits the operating system had not written to disk yet
RDB snapshots only Nothing, because Valkey writes a snapshot as it shuts down Edits accepted since the last snapshot that were still waiting for their save
No persistence Every edit waiting for its save Every edit waiting for its save

A clean restart is Valkey shutting down on its own terms, as it does on a normal stop. Losing the data volume itself loses every edit waiting for its save, whatever the setting.

That is only the server’s copy. Every editor also keeps its own edits in encrypted storage on the device until PostgreSQL holds them. When Valkey comes back, open editors send again whatever the server lost. An editor that was closed in the meantime sends it the next time its author opens Uniffy on that device.

An edit is gone for good only when its author never returns to that device, signs out and discards it, or edits in a browser that cannot store it. When a write to the device fails, the chip reads Not saved on device, so the author knows to keep the editor open.

So pick Valkey’s persistence for how much of the last few seconds you can afford to lose on a crash, keep its data volume on storage you trust, and keep backing up PostgreSQL.

A file backup of Valkey adds little for edits. Saves follow within seconds, so a backup taken minutes or hours ago holds an edit PostgreSQL lacks only when saves were stalled at that moment, for example while the core workers were down. Its edits stay encrypted with your organization’s key. It also holds queued jobs and a record of who edited which document and when, so guard it like a PostgreSQL backup.

If you restore one, start Valkey on the restored files; never load keys into a running instance. The restart gives Valkey a new identity. On the next touch of each document, Uniffy saves the restored edits that continue what PostgreSQL already holds and sets the rest aside until maintenance clears it a week after its newest edit. The restored files also bring back the job queue as it was, so jobs that ran after the backup can run again.

Edits waiting in Valkey are encrypted with your organization’s key, in memory and in its persistence files. The key never reaches Valkey. Valkey still holds queued jobs, caches, and a record of who edited which document and when. Protect Valkey, its disk and its files the way you protect PostgreSQL. Never give it an allkeys-* eviction policy; Configure Uniffy explains why.

Optional, and the first optional thing you should turn on. When Postgres is bundled, the chart configures CloudNativePG to archive write ahead logs to an S3 bucket continuously and take a base backup on schedule. Continuous archiving gives you point in time recovery, so you can restore to any moment inside the retention window.

postgres:
backup:
enabled: true
endpoint: https://s3.eu-central-1.amazonaws.com
destinationPath: s3://acme-uniffy-pg-backups/prod
retention: 30d
schedule: "0 30 2 * * *" # daily base backup at 02:30, six field cron, seconds first

Credentials go in their own secret, not in uniffy-secret:

Terminal window
kubectl -n uniffy create secret generic uniffy-pg-backup-s3 \
--from-literal=ACCESS_KEY_ID='...' \
--from-literal=ACCESS_SECRET_KEY='...'

Use a dedicated bucket, never the file storage bucket. Give it a different lifecycle, different credentials, and ideally a different failure domain. Backups in the same account with the same keys as the data they protect are one leaked credential away from being deleted together. Write only credentials on this bucket are worth the extra provider clicks.

This applies to bundled Postgres only. If you brought an external database, its backup regime belongs to whoever runs it, and the master key ritual below still applies to you.

The same values work in /etc/uniffy/values.yaml, then sudo uniffy-k3s upgrade applies them. The bucket must live off the machine: any S3 provider, another building, another company, anywhere that is not this VM.

Local dumps under /var/backups/uniffy/ live on the same disk as the database. They help recover from a bad upgrade, but cannot survive a dead machine.

Back up the bundled object store separately through volume snapshots or a file bucket backup. Keep those backups off the VM too. PostgreSQL archiving does not include uploaded files or completed playback copies. A media worker’s temporary scratch volume needs no snapshot.

APP_MASTER_KEY never changes after install, so backing it up is a one time ritual:

Terminal window
kubectl -n uniffy get secret uniffy-secret \
-o jsonpath='{.data.APP_MASTER_KEY}' | base64 -d

Print it, store it in your password manager or offline vault, and confirm the copy against the running secret. Losing it does not break the app today; it breaks the restore you will attempt in two years. That failure stays silent until the worst moment, which is why the installer shouts about it.

Restore builds a fresh cluster from the archive rather than repairing the old one in place. Point the chart at the backup store, optionally at an exact moment:

postgres:
recovery:
source: s3://acme-uniffy-pg-backups/prod
endpoint: https://s3.eu-central-1.amazonaws.com
# targetTime: "2027-03-14 09:00:00+00" # omit for latest

Install with these values, the same uniffy-secret including the same APP_MASTER_KEY, and the same backup credentials secret. CloudNativePG replays the base backup plus the archived logs up to the target, and the app comes up against the recovered database. Search returns as the reindex job rebuilds Meilisearch; nobody has to do anything for that beyond waiting.

The pre upgrade dump is the faster path when the database is minutes old and the archive is overkill. The Upgrades page carries that procedure and the reasons rollback without a restore is not offered.

kubectl -n uniffy get backups shows the base backups CloudNativePG has taken and whether the last one completed. Check it after turning on backups, then schedule a quarterly restore drill in a separate namespace or VM.

Log in, open a note, download a stored file, and play a video from the restored object store. Check an encrypted credential too.