VPS Deploy & Backup Kit

Ship a small app on one server with no failed requests, and back it up in a way you have actually restored.

Buy now: $19 One-time purchase · v0.3.0 · instant download via Polar

VPS Deploy & Backup Kit cover
Output from a real test run, not a mock-up.

What you get

Three small bash scripts, one shared alert helper and the config files to wire them up. Read them in ten minutes; there is nothing to install except the tools they call.

FileWhat it does
deploy.shClones a release, builds, health-checks and rolls back on failure. Two modes: restart (symlink plus pm2 restart) and bluegreen (zero failed requests in the test below). deploy.sh rollback flips back in blue/green mode.
backup.shDumps SQLite and Postgres databases safely, then backs up with restic (7 daily, 4 weekly, 6 monthly by default) and checks the repository. A corrupt dump stops the run before any snapshot is made.
restore-test.shRestores the latest snapshot to a temporary directory, verifies the database dumps, runs your own optional check and reads a sample of the data.
notify.shSends a Telegram message or a webhook POST when any script fails. Silent on success.
Configkit.env.example, Caddyfile.example, ecosystem.config.js, cron.example

Zero-downtime deploys, measured

In bluegreen mode the new release starts on the idle port, is health-checked directly, Caddy is switched to it with a graceful reload, and only then does the old release stop. A release that is unhealthy never receives traffic.

Test setup: Debian 12 container, Caddy 2.6.2 from apt, pm2, a Node app that needs 1.5 seconds to start, a load of 20 requests per second through Caddy for the whole deploy (every fifth request is a 300 ms endpoint). A request counts as failed on a connection error, a timeout over 3 seconds, or any status other than 200.

ScenarioRequestsFailedDeploy time
restart mode, good release11632 (all HTTP 502)2.4 s
restart mode, broken release (rolled back)38067 (all HTTP 502)16.8 s
bluegreen, good release21107.6 s
bluegreen, broken release (health returns 500)373016.6 s
bluegreen, release that crashes on start371016.5 s
bluegreen, rollback20807.5 s

In the good blue/green run the load saw both versions (115 answers from v1, then 96 from v2), so traffic really moved. Over several runs the restart mode failed 31 to 35 requests and blue/green failed none.

Backups you have actually restored

  • Databases are dumped properly. SQLite uses its online backup and an integrity check; Postgres uses pg_dump -Fc and pg_restore -l. Copying a live database file is how backups end up corrupt.
  • A bad dump stops the run. In the test a corrupted SQLite file aborted the backup without creating a snapshot.
  • Old dumps cannot leak in. A database you removed from the config does not keep sneaking into new snapshots.
  • restore-test.sh proves it. It restores, checks the dumps and runs restic check on a sample. Run it weekly from cron.
  • Remote repository. sftp: repositories work with both scripts (tested against a local OpenSSH server).

Failure alerts

Set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID, or NOTIFY_WEBHOOK_URL. A failing deploy, backup or restore test sends exactly one message per channel; a successful run sends nothing. The token is passed to curl on stdin and never printed, and an unreachable notification API does not change the script's exit code.

Setup in five steps

  1. Install restic, git, curl and pm2 (plus caddy for blue/green, sqlite3 or postgresql-client for those databases).
  2. Copy the folder to the server and cp kit.env.example kit.env, then edit it.
  3. Put the repository password in a mode-600 file.
  4. Run ./backup.sh, ./restore-test.sh, then ./deploy.sh.
  5. Add the cron lines from cron.example.

How it was tested

59 checks run in a clean Debian 12 container (Node 22, restic, PostgreSQL 15, Caddy 2.6.2 and OpenSSH from apt, pm2). They cover: backup of folders, SQLite and Postgres, restoring and comparing rows, wrong repository, corrupt database, missing dump, deploy and rollback in both modes under load, alerts against a local fake API, and restic over SFTP.

Who it is for

For solo developers and small teams running one or two Node or Go apps on a single Linux VPS who want deploys and backups they can trust without adopting Kubernetes.

Requirements

Linux, bash, pm2, restic, and Caddy for blue/green. Your app reads its port from PORT. Blue/green runs two releases side by side for a few seconds, so database migrations need to be backward compatible.

FAQ

Do I need the kit to get zero-downtime deploys?

No. The technique is described in this post with the core commands. The kit packages it with health checks, rollback, state handling, alerts and the test suite.

Which stack does it assume?

Linux, bash, pm2, Caddy for blue/green, restic for backups, and an app that reads its port from the PORT environment variable.

Does it work with Postgres and SQLite?

Yes, both are dumped and verified.

Can I use it for several apps?

One kit.env per app; set KIT_CONF to choose the file.

What is the license?

Use it on your own servers. Do not resell the files.

Changelog

  • 0.3.0: blue/green deploys with rollback, failure alerts (Telegram and webhook), SFTP repositories tested, all messages in English.
  • 0.2.0: SQLite and Postgres dumps in backup.sh, dump verification in restore-test.sh.
  • 0.1.0: first release.

Read more

Get it

Buy now: $19

Payment, tax and invoices are handled by Polar as merchant of record. You receive a download link after checkout.