Back to Blog

I Built My Own Backup System

I had a Node app that logged successful backups to Discord. Failures were silent. I found an eleven-day gap by accident. So I built something better.

I have a lot of projects running at any given time. Client work, side projects, internal tools. Each one has its own server, its own database, sometimes its own database engine. MySQL here, PostgreSQL there. Some servers run DirectAdmin, which handles its own backups and drops them in a folder - but nobody's picking those up and putting them somewhere safe.

I take data loss seriously. Not in the "yeah backups are important" way. In the way where if I lost a client's production database because I forgot to check whether the backup was still running, I'd never forgive myself.

So I had a backup system. A Node app I wrote that would run mysqldump on a schedule and push the files to storage. Every successful download got logged to a Discord channel. I'd see the messages scroll by and feel good about it.

Here's the problem with that setup: I only saw the successes.

If a backup failed, nothing happened. No message. No alert. The absence of a message at 2am isn't something you notice at 2pm. If the whole app stopped running, same thing. Silence. I found out a server hadn't been backed up in eleven days because I happened to be looking for an old dump and noticed the dates.

Eleven days. That's not a backup system. That's a logging script that makes you feel like you have a backup system.

What I Actually Needed

I sat down and listed what a real backup system needs to do. Not a fancy one. Just one that actually works.

It needs to back up every database on every server, on its own schedule. Some databases change hourly. Some change weekly. A flat "back up everything at midnight" doesn't fit.

It needs to tell me when something fails. Not log the success and hope I notice the gap. Actually tell me - email, Discord, something that interrupts me.

It needs to tell me when something succeeds but looks wrong. A backup that runs every day and produces the exact same file size for two weeks straight isn't healthy. Something's probably not writing to that database anymore, or the dump is silently truncating.

It needs retention rules. Keep dailies for 30 days, weeklies for 90. Different projects have different needs. A client project with compliance requirements gets longer retention than a side project.

It needs to store backups somewhere that isn't the same server as the database. Ideally multiple places. NAS at home, cloud storage, offsite.

And it needs to restore. Not "I can figure it out with the right commands." A button. Pick a backup, pick a target server, click restore. Because when you actually need to restore a backup, that's the worst possible time to be debugging a restore process.

Why Not Use Something Off the Shelf

I looked. The Laravel ecosystem has backup packages. There are SaaS backup services. They're fine for the simple case.

But I have already paid for storage. Two Synology NAS units, pCloud, rsync.net. I'm not adding another subscription on top of infrastructure I already own. The SaaS tools want you on their storage, their pricing tiers, their retention model.

And I have eight servers. Some run MySQL, some PostgreSQL. Some are behind slow connections where I need bandwidth throttling so the backup doesn't saturate the link. Some have databases with custom credentials that differ from the server-level defaults. Some are MySQL replicas where I need to check replication status before dumping. And some run DirectAdmin, which already does its own database dumps - I just need to grab those files and store them properly.

Every tool I looked at assumed one server, one database engine, one storage target. Or it assumed I'd write glue code to handle the rest. At that point I'm building a backup system anyway, just with someone else's abstractions in the way.

What I Built

A Laravel app with a Filament admin panel. It runs on its own server.

You add a server - hostname, SSH credentials, database credentials. The backup strategy is a composable pipeline: mysqldump, then compress with pigz. Or pg_dump, then compress. Or download an existing file from a folder where DirectAdmin already dumped it, then compress. The steps snap together. Adding a new backup type means registering a new step, not rewriting the strategy.

You add schedules - cron expressions, per-database or server-wide. Retention per schedule, per server, or global defaults. The most specific rule wins.

You add storage destinations - Synology NAS, pCloud, rsync.net over SFTP. Backups replicate to each destination independently. Each destination can have its own retention. A backup can expire from one destination after 30 days but live on rsync.net for a year.

The dashboard shows everything. Which backups ran, which failed, which are stale. A backup calendar. Health widgets. Unchanged size alerts. Discord and email notifications for failures.

And restore schedules. Pick a source database, a target server, a target database name. Run it on a cron, or trigger it automatically after every backup. That last one is the verification workflow - if the restore succeeds, the backup is good. If it doesn't, I hear about it.

The Part That Matters

I open the dashboard once a day. Green means everything ran. Red means something needs attention. I don't check Discord hoping I didn't miss a silent failure. I don't ssh into servers to verify dump files exist.

And watching the watcher: I have uptime monitoring pointed at the app's health check endpoint. The endpoint checks backup health - are any stale, are any failing. If the app goes down or backups are unhealthy, Discord tells me. If the monitoring service itself can't reach the endpoint, Discord tells me. There's no silent failure mode left.

The old Node app gave me confidence I didn't earn. This one gives me confidence I can verify.


That's the overview. The next post covers how the pipeline strategy pattern works - how composable backup steps let me handle MySQL, PostgreSQL, DirectAdmin file pickups, and everything else with the same architecture.

Share this article

Want to Work Together?

Let's discuss how I can help with your project.

Get in Touch