Replacing My Backup Container with 80 Lines of Bash

21 March 2026

I was using kartoza/pg-backup for my PostgreSQL backups. It mostly worked, until it didn’t. Environment variables weren’t accessible inside cron jobs, and if you’ve ever debugged cron-in-Docker issues, you know the kind of afternoon that leads to.

So I wrote a replacement. About 80 lines of bash. It does four things: dump the database, upload to S3, clean up old backups, and ping a heartbeat URL. That’s it.

Instead of cron, it uses a while true loop that checks the clock every 60 seconds:

while true; do
  CURRENT_HOUR=$(date -u +"%H" | sed 's/^0//')
  CURRENT_MIN=$(date -u +"%M")

  for HOUR in $SCHEDULE; do
    if [ "$CURRENT_HOUR" -eq "$HOUR" ] && [ "$CURRENT_MIN" -eq "0" ]; then
      run_backup
    fi
  done

  sleep 60
done

It’s dumb and it works.

The whole thing runs as a Kamal accessory, so it deploys alongside the app. Triggering a manual backup is just bin/kamal accessory exec db-backup --reuse "/backup.sh --once". Checking logs, listing S3 backups, restoring — all straightforward.

I spent more time debugging the “proper” backup container than I spent writing this entire replacement. Sometimes the best solution is the boring one.

The full setup is on GitHub if you want to grab it.

Jankees van Woezik profile picture

Hello, I'm Jankees van Woezik

Like this post? Follow me on X (@jankeesvw)