saved.sh
DocsPricingDownloadBlog

By source

  • Database backupsPostgres today, on any of the three paths.
  • Files and foldersDirectories on your own hardware. Local path only.
  • Anything you can scriptThe escape hatch, with no reduced guarantees.

By how it runs

  • Backups you driveCLI or API, driven by whatever you already run.
  • Backups on your infrastructureOur worker, your hardware. We never hold the credential.
  • Fully managed backupsWe execute and orchestrate. Nothing to host.
  • Compliance and custodyYour bucket, your account, our orchestration.

Understand it

  • How it worksThree paths, one artifact lifecycle
  • SecurityWhat we can and cannot see
  • CompareAgainst snapshots, cloud-native and scripts
Get started
saved.sh

External backups for the systems a business actually runs on.

A product of reops.

Product

  • Documentation
  • Solutions
  • Compare
  • How it works
  • Security
  • Pricing
  • Download

Developers

  • CLI
  • REST API
  • Recover

Company

  • Blog
  • Privacy
  • Terms

© 2026 saved.sh

Your data survives what holds it.

All postsEngineering

How to back up the output of any script

Every backup tool has a list of things it supports, and your thing is not on it. The honest answer is an escape hatch. If you can write a script that produces a file, that file can be backed up on a schedule like anything else.

8 August 2026·saved.shView as Markdown

Every backup product publishes a list of supported sources, and every list is missing something you run. An in-house service with a bespoke export. A SaaS with an API and no integration. A queue, a search index, a licence server, a thing somebody wrote in 2019 that nobody wants to touch.

The usual answer is "it is on the roadmap". The better answer is an escape hatch.

The contract

If you can write a script that produces a file, you have a backup source. The whole interface is one environment variable:

#!/usr/bin/env bash
set -euo pipefail

# SAVED_OUTPUT is a path. Write your backup there and exit zero.
curl --fail --silent --show-error \
  -H "Authorization: Bearer $VENDOR_TOKEN" \
  https://api.vendor.example/v1/export \
  > "$SAVED_OUTPUT"

That is the entire integration. Point a backup at that script, give it a schedule, and it now has the same retention, the same encryption, the same delivery to your own bucket and the same run history as a Postgres source.

Three rules the contract enforces

Write to $SAVED_OUTPUT, not stdout. Anything on stdout is treated as a log, which means it shows up in the run record where it is useful for debugging and does not end up inside your backup. A script that prints its data instead of writing it produces a zero-byte artifact.

An empty file is a failure, not a success. If the script exits zero and $SAVED_OUTPUT is empty, the run fails and tells you so, rather than archiving nothing and going green. This is the single most valuable line in the whole feature, because "exited zero, produced nothing" is the most common way a homemade backup lies to you.

Exit non-zero to fail the run. Use set -euo pipefail and let the shell do it. A curl that 404s inside a pipe will not fail the script without it, which is how people end up archiving an HTML error page.

The failure this prevents

The classic homemade backup writes curl ... > backup.json and never checks anything. When the token expires, curl writes a JSON error body, the file has a plausible size, and the cron job is green. Six weeks later somebody tries to use it. The --fail flag and the empty-output check are what turn that into an alert on day one.

What it is good for

The pattern is worth reaching for in more cases than people expect:

SituationThe script
SaaS with an export API and no integrationcurl the export endpoint
A database we do not supportIts own dump tool, redirected to $SAVED_OUTPUT
Several things that belong togethertar them into one artifact
A dump that needs pre-processingDump, filter out the two tables you cannot store, then write
Compliance evidenceRun the report, save the output, keep it under a retention lock

The fourth row is the interesting one. Because you control the script, you can strip what must not be stored before it is ever archived, which is much easier than deleting it from artifacts afterwards.

1env var

The entire interface

0

Integrations you have to wait for

0bytes

What silently passes as success

Where it runs, and why that matters

Script sources run on a worker on your own machine, not on ours. That is a deliberate constraint rather than a limitation we have not got to yet.

A script is arbitrary code with access to whatever credentials it needs. Running that on our infrastructure would mean you hand us the credentials and trust us to execute your code, which is a much larger ask than "back up this database". So the script runs where you already have the access, and we never see the token, the code, or the bytes.

The honest caveat

An escape hatch is not the same as an integration. You own the script: when the vendor changes their export endpoint, your backup breaks and you fix it.

What you get in exchange is that you are never blocked. Everything around the script, the scheduling, retries, retention, encryption, delivery to your own bucket and a record of what each run produced, is the same machinery the built-in sources use. Only the fifteen lines that produce the file are yours.

Read next

Backup vs replication vs snapshot

Replication copies your mistake faithfully and instantly. Snapshots live inside the system they protect. Only one of the three survives the thing you are actually afraid of.

Backups that survive the thing that took out production.

How it worksStart free