How Taxiway handles backups

Lloyd Owen

1 September 2026 · 7 min read

“Managed backups” usually means your data goes through the vendor. That is a reasonable thing for a vendor to build and a strange thing to accept, so Taxiway does it the other way round: the dump happens on your host and uploads directly to your bucket, and the only thing that reaches us is a line saying it worked.

The path

Where a backup travels
Your VPS postgres still serving dump container short-lived · gzipped dump.gz local disk Taxiway agent takes the dump, then streams the upload itself one dump at a time, and it refuses to start if the disk is nearly full direct multipart Your S3 bucket your keys, your region Our control plane never sees the bytes backup_result { size, key, duration }

Everything in the top row happens inside your machine. The only thing crossing to us is the dashed line at the bottom, and it carries an object key, a byte count and a duration.

Why a separate container

The obvious way to dump a Postgres database is to run pg_dump inside the Postgres container. It works, and it is the wrong shape.

Instead the agent starts a short-lived container for the job, on the same network, and mounts a single directory into it at /taxiway-backup. The dump is written there as dump.sql.gz, gzipped as it is produced.

Three reasons this is worth the extra moving part:

Your database container stays exactly as you deployed it. No extra process, no memory spike inside the container your traffic depends on, and nothing added to the image.

The tool version can match the engine. The dump container is chosen for the database family being dumped, so a Postgres 17 database gets a Postgres 17 dump tool. Dumping a newer server with an older client is a classic way to produce a backup that only fails at restore time, which is to say the worst possible time.

The credentials come from the desired state, not from the wire. The agent already knows the spec that database is running with, because it is the thing that started it. It resolves the username and password from there rather than accepting them in the backup instruction. The control plane does not need to send a database password to trigger a backup, so a backup instruction is not a credential-bearing message.

Volumes get the same treatment, with a trick

For file volumes (uploads, media, anything on disk) there is no dump tool, so the agent archives the volume with tar from a busybox container.

Busybox specifically, rather than the service’s own image, because whether the image you deployed happens to contain tar is not something a backup system should be betting on. It is a couple of megabytes and pulls in seconds.

The trick is where it mounts. The volume under backup is always mounted at the same fixed path, /taxiway-source, regardless of where that volume actually lives in the running service. That means the resulting archive contains paths relative to that fixed root, with no trace of the original directory layout, so it can be restored into a document root at any path later. Archive a volume that lives at /var/www/html today and restore it into /app/storage next year, without unpicking the tar.

If you have ever tried to restore a backup and found it full of absolute paths from a server that no longer exists, you will know why this is in here.

The upload

Once the file is on disk, the agent uploads it, using the AWS SDK’s S3 manager: multipart, 32 MB parts, three parts in flight.

The connection is from your host to your bucket. Your credentials, your endpoint, your region, your retention policy. Any S3-compatible target works: AWS, Backblaze B2, Wasabi, Hetzner Object Storage, or a MinIO or SeaweedFS instance on another machine you own.

We are not a hop in that path. We could not read your dumps if we wanted to, because they are never sent to us.

The unglamorous safety rails

Backups are one of those features that are easy to demo and hard to make trustworthy. The ones that matter here are the ones that stop a backup from taking down the thing it is protecting.

One at a time, per host. A database large enough to be worth backing up is also large enough that two simultaneous dumps will hurt. The runner takes a lock. The second job waits.

A disk floor. The dump size is unknown until the dump is taken, and you cannot estimate it usefully beforehand. So the check is not an estimate, it is a floor: if the host has less than a gibibyte free, the backup refuses to start and says so. Failing fast and legibly on a nearly-full host is much better than filling it the rest of the way and taking the applications down with it.

A timeout that agrees with the other side. A single capture is bounded at four hours. That number is not arbitrary: the control plane treats a backup row that has not reported after six hours as failed, and the agent’s own limit has to sit comfortably inside that window. Otherwise you get the worst outcome available: a backup marked failed that is in fact still running and still writing to your disk.

A label the reaper respects. The dump container carries a system role label. Without it, the reaper that removes unmanaged containers every thirty seconds would delete the backup mid-dump, because a transient job is by definition not in the desired state.

What we actually store

When the job finishes, the agent sends one message back:

{
  "type": "backup_result",
  "payload": {
    "uid": "svc_9f1c…",
    "object_key": "taxiway/prod/postgres/2026-09-01T02-00-11Z.sql.gz",
    "size_bytes": 184219904,
    "duration_ms": 42618,
    "status": "ok"
  }
}

That is what the dashboard renders. A list of backups, their sizes, how long they took, and whether they worked. If you delete your Taxiway account tomorrow, every one of those files is still sitting in your bucket, in a standard format, restorable with psql and tar and no software of ours involved.

The point

The reason to run on your own server is usually some version of “I want to know where my data is”. A backup system that quietly routes every byte of your database through a third party undoes that, no matter how good the encryption story is.

So: the dump runs on your machine, the file lands on your disk, the upload goes to your bucket, and we get a receipt.


Backups are configured per service in the dashboard, and like everything else, they are free.

Start deploying in minutes.

Point Taxiway at a server you own, connect your repo, and push. That's the whole setup, and it costs nothing.