
When we introduced Apache Iceberg branching in Starburst, it gave data engineers the ability to safely isolate and test changes before merging them into production. Branching is the mutable, evolving side of version control. But sometimes you need the opposite, an immutable bookmark that never moves.
That’s exactly what Iceberg tags provide. If branches are like Git branches, pointers that advance with every new commit, then tags are like Git tags. They offer fixed references to a specific point in time. They let you say “this exact state of the data matters” and guarantee it stays accessible, no matter what happens to the table afterward.
Tags unlock several practical workflows:
- Regulatory snapshots: Capture the state of a table at the close of a reporting period so auditors can always query the same data.
- Release milestones: Mark a known-good state before a large migration or backfill, so you can compare results or roll back if needed.
- Reproducible analysis: Pin the exact dataset used for a model training run or a quarterly business review, making results reproducible months later.
- Snapshot protection: Prevent Iceberg’s snapshot expiration from garbage-collecting a snapshot you still need, by attaching a retention policy to the tag.
What is a Tag in Apache Iceberg?
Apache Iceberg organizes a table’s history as a series of snapshots — immutable captures of the table’s state after each write operation. On top of these snapshots, Iceberg provides two kinds of named references (see the official Iceberg branching and tagging documentation at for the full specification):
- Branches are mutable pointers that advance as new data is written. The “main” branch is the default, and you can create additional branches to isolate work.
- Tags are immutable pointers fixed to a single snapshot. Once created, a tag always resolves to the same data, regardless of subsequent writes to the table.
Both branches and tags have a maximum reference age property that controls when the reference itself is cleaned up by expire_snapshots. Tags additionally protect their underlying snapshot from expiration for the duration of their retention period.
This distinction is important. A branch evolves; a tag is a bookmark. Together, they give you full version-control semantics over your data.
Working with Tags
Starburst extends the existing CREATE BRANCH SQL syntax with a type property, so you can create and manage tags without any additional SQL grammar. Let’s walk through the key operations.
Creating a tag
The simplest form creates a tag pointing to the table’s current snapshot:
CREATE BRANCH audit WITH (type = 'TAG') IN TABLE lakehouse.sales.transactions;
You can query the tagged data at any time using standard time-travel syntax:
SELECT * FROM lakehouse.sales.transactions FOR VERSION AS OF 'audit';
Creating a tag with a retention policy
By default, Iceberg’s snapshot expiration can eventually remove the snapshot a tag points to. To protect it, set a retention period:
CREATE BRANCH end_of_q1 WITH (type = 'TAG', retention = '90d') IN TABLE lakehouse.sales.transactions;
This ensures the underlying snapshot is retained for at least 90 days, even if expire_snapshots runs in the meantime.
Tagging a specific snapshot
Sometimes you want to tag a historical state, not the current one. You can target a specific snapshot by its ID:
CREATE BRANCH before_migration WITH (type = 'TAG', snapshot_id = 4523895722344515212) IN TABLE lakehouse.sales.transactions;
You can find snapshot IDs by querying the table’s metadata:
SELECT snapshot_id, committed_at FROM "lakehouse.sales.transactions$snapshots" ORDER BY committed_at DESC;
Creating a tag from a branch
If you’ve been working on a named branch and want to bookmark its current state before making further changes:
CREATE BRANCH source IN TABLE lakehouse.sales.transactions; -- ... write data to the source branch ... CREATE BRANCH pre_review WITH (type = 'TAG') IN TABLE lakehouse.sales.transactions FROM source;
The tag captures the branch’s state at the moment of creation. Subsequent writes to source – or even dropping the branch entirely – do not affect the tag.
Replacing a tag
To move a tag to a new snapshot, use CREATE OR REPLACE:
CREATE OR REPLACE BRANCH audit WITH (type = 'TAG') IN TABLE lakehouse.sales.transactions;
This works whether or not the tag already exists, making it idempotent and safe to use in automated pipelines.
Dropping a tag
When a tag is no longer needed:
DROP BRANCH audit IN TABLE lakehouse.sales.transactions;
Practical Example: Auditable Data Pipeline
Let’s walk through a realistic scenario. You operate a daily data pipeline that loads transactions into an Iceberg table. At the end of each quarter, regulators need to query the exact data that was present at the close of business.
-- Create the transactions table CREATE TABLE lakehouse.sales.transactions ( tx_id BIGINT, amount DECIMAL(10, 2), tx_date DATE ) WITH (partitioning = ARRAY['tx_date']); -- Daily pipeline loads data INSERT INTO lakehouse.sales.transactions VALUES (1, 150.00, DATE '2025-03-31'), (2, 230.50, DATE '2025-03-31'); -- End of Q1: create an immutable tag with 365-day retention CREATE BRANCH q1_2025_close WITH (type = 'TAG', retention = '365d') IN TABLE lakehouse.sales.transactions; -- Pipeline continues -- new data arrives in Q2 INSERT INTO lakehouse.sales.transactions VALUES (3, 420.00, DATE '2025-04-01'), (4, 89.99, DATE '2025-04-02'); -- Months later, an auditor queries exactly what was present at Q1 close SELECT * FROM lakehouse.sales.transactions FOR VERSION AS OF 'q1_2025_close';
Result:
tx_id | amount | tx_date -------+--------+------------ 1 | 150.00 | 2025-03-31 2 | 230.50 | 2025-03-31
The tag guarantees this result is reproducible for at least a year, regardless of how much new data is loaded or how many snapshots are expired in the meantime.
Historical Tags: A Tiered Retention Strategy
The Iceberg documentation describes a powerful pattern: using tags with different retention periods to implement a tiered snapshot retention policy. The idea is to keep more granular history for recent data and progressively coarser history as you go further back.
Here’s how this looks in practice with Starburst:
-- Weekly snapshot: retain for 1 month CREATE BRANCH eow_04_14 WITH (type = 'TAG', retention = '30d') IN TABLE lakehouse.sales.transactions; -- Monthly snapshot: retain for 6 months CREATE BRANCH eom_march_2025 WITH (type = 'TAG', retention = '180d') IN TABLE lakehouse.sales.transactions; -- Annual snapshot: retain with a long retention period CREATE BRANCH eoy_2024 WITH (type = 'TAG', retention = '3650d') IN TABLE lakehouse.sales.transactions;
When expire_snapshots runs, it respects tag retention: tagged snapshots are preserved for the specified duration, while untagged snapshots are cleaned up according to the table’s default retention policy. This gives you a natural way to balance storage costs against historical access needs — keep fine-grained weekly checkpoints for recent troubleshooting, monthly milestones for quarterly reviews, and annual markers for long-term compliance.
Tags vs. Branches: When to Use Which
| Aspect | Branch | Tag |
| Purpose | Isolate in-progress work | Bookmark a fixed point in time |
| Mutable? | Yes — advances with writes | No — always points to same snapshot |
| Use case | ETL staging, what-if analysis, Write-Audit-Publish (WAP) | Audits, compliance, reproducibility |
| Retention | Follows table defaults | Can set explicit retention policy |
| Query | SELECT … FOR VERSION AS OF ‘branch’ | SELECT … FOR VERSION AS OF ‘tag’ |
A good mental model: use branches for work in progress, and tags for work that’s done and needs to be preserved.
One subtle difference worth noting: when you query a tag, Iceberg uses the snapshot’s schema — the schema that was in effect when the snapshot was created. This means that if you add or drop columns after creating a tag, querying the tag still returns data in its original shape. This is exactly the behavior you want for audit and compliance use cases, where the historical record must reflect the data as it actually existed.
Listing Tags
Tags created through branch syntax appear alongside branches in the SHOW BRANCHES statement:
SHOW BRANCHES IN TABLE lakehouse.sales.transactions;
Result:
Branch ---------------- main q1_2025_close
You can also inspect detailed reference metadata — including type, snapshot ID, and retention — through the $refs metadata table:
SELECT name, type, snapshot_id, max_reference_age_in_ms FROM "lakehouse.sales.transactions$refs";
Why Iceberg tagging matters
Tags complete the version-control story for Apache Iceberg tables in Starburst. Where branching gives you a safe space to experiment, tagging gives you a permanent record. Together, they bring the same disciplined version management that software engineers rely on every day to the world of data engineering.
Whether you need to satisfy auditors, reproduce a machine learning experiment, or simply protect a critical snapshot from expiration, Iceberg tagging in Starburst provides a straightforward, SQL-native way to do it.
Want to know more about Iceberg and Starburst? Download the The Data Engineer’s Guide to Apache Iceberg v3.



