Go back

Open source / Developer tooling

BullStash

A Go CLI for automated, observable database backups across local and cloud storage.

BULLSTASHBACKUP_017
$ bullstash backup postgres
  --storage s3 --compress
  --notify discord

backup complete · restore point verified

Backups become trustworthy when restore behavior is part of the same system.

I started BullStash after seeing how easily teams accumulate a different backup script for every database and environment. Configuration drifts, schedules become invisible, and the first restore test happens during an incident.

I used the project to design one command model across database engines and storage providers. The goal was not just moving bytes. I wanted schedules, failures, notifications, metrics, and restoration to behave predictably from the same interface.

What the repository can run today.

Database enginesPostgreSQLFULLMySQLFULLMongoDBFULLSQLiteFULL
Storage targetsLocalREADYAmazon S3READYGoogle CloudREADYAzure BlobREADY

A provider model that separates database capture from storage delivery.

BullStash normalizes configuration and lifecycle events while keeping database and object-storage adapters independent. A run composes capture, compression, transfer, retention, and notification without putting provider logic in the command layer.

That separation kept the CLI understandable as I added engines, storage targets, and observability without turning each command into a separate script.

  1. 01Capture databaserun.next()
  2. 02Compress and verifyrun.next()
  3. 03Transfer to storageprovider.put()
  4. 04Report and restorerestore.verify()
engineering-notes.md

The abstractions were the easy part. Trust was harder.

Separate capture from storage

Database code produces an artifact; storage code moves it. That keeps S3, GCS, Azure, and local delivery out of the database-specific commands.

Keep restore beside backup

I wanted recovery to use the same configuration and provider model. A green upload message is not proof that the data can be restored.

Expose the lifecycle

Schedules, metrics, and webhook notifications are part of the backup path because silent automation is difficult to trust during an incident.

BullStash connecting PostgreSQL, MySQL, MongoDB, and SQLite backups to cloud and local storage through capture, compression, transfer, and verification
Database adapters enter one backup lifecycle before storage and restore verification.
A backup is an operational promise, not a successful upload message.

I designed and built the Go command surface, provider abstractions, backup and restore lifecycle, observability hooks, and documentation as an open-source system.

  • I built BullStash as a Go CLI after getting tired of backup scripts that worked for one database, one bucket, and one machine.
  • I wrote the command surface and the database paths for PostgreSQL, MySQL, MongoDB, and SQLite, then added local and cloud storage delivery.
  • I added restore commands, scheduled runs, Discord notifications, Prometheus metrics, and a Grafana setup so a backup can be operated instead of merely started.
GoCobraPrometheusGrafanaS3Databases

Current state

Open-source work in progress with working full backup, restore, scheduling, and provider paths

Still on my list

What I would work on next.

  1. Add support for more databases and their engine-specific backup paths.
  2. Add more compression options and optimize backup speed, storage use, and restore performance.
  3. Make monitoring more complete across backup health, failures, restore readiness, and operator alerts.