Archon
archon sidecar GitHub
A Docker backup sidecar

Automatic, encrypted database backups. Zero code changes.

Archon is one Docker container you run next to your app. On a schedule, it dumps your database, encrypts the dump, fingerprints it with a checksum, ships it to S3, Azure or local disk, and deletes old copies. When you need it back, one API call verifies and restores it.

Python 3.11FastAPI + APSchedulerAES-256 + SHA-256MIT licence
  1. 1 Dump
  2. 2 Encrypt
  3. 3 Checksum
  4. 4 Store
  5. 5 Retain

Dump. On schedule, Archon runs the database's own tool, such as pg_dump, in a subprocess.

In one sentence

You add one container. Archon handles the rest.

You give itOne YAML file

Which databases, how often, where to store, how many copies to keep.

It doesBackup on schedule

Dump, encrypt with AES-256, checksum with SHA-256, upload, prune old copies.

You getSafe restores

One API call. The file is verified before it is decrypted, and decrypted before anything is written.

See it work

a backup, then a tampered file refused on restore
The problem

Every team rebuilds backups. Most rebuild them badly.

A cron job here, a shell script there. No encryption, no checksum, and nobody finds out it was broken until the day it is needed. Then the next project does it again, slightly differently.

Hand-rolled
  • A pg_dump cron job with no encryption
  • No way to tell whether last night's backup worked
  • Backups pile up until the disk fills
  • Restore means SSH and guesswork
  • Written again for every project and language
With Archon
  • AES-256-CBC on every file; the key never touches disk
  • A SHA-256 sidecar, verified before every restore
  • Hourly, daily, weekly and monthly retention, enforced
  • One POST /restore call, drop-and-recreate, audited
  • One image and one config file for any stack

Nothing asked of your codebase. No SDK import, no app-side hook, no vendor lock-in. If you can add a block to docker-compose.yml, you are done.

How it works

Two fixed pipelines. No shortcuts, no reordering.

Every backup and every restore goes through the same steps in the same order. The restore path checks integrity before it decrypts, and decrypts before it writes anything.

Backup

POST /backup · or on schedule
1Dumpprovider.backup()

Native tool per engine, run in its own subprocess.

2Encryptencrypt()

AES-256-CBC with a key held only in memory.

3Checksumchecksum()

SHA-256 written as a .sha256 sidecar file.

4Storestorage.write()

Local disk, Amazon S3 or Azure Blob.

5Retainretention.enforce()

Prunes old files by your hourly to monthly mix.

Restore

POST /restore · confirm: true required
1Readstorage.read()

Fetches the file and its checksum sidecar.

2Verifyverify_checksum()

On mismatch the restore stops here.

GATE · aborts on mismatch
3Decryptdecrypt()

Only after the file is proven intact.

4Restoreprovider.restore()

Drop, recreate, restore. No partial state.

What you get

Built for the day something goes wrong.

Non-blocking jobs

Requests return 202 with a job ID. A second backup for the same database queues behind the first, never in parallel and never dropped.

Row-level restore

Open a session on any backup, browse tables, pick rows, resolve foreign keys, and apply only those rows back.

Readable schedules

daily at 14:00 Asia/Kolkata becomes a cron trigger. Each database has its own schedule and target.

Live event stream

/logs/stream tails structured JSON over SSE: queued, started, completed, integrity_failed.

Signed webhooks

The same events go out as HMAC-signed HTTP calls, so other systems react without polling.

A dashboard

React UI for backups, restores, logs and settings. Dark by default, made to be read during an incident.

Quickstart

Two keys, one config file, one container.

Add this block to your existing docker-compose.yml. Nothing else in your project changes.

archon:
  image: archon:latest
  volumes:
    - ./archon.config.yaml:/app/config.yaml
    - ./backups:/app/backups
  ports:
    - "8765:8765"
  env_file: .env
Configuration

One block per database.

Each database gets its own schedule, storage target and retention mix. Postgres, MongoDB, SQLite and MySQL can sit in the same file.

archon.config.yaml
databases:
  - name: primary_postgres
    type: postgres
    host: postgres
    port: 5432
    db: mydb
    user: ${DB_USER}
    password: ${DB_PASSWORD}
    schedule:
      frequency: daily
      at: "14:00"
      timezone: "Asia/Kolkata"
    storage: s3
    retention:
      daily: 14
      weekly: 8
Good to know

${VAR} values come from the environment, so secrets stay out of the file.

Config is read once at startup. Call POST /reload to pick up changes without a restart.

max_parallel caps how many databases back up at once across the whole sidecar.

The full annotated reference is config.yaml.example.

Supported databases

Native tools, always drop-and-recreate on restore.

PostgreSQLpg_dump / pg_restore

Drops and recreates the database before restoring.

MongoDBmongodump / mongorestore

Restores with --drop.

MySQLmysqldump

Full dump, full restore.

SQLitehot file copy

Replaces the file wholesale.

API

A small REST surface.

Every route except /health needs the X-API-Key header.

MethodPathReturns
POST/backup202 and job ID(s)
GET/jobs/{job_id}Job status and result
POST/restore200, 400 or 500
GET/backupsBackup listing
GET/statusScheduler status per database
GET/logs · /logs/streamRecent logs, or a live SSE tail
DELETE/backups/{filename}Removes the file and its checksum
POST/reloadReloads config without a restart
POST/granular/sessionStarts a row-level restore session
GET/granular/session/{id}/tables · /table/{t}/rowsBrowse a backup
POST/granular/session/{id}/resolve-multi · /restore-multiResolve foreign keys, apply rows
DELETE/granular/session/{id}Closes the session
GET/healthLiveness probe, no key needed
Compared to others

Why a sidecar.

ArchonCron scriptManaged (RDS, Atlas)
Works with any Docker stackYesYesVendor-locked
Zero changes to your appYesUsually notYes
AES-256 encryption at restYesUsually skippedYou don't hold the key
Checksum verified before restoreYesNoOpaque
Many databases and targets, one configYesOne-off per DBOne provider
Row-level restoreYesNoNo
Self-hosted, you own the filesYesYesNo
Invariants

Rules that never change, by design.

Not in v1: Slack or email alerts (webhooks cover events), point-in-time recovery, per-tenant backups, config file watching, and job history beyond 24 hours in memory.

Backups nobody thinks about.

Until the day everybody is glad they exist. Add one block to your compose file and point it at your database.

← parthkomalwad.dev