Skip to content

About

Backups for Convex you can actually restore. Scheduled, encrypted (age), off-site backups for self-hosted Convex and Convex Cloud — S3/R2/B2 with Object Lock, one-click & partial restores, workspaces, 2FA/passkeys/SSO, branded emails. Plus a Convex-on-Coolify template.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Convex Backup Console logo

Convex Backup Console

Backups for Convex you can actually restore.

Scheduled, encrypted, off-site backups for self-hosted Convex and Convex Cloud — see your whole backup estate on one map, spin up a brand-new bucket in under a minute, and restore with confidence instead of hope. Plus a production-ready Convex-on-Coolify template to run the instances themselves.

Version AGPL-3.0-or-later License Deploy on Coolify Convex TypeScript e2e

Deploy on Coolify Deploy to Render Deploy on Railway Deploy on Fly.io Run with Docker

60-second setup · Deploy anywhere · Why teams use it · Screenshots · How it works · Security · FAQ

Convex Backup Console — overview with topology map

Why this exists

Convex's snapshot export is great, but a backup strategy needs more than npx convex export in a cron job:

  • Off-site, versioned and immutable copies. A backup on the same server, or one an attacker can delete, isn't a backup.
  • Encryption you control, so a leaked bucket doesn't leak your users' data.
  • Restores that are rehearsed and safe: into a staging copy, one table at a time, with personal data scrubbed.
  • Visibility: know when a backup didn't happen, not just when one failed, and see how everything connects.
  • One place for every project and client, with proper access control.

Convex Backup Console does all of that in a single container — and now shows you the whole picture at a glance.

✨ Why teams use it

🧭 See your whole backup estate on one map

Every instance, job and destination, drawn as a live topology — not three separate lists you have to mentally wire together. Each connection is colored by the health of its last run, running jobs animate, and hovering (or tapping, on mobile) traces one path end to end. ⌘K jumps straight to any node.

Overview page with the topology map: instances, jobs and destinations connected and colored by health

☁️ A storage bucket in under a minute

No more hand-creating an R2 or S3 bucket, an IAM user, and a least-privilege policy before you can even add a destination. Paste a one-time setup credential and the console provisions the bucket and mints a brand-new credential scoped to only that bucket — the setup credential is never stored, logged or shown again. Progress streams in step by step, and anything created is rolled back if a later step fails.

Create a bucket for me wizard, options step

📈 A pipeline you can actually audit

Every backup shows its export → compress → encrypt → upload → verify steps, each with a real duration — live while it's running. After every upload, the stored object's size is confirmed against the file that was sent (S3 HeadObject, pinned to the version; local: stat); a mismatch fails that destination's copy exactly like an upload failure would.

Runs table with per-step pipeline timings

↺ Restore exactly what you need, into the right place

Full snapshot or selected tables, into the same instance or a different one, with source → target lineage shown above the live log so there's no ambiguity about what's about to happen. Scrub emails, names and phone numbers for a safe staging copy, confirm by typing the target's name, and watch the import stream in real time.

Restore dialog showing source backup, target instance, scrubbing and typed confirmation

🧱 Ransomware-proof, cost-aware storage

Fan out to Cloudflare R2, AWS S3, Backblaze B2, Wasabi, MinIO, RustFS or a local volume. Turn on S3 Object Lock and a backup can't be deleted or overwritten for N days — not even with stolen credentials. The Storage page shows growth over 90 days, a retention timeline per destination, and an estimated monthly cost.

Storage page with growth chart and per-destination retention timeline

🚀 60-second setup

Coolify (recommended)

  1. + New → Docker Compose Empty → paste backup-console.coolify.yaml.
  2. Deploy. Coolify generates the domain and the secrets.
  3. Open the URL and follow the setup wizard: paste the one-time setup code from the service's Logs, create your admin account, add 2FA, create the first workspace (name, logo, timezone) and invite your team.
  4. Create your encryption key in Workspace → Settings.

Save SERVICE_PASSWORD_64_MASTERKEY in your password manager. It encrypts every stored key and secret.

Full guide, including OAuth setup: docs/coolify.md.

Render, Railway, Fly.io

Deploy to Render One click from render.yaml: disk, health check and generated secrets (paid instance for the disk).
Deploy on Railway Deploy the repo with root directory /app, add a volume at /data and two secrets.
Fly.io cd app && fly launch --copy-config with the included fly.toml (steps).

Not Vercel/Netlify: the console needs an always-on process (for schedules) and a persistent disk (for its database), which serverless platforms don't provide. See docs/deploy.md for requirements and every platform.

Any Docker host

docker run -d --name convex-backups -p 3000:3000 \
  -v convex-backups:/data --read-only --tmpfs /tmp \
  -e PUBLIC_URL=https://backups.example.com \
  -e BACKUP_MASTER_KEY="$(openssl rand -base64 48)" \
  -e BETTER_AUTH_SECRET="$(openssl rand -base64 48)" \
  ghcr.io/flyingwebie/convex-backup-console:latest
docker logs convex-backups   # shows the setup code; then open your PUBLIC_URL

Put it behind HTTPS (Traefik, Caddy, Nginx or a Cloudflare Tunnel; see app/docker-compose.tunnel.yaml).

Then

Step Where
Add a self-hosted instance: API URL + ./generate_admin_key.sh output Instances → Add instance
Add a Convex Cloud deployment: a production or dev deploy key Instances → Add instance → Convex Cloud (guide)
Add a destination — Create bucket for me, or connect an existing R2/S3/B2… Destinations
Create a job: schedule, retention, encryption Jobs
Choose who gets which email, brand and edit them Emails
Chat/webhook alerts Notifications

Need a Convex instance first?

convex-instance.coolify.yaml deploys self-hosted Convex with Postgres and RustFS (S3) storage on Coolify. The detailed walkthrough is in docs/convex-on-coolify.md.

🖼️ See it

A live demo workspace — "Acme Agency," four instances, three destinations, three months of scheduled runs — walked through the redesigned console.

Overview, dark theme Overview: health of every instance, plus the topology map Runs & restores Runs: every backup, its pipeline and its copies
Backup contents Contents: tables, rows, changes since last backup Restore Restore: source → target lineage, mode, tables, scrubbing
Storage Storage: growth, cost and retention timeline Create bucket for me Create bucket for me: a bucket and scoped credential, no cloud console required
Destinations Destinations with Object Lock Jobs Jobs: instance → destination at a glance
Instances Instances Members Members & roles
Command palette ⌘K command palette: jump to any instance, job or destination Overview, light theme Light theme

🧠 How it works

flowchart LR
  subgraph Sources
    SH[Self-hosted Convex<br/>URL + admin key]
    CC[Convex Cloud<br/>deploy key]
  end
  subgraph Console[Convex Backup Console · one container]
    S[Scheduler / API / UI] --> X[Export → compress → encrypt → upload → verify]
    R[Restore: filter tables · scrub PII] --> I[Convex import API]
    DB[(SQLite · secrets encrypted<br/>with BACKUP_MASTER_KEY)]
  end
  SH & CC -- snapshot export --> X
  X --> D1[(Cloudflare R2)]
  X --> D2[(S3 / B2 with Object Lock)]
  X --> D3[(Local volume)]
  D1 & D2 & D3 --> R
  I --> SH & CC
  X -. alerts & digests .-> N[Branded email · Slack · Discord · Telegram · Webhook]
Loading
  1. A job asks the instance for a snapshot export through Convex's admin HTTP API, the same API npx convex export uses.
  2. The console downloads it, records a manifest (tables, row counts, files, SHA-256) and encrypts it with the workspace's age key.
  3. The archive is uploaded to every destination (with Object Lock where configured) and verified against the stored object, then retention prunes old copies and the export is removed from the source.
  4. A restore downloads and decrypts a copy, optionally filters tables or scrubs personal data, and runs Convex's import with your chosen mode.

Recover without the console

age -d -i convex-backups-age-key.txt backup.zip.age > backup.zip   # key from Workspace → Settings
npx convex import --replace-all backup.zip                           # into any Convex deployment

🔐 Security

Area How
Sign-in Invite-only. Password (12+ characters) or Google / GitHub / Microsoft; second factor always required (TOTP or passkey), including after social sign-in
Sessions 24h, HttpOnly cookies, per-session factor tracking, list/revoke, sign out everywhere
Brute force 5 sign-in / 2FA attempts per minute per IP (Cloudflare-aware)
Secrets at rest Admin keys, deploy keys, S3 secrets, webhook URLs and age keys are encrypted with AES-256-GCM under BACKUP_MASTER_KEY
Backups at rest age (X25519) per workspace; S3 Object Lock for immutability
One-time credentials "Create bucket for me" setup credentials are held in memory for a single request only — never stored, logged or shown again
Isolation Every query scoped to the active workspace; role checks on every server function and API call
Container Non-root, read-only filesystem, all capabilities dropped
Audit Every change, backup, restore, download and key reveal is logged

🤖 Back up before every deploy

- uses: flyingwebie/convex-backup-console/.github/actions/convex-backup@main
  with:
    console-url: https://backups.example.com
    token: ${{ secrets.BACKUP_CONSOLE_TOKEN }}   # Workspace → Settings → API tokens
    job-id: <job-id>
- run: npx convex deploy

The REST API is documented in docs/github-action.md.

🆚 Compared with

This console npx convex export + cron Convex Cloud built-in backups
Self-hosted and Cloud ✅ ✅ Cloud only
Off-site copies in your own storage ✅ DIY ❌
Encryption you hold the key to ✅ DIY ❌
Immutable (Object Lock) ✅ DIY ❌
One-click bucket + scoped credential ✅ ❌ —
Partial & scrubbed restores ✅ ❌ ❌
Visual topology of instances/jobs/destinations ✅ ❌ ❌
Alerts, digests, status URL ✅ DIY —
Multi-project, roles, audit log ✅ ❌ Per-team

Convex Cloud's own backups are excellent, so keep them on. This adds independent, encrypted copies outside Convex.

📦 Everything else

Backups

  • 🔁 Manual, cron or API-triggered jobs, one instance → many destinations, including file storage.
  • ☁️ Self-hosted Convex (URL + admin key) and Convex Cloud (deploy key; URL looked up automatically).
  • 🔐 age encryption per workspace: decryptable offline with the standard age CLI.
  • 🗄️ S3-compatible destinations (Cloudflare R2, AWS S3, Backblaze B2, Wasabi, MinIO, RustFS) and local volumes.
  • 🧱 S3 Object Lock: backups can't be deleted or overwritten for N days, not even with stolen credentials.
  • 🧹 GFS retention (keep-last + daily/weekly/monthly), exports cleaned up on the source automatically.
  • 🧾 Manifests: tables, row counts, files and SHA-256 per backup, compared with the previous one.

Restores

  • ↺ Into the same or another instance, in four modes (full, replace, append, empty-only).
  • ✂️ Partial restores: only the tables you pick; everything else stays untouched.
  • 🕶️ Scrubbed staging copies: fake emails, names and phones; redaction; hashing; consistent within a restore.
  • ⬇️ Download any backup, decrypted or as the raw .age file.

Operations

  • 🧭 Topology map on the Overview: instances → jobs → destinations, colored by last-run health.
  • ⌘K command palette: jump to any page, instance, job or destination, or run a quick action.
  • 📈 Overview & storage dashboard: health per instance, growth chart, monthly cost estimate.
  • ✉️ Branded emails built with React Email: workspace logo, accent colour and footer; templates you edit with variables and a live preview (owners can write full HTML); a per-email recipients grid (roles, specific members, outside addresses). Members can opt out; alerts cover failed/partial backups, no recent backup, restores and digests.
  • 🔔 Chat alerts: Slack, Discord, Telegram, webhook.
  • 🩺 Status URL for Uptime Kuma, Better Stack and UptimeRobot (200 healthy / 503 not).
  • 🚀 REST API + GitHub Action: back up right before npx convex deploy, with a per-workspace switch to turn API access off.
  • 🧭 Guided product tour on first sign-in (role-aware, replayable from the account menu).
  • 🗑️ Trash & deletion policy: restore deleted items for N days, with separate toggles and delays for deleting their backup files from remote (S3/R2/B2) and local storage.
  • 📜 Audit log of every action, exportable as CSV.

Teams

  • 🏢 Workspaces, e.g. one per client, each with its own logo, timezone, keys, alerts and members.
  • 👥 Roles: Owner · Admin · Operator (run and download, no restore) · Viewer (read-only).
  • 🔑 Mandatory 2FA (authenticator or passkey), Google / GitHub / Microsoft sign-in, invite-only accounts.
  • 🛡️ Session management, sign out everywhere, admin 2FA reset, login rate limiting.

📁 Repository

app/                            The console: TanStack Start · Better Auth · SQLite · React Email · shadcn/ui
backup-console.coolify.yaml     Coolify template: the console (deploy once)
render.yaml                     Render Blueprint (Deploy to Render button); app/railway.json, app/fly.toml for Railway / Fly.io
convex-instance.coolify.yaml    Coolify template: a self-hosted Convex instance (Postgres + RustFS), one per project
docs/                           Guides (Coolify, Convex on Coolify, GitHub Action), images
testing/                        Local Docker kit, Playwright e2e (45 + 8 onboarding checks), demo data
legacy/                         The old all-in-one instance compose, kept for existing deployments
.github/                        Image build (amd64 + arm64) and the convex-backup GitHub Action

🧪 Development & tests

cd app && pnpm install
BACKUP_MASTER_KEY=dev BETTER_AUTH_SECRET=dev-secret-at-least-32-characters-long \
SETUP_CODE=dev-setup pnpm dev   # then open http://localhost:3000/setup
pnpm test                       # unit tests
pnpm typecheck

The end-to-end suite runs two real Convex stacks (Postgres + RustFS), an S3 stand-in, Mailpit and a webhook receiver locally. See testing/local/README.md and the results in testing/TESTING.md.

❓ FAQ

Does it need Cloudflare? No. Cloudflare R2 is a great destination and Cloudflare Tunnel is an optional hardening layer, but neither is required.

What happens if I lose the console? Your backups are plain (or age-encrypted) Convex snapshot zips in your own buckets. Decrypt them with the age key you saved and npx convex import them. The console is a convenience, not a dependency.

Can I restore into Convex Cloud? Yes, with a deploy key for the target deployment. Restores into a production Cloud deployment show an extra warning.

How big can a backup be? Exports are streamed to disk and uploaded in parts, so size is bounded by the container's free disk space, not memory.

Is Microsoft sign-in work-accounts only? Set MICROSOFT_TENANT_ID to common (any account), organizations (work/school), or your tenant ID.

Does "Create bucket for me" need standing cloud credentials? No — the setup credential you paste in (an R2 API token or an AWS IAM access key) is used once, in memory, to create the bucket and a brand-new credential scoped to only that bucket. It's never stored, logged or shown again.

🗺️ Roadmap

  • Scheduled restore drills with automatic verification
  • Backing up Convex environment variables alongside data
  • Self-registration of new instances from the Convex template
  • One-click template in Coolify's built-in catalogue (how to submit)

🤝 Contributing

Issues and pull requests are welcome — start with CONTRIBUTING.md. Found a vulnerability? Please report it privately as described in SECURITY.md.

License

Code: AGPL-3.0-or-later, from version 1.0.0-beta.3. Versions up to and including 1.0.0-beta.2 were released under the MIT licence and remain available under those terms (see their tags/releases).

  • Self-hosting the console unmodified is free for any use, including commercial.
  • If you modify it, you don't have to publish your changes to the world. But everyone who uses your modified version over a network (your team, contractors or customers) must be offered its source code (AGPL §13). The sidebar's source link points to this repository, so after modifying the console, point it at your own modified source.
  • Commercial licences (to use the code without the AGPL's source-sharing obligations) are available from the copyright holder — open an issue or see the contact in SECURITY.md.
  • Contributions are accepted under the Contributor License Agreement, which keeps relicensing (including dual-licensing under terms other than the AGPL) possible.

Docs: the contents of docs/ and this README (including screenshots) are licensed under CC BY 4.0. Code samples embedded in the docs remain licensed under the code licence above.

© Davide Del Gatto · Flying Web Solutions. Not affiliated with Convex, Inc. or Coolify.

About

Backups for Convex you can actually restore. Scheduled, encrypted (age), off-site backups for self-hosted Convex and Convex Cloud — S3/R2/B2 with Object Lock, one-click & partial restores, workspaces, 2FA/passkeys/SSO, branded emails. Plus a Convex-on-Coolify template.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages