Repositories (off-site backup)¶
A Repository is an off-site, encrypted store for snapshots of your mailboxes. Each Repository is a restic repository backed by an S3 bucket or a local filesystem path. Mailboxes pick a Repository in their backup policy to enable scheduled off-site backups.
This page is the admin's home for managing Repositories: adding them, testing them, monitoring their health, and recovering snapshots from them.
The four-stage model¶
MFB keeps your mail safe in four stages:
┌─────────┐ pull ┌──────────────┐ push ┌──────────────┐ capture ┌──────────────┐
│ SOURCE │──────────▶│ LOCAL │──────────▶│ REPOSITORY │────────────▶│ SNAPSHOT │
│ IMAP │ sync │ BACKUP │ back up │ │ retention │ │
└─────────┘ └──────────────┘ └──────────────┘ └──────────────┘
- Source — the user's IMAP server (Gmail, Outlook, IMAP host, …).
- Local backup — the local Maildir kept by mbsync. Always-on, lives on the MFB host.
- Repository — the off-site, encrypted restic store. Configured here.
- Snapshot — a point-in-time capture stored inside a Repository.
The Repositories admin page covers stages 3 and 4. Stage 1 lives on each account's detail page; stage 2 is the Local backup on the same page.
Adding a Repository¶
Open Admin → Repositories and use the Add Repository form at the bottom of the page.
Common fields¶
| Field | Notes |
|---|---|
| Name | Human-friendly label, shown in account-detail pages and audit log. Example: rustfs or Hetzner S3. |
| Backend Type | S3 for an S3-compatible object store, Local for a filesystem path. |
| Restic Password | The encryption passphrase for the restic repository. Treat this as a master key — see Disaster Recovery. |
| Skip TLS certificate verification | For self-signed CA endpoints only. Production deployments must use a trusted certificate. The UI shows a yellow warning banner when this is enabled. |
S3 fields¶
| Field | Notes |
|---|---|
| S3 Endpoint | Full URL of the S3 endpoint (e.g. https://s3.amazonaws.com or https://s3.k8s.local). |
| S3 Bucket | The bucket name. Each mailbox's snapshots are isolated under a per-account path inside this bucket. |
| Access Key | S3 access key ID. Encrypted in the database. |
| Secret Key | S3 secret access key. Encrypted in the database. |
Local fields¶
| Field | Notes |
|---|---|
| Local Path | Filesystem path on the MFB host (or a volume mounted into the container). Each mailbox's snapshots are isolated under a per-account subdirectory. |
When you submit the form, MFB runs restic init against the Repository to verify the credentials. A failure leaves the form populated and shows a flash error; success persists the Repository.
The status quartet¶
Each Repository row shows four columns of cached status:
| Column | Meaning |
|---|---|
| Mailboxes | Number of mailbox backup policies pointing at this Repository. |
| Snapshots | Sum of last_snapshot_count across all those mailboxes (i.e., snapshots stored in this Repository). |
| Last back-up | Most recent successful back-up across all mailboxes using this Repository. |
| Health | Healthy (last success ≤ 2d), Stale (older), No back-up yet (configured but never), Empty (no policies pointing here). |
These values are cached in the database and updated by backup_worker after each run — the page never shells restic to render. Reads are constant-time regardless of how many snapshots exist.
Per-mailbox backup policy¶
Each mailbox can attach a backup policy to a Repository from its account-detail page (Off-site backup → Repository section). The policy specifies:
- Repository — which Repository to use.
- Schedule — cron expression. Default
0 2 * * *(daily at 02:00). - Retention preset —
Light(7 daily / 4 weekly),Standard(30 daily / 12 weekly / 6 monthly),Full(90 daily / 52 weekly / 24 monthly), orCustom.
Triggering a back-up:
- Scheduled — APScheduler runs
backup_worker.execute_backupon the cron. - Manual — the Back up now button on the account-detail page submits to
/accounts/{id}/backup/now.
Each successful back-up updates: last_status=completed, last_run_at, last_successful_run_at, last_backup_at, last_snapshot_count, last_snapshot_at.
Recovering from a snapshot¶
Two entry points to the same flow:
/recover— top-level Restore page → "Recover from a snapshot". Lists mailboxes that have at least one snapshot.- Account detail → Off-site backup → Repository → Snapshots — the per-mailbox snapshot table.
Click Restore this snapshot on a row. MFB:
- Runs
restic restoreto a temporary directory under the mail store:{store_path}/.offsite-restore/{account-id}-{timestamp}/. - Creates a new placeholder mailbox named
Recovered <name> (date)pointing at that directory, withsuspended=true. - Shows a flash message explaining the next step.
The recovered placeholder is suspended: Dovecot does not serve it (the userdb endpoint filters on enabled AND NOT suspended), so it cannot be opened in the webmail. This is deliberate — it's a chance for the operator to inspect or rename before exposing.
To complete the recovery: open the placeholder mailbox and click Promote to live. This clears the suspended flag; Dovecot starts serving the mailbox immediately under its new namespace.
To discard a recovery: delete the placeholder mailbox. The on-disk directory under .offsite-restore/ is removed when the mailbox is deleted.
Editing or deleting a Repository¶
| Action | UI | Notes |
|---|---|---|
| Edit | Pencil icon on the row — expands an inline form. | Change name, S3/local fields, Restic password, or insecure-TLS. New fields submit; existing-blank fields are left untouched. |
| Test connection | Plug-zap icon. | Re-runs restic init (idempotent) to verify credentials are still valid. |
| Delete | Red trash icon. | Disabled when ≥1 mailbox backup policy still references this Repository. Drain those first by reassigning or removing them from the affected mailboxes. |
Deleting a Repository does not delete snapshots in the underlying S3 bucket or local path. To purge snapshots from disk, run restic forget --prune against the repository directly using the encryption password.
What MFB does NOT do (for Repositories)¶
- It does not back up the MFB database itself. See Disaster Recovery. Without your
MAILFALLBACK_SECRET_KEYand a Postgres dump, snapshots are unrecoverable. - It does not migrate snapshots between Repositories. Moving from one Repository to another means starting a fresh chain in the new Repository — old snapshots stay in the old one until you decide what to do with them.
- It does not garbage-collect on-disk recovered placeholders. The
.offsite-restore/{account-id}-{timestamp}/directory persists until the placeholder mailbox is deleted.
Audit log¶
Every Repository action is logged with display labels:
| Underlying action | Display label |
|---|---|
backup_destination.create |
Repository created |
backup_destination.edit |
Repository edited |
backup_destination.delete |
Repository deleted |
account.backup_configure |
Backup policy configured |
account.backup_now |
Manual back-up triggered |
account.backup_restore |
Recovered from snapshot |
account.promote_recovered |
Recovered mailbox promoted to live |
Filter by these in the Audit Log page.