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 push and 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 to pull for 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

  1. Using commas in --tag on pull — commas are for push. Use + / | / - on pull.
  2. Expecting AWS console “Tags” tab — those are S3 object tags; TagBackup’s labels are in the key.
  3. Forgetting --non-interactive in scripts — prompts are fine for humans, fatal for cron.
  4. Wrong bucket alias — aliases are local names in config.yaml; verify with tagbackup bucket list and tagbackup bucket verify --bucket=ALIAS.

Further reading

Once tags are on the objects, “get me the latest matching dump” should stay a short command — that is the whole point.