Guides · CLI
— Guide
Pull the latest S3 backup by tag (CLI)
~6 min read · Sep 2026 · Commands match docs v0.0.10
You have dumps in an S3-compatible bucket. You want the newest file that matches a meaning — production, nightly, a particular database — without scrolling object listings or decoding timestamp prefixes by hand. This guide focuses on TagBackup’s pull, files, and tag expressions.
TagBackup stores tags in the object key, then lets you filter with AND / OR / NOT. That is different from AWS S3 object tags (metadata via the tagging API). Same English word; different mechanism.
When to use this
- Dumps (or other single files) were uploaded with TagBackup
pushand tags. - You want the newest match for a tag expression from a script or a laptop shell.
- You use AWS S3, Cloudflare R2, MinIO, Backblaze B2, or another S3 API, with a local bucket alias already configured.
When not to use this
- Objects were uploaded by other tools with arbitrary keys — TagBackup only considers keys matching its pattern; others are ignored.
- You need to query AWS object-tag metadata — use the AWS CLI / SDK tagging APIs instead.
- You need interactive browsing of many files — try
tagbackup serve, then fall back topullfor scripted “latest.”
Tags in the key (not AWS object tags)
On push you pass a comma-separated list:
tagbackup push dump.sql --bucket=dbbackup --tag=nightly,prod
Tags are alphanumeric ([a-zA-Z0-9]), sorted, and embedded in the key. With a prefix, an object might look like:
nightly/1776831788343-nightly,prod-dump.sql
There is no separate index database. Filtering reuses what is already in the key. That is intentional: portable, inspectable with any S3 browser, and boring in a good way.
Tag expressions
pull, files, and delete take --tag=EXPRESSION. No spaces. Operators:
| Token | Meaning |
|---|---|
a|b |
OR — either tag |
a+b |
AND — must have all tags |
-a |
NOT — must not have this tag |
(…) |
Grouping (highest precedence) |
Precedence: () → - → + → |.
Examples:
# Anything tagged prod
tagbackup files --bucket=dbbackup --tag=prod
# Must have both nightly and prod
tagbackup files --bucket=dbbackup --tag=nightly+prod
# nightly or weekly
tagbackup files --bucket=dbbackup --tag='nightly|weekly'
# Has release, does not have scratch
tagbackup files --bucket=dbbackup --tag='(-scratch)+release'
On push, tags are a comma list (--tag=nightly,prod). On pull/files/delete, use expressions (nightly+prod). Mixing those two forms is the usual source of confusion.
List before you pull
Non-interactive listing, newest first:
tagbackup files --bucket=dbbackup --tag=nightly+prod
Machine-readable:
tagbackup files --bucket=dbbackup --tag=prod --json | jq -r .key
See which tags exist in the bucket (with counts and date ranges):
tagbackup tags --bucket=dbbackup
Pull the latest match
tagbackup pull --bucket=dbbackup --tag=nightly+prod --latest
This downloads the single newest matching object into the current directory under its original filename.
Useful flags:
| Flag | Purpose |
|---|---|
--latest |
Newest match only; without it, an interactive chooser appears |
--output=PATH |
Destination path; omit for cwd; - writes to stdout (progress on stderr) |
--non-interactive |
Fail instead of prompting — use in scripts with --latest |
Pipe into a restore:
tagbackup pull --bucket=db --tag=maindb --latest --output=- | psql mydb
Save under a fixed name:
tagbackup pull --bucket=dbbackup --tag=nightly+prod --latest --output=./latest-prod.sql
Exit codes that matter for scripts
If nothing matches, TagBackup exits with code 5. Other documented codes include usage errors (2), config problems (3), and S3/network/auth failures (4). Always use --latest with --non-interactive in cron or CI so a missing dump fails loudly instead of hanging on a prompt.
set -euo pipefail
tagbackup pull --bucket=dbbackup --tag=nightly+prod --latest --non-interactive
Interactive choose (no --latest)
Omit --latest and TagBackup shows a paged chooser (20 at a time) with timestamp, size, filename, and tags. Handy when you want last Tuesday’s dump rather than the absolute newest.
Local web UI (brief)
tagbackup serve
# or: tagbackup serve --port 8080
# or: tagbackup serve --no-open
The embedded UI binds to 127.0.0.1 only, uses your existing config, and lets you browse buckets, filter by tag, upload, download a specific row, or delete. Row download saves that object by original filename — there is no “latest by expression” button. For that, use the CLI pull --latest.
End-to-end reminder
Server (or CI) pushes with tags; laptop pulls by expression:
# somewhere that produces dumps
pg_dump mydb | tagbackup push - --bucket=dbbackup --tag=nightly,prod --filename=dump.sql
# on your laptop
tagbackup pull --bucket=dbbackup --tag=nightly+prod --latest
Retention on the server side uses the same expressions:
tagbackup delete --bucket=dbbackup --tag=nightly+prod --older-than=30d --force
Common pitfalls
- Using commas in
--tagon pull — commas are forpush. Use+/|/-on pull. - Expecting AWS console “Tags” tab — those are S3 object tags; TagBackup’s labels are in the key.
- Forgetting
--non-interactivein scripts — prompts are fine for humans, fatal for cron. - Wrong bucket alias — aliases are local names in
config.yaml; verify withtagbackup bucket listandtagbackup bucket verify --bucket=ALIAS.
Further reading
- Website: tagbackup.com
- Full command reference: tagbackup.com/docs
- Source and releases: github.com/joncombe/tagbackup
- Related: Automate database dumps to S3/R2 with retention
Once tags are on the objects, “get me the latest matching dump” should stay a short command — that is the whole point.