A modern web console to manage, inspect and maintain Apache Iceberg™ tables across multiple catalogs.
Browse tagged catalogs → inspect a table's metadata, snapshots, storage & timeline → run a Spark maintenance action and watch its result + logs. ▶ MP4 version
⚠️ Independent community project — not affiliated with or endorsed by the Apache Software Foundation. "Apache", "Apache Iceberg", "Iceberg" and "Apache Polaris" are trademarks of the ASF.
IceGuard is an admin & maintenance console for the Apache Iceberg lakehouse. Point it at one or more catalogs (REST, Nessie, Polaris) and get a single UI to browse namespaces and tables, evolve schemas and partitions, edit properties, sample data, inspect storage layout, view lineage, and run (or schedule) maintenance like snapshot expiration and data-file compaction.
It is not a catalog — it sits on top of your existing Iceberg catalogs.
IceGuard is two components:
- Frontend — the React/TypeScript web UI.
- Backend — the Quarkus REST API.
It has one required dependency: a PostgreSQL database (you provide it), where the backend stores IceGuard's own state — registered catalogs, pipelines, schedules, alerts and execution history. Postgres is not part of IceGuard; it's infrastructure IceGuard needs to run, like a JVM.
IceGuard then connects to your own Iceberg catalog(s) and object store.
The
docker-compose.ymlin this repo (MinIO, an Iceberg REST Catalog, Nessie, Apache Polaris, optionally Spark) is only a local test sandbox so you can try IceGuard end-to-end without bringing your own infrastructure. It is not part of the product — in production you point IceGuard at your existing catalogs and storage.
- Multi-catalog — register and switch between REST, Nessie, Polaris and Unity Catalog (None/Bearer/OAuth2 supported).
- Namespaces & tables — browse the tree, create namespaces, create/drop/rename tables, insert sample rows.
- Schema editor — add / rename / retype / re-doc / drop multiple columns, applied in a single commit.
- Properties editor — add / update / remove table properties in a single commit.
- Partition evolution — add or drop partition fields (identity, bucket, truncate, year/month/day/hour).
- Storage tab — point-in-time storage state: totals, file-size histogram, per-partition aggregates (server-side paginated), and file drill-down.
- Lineage / history — schema-version history with column diffs, and a visual snapshot-to-snapshot diff.
- Maintenance — expire snapshots, rewrite data files, rewrite manifests, remove orphan files, rollback.
Pluggable executors: a Java API executor (analyse) and a Spark executor (real
rewrite_data_files, locallocal[*]or a remote Spark cluster). - Pipelines — chain maintenance actions with per-action parameters and a cron schedule (Airflow-style run view).
- Alerts — threshold rules on table metrics with optional SMTP email notifications.
- Timeline — snapshots + executions on one timeline, click any item for its output/logs.
Any Iceberg REST-compatible catalog works. The Add Catalog wizard shows a card per engine, and the type is auto-detected from the catalog name/URI:
Unity Catalog (Databricks or the OSS unitycatalog.io) is reached through its Iceberg REST
Catalog API — browse works today; writes depend on the endpoint (Databricks managed-Iceberg
read/write is in preview, requires EXTERNAL USE SCHEMA + a PAT/OAuth token).
Nessie has a few specifics (snapshot history rebuilt from its commit log, S3 region requirement, flaky-remote handling) — see docs/NESSIE.md.
| Layer | Tech |
|---|---|
| Frontend (product) | React, TypeScript, Vite, Tailwind CSS, shadcn/ui, TanStack Query, Zustand, Recharts |
| Backend (product) | Java 21, Quarkus 3.17, RESTEasy Reactive, Hibernate Panache, Flyway, Apache Iceberg 1.7 |
| Required dependency | PostgreSQL (backend state) |
| Test sandbox only | Docker Compose: MinIO (S3), Iceberg REST Catalog, Nessie, Apache Polaris, (optional) Spark |
Requirements: Docker + Docker Compose. (For hot-reload development: JDK 21 & Maven, Node 20+.)
The app is two services — frontend + backend — plus a required Postgres.
docker-compose.yml bundles them in three tiers via Compose profiles:
| Level | Command | Services | When |
|---|---|---|---|
| 1 — app (official) | docker compose up -d --build |
frontend + backend | Bring your own Postgres (ICEGUARD_DB_URL in .env) |
| 2 — + database | docker compose --profile db up -d --build |
+ Postgres | Self-contained app + its required DB |
| 3 — + test sandbox | docker compose --profile db --profile sandbox up -d --build |
+ MinIO + REST catalog | Try everything locally |
No local build? Use the published images from GHCR instead — same levels, just point at the images file (no
--build):docker compose -f docker-compose.images.yml --profile db --profile sandbox up -dDefaults to
:latest; pin a release withTAG=0.1.0 docker compose -f docker-compose.images.yml ….
For the quickest tour, use Level 3 and open http://localhost:8090.
In the UI, go to Catalogs → Add Catalog and register the bundled Iceberg REST catalog:
| Field | Value |
|---|---|
| Catalog type | Iceberg REST |
| URI | http://rest-catalog:8181 (Docker service name — not localhost; the backend reaches it over the Compose network) |
| Warehouse | s3://warehouse/rest/ |
| Authentication | None |
| S3 / Storage access | Static keys |
| • Endpoint | http://minio:9000 |
| • Access key / Secret key | minioadmin / minioadmin |
| • Region | us-east-1 |
| • Path-style access | enabled ✅ |
The S3 settings let the backend read/write data files on the sandbox MinIO. Once added, create a namespace and a table to see browsing, schema, storage and maintenance in action.
Tear down with docker compose --profile db --profile sandbox down (add -v to wipe data)
— use -f docker-compose.images.yml in the same command if you started from the images file.
| Service | URL | Notes |
|---|---|---|
| Frontend (UI) | http://localhost:8090 | nginx; proxies /api to the backend |
| Backend API | http://localhost:8080 | Swagger at /q/swagger-ui |
| MinIO console | http://localhost:9001 | minioadmin / minioadmin (sandbox profile) |
| Iceberg REST catalog | http://localhost:8181 | sandbox profile; register it as http://rest-catalog:8181 |
Run the dependencies in Docker and the app from source:
# deps only (Postgres :5433, REST catalog :8181, Nessie :19120, MinIO) — not the backend container
docker compose -f docker-compose.dev.yml up -d postgres rest-catalog nessie minio minio-init
cd backend && mvn quarkus:dev -Dquarkus.profile=docker # :8080 (Swagger: /q/swagger-ui)
cd frontend && npm install && npm run dev # :5173 (proxies /api to :8080)For every catalog type at once (REST + Nessie + Polaris) and real Spark compaction:
docker compose -f docker-compose.dev.yml up -d
./scripts/seed-catalog.sh # after the backend is upThe REST Catalog and Nessie demo catalogs work out of the box on MinIO.
Polaris writes require real AWS S3 — copy .env.example to .env.
Deploy the frontend and backend, point the backend at a PostgreSQL you provide
(QUARKUS_DATASOURCE_JDBC_URL / username / password), then add your own catalog(s) from the UI
(Catalogs → Add Catalog): REST / Nessie / Polaris URI, warehouse, auth (None / Bearer / OAuth2)
and, if needed, S3 credentials. No MinIO/Spark/sandbox containers required.
IceGuard (the product) required dep your existing systems
┌───────────────────────────────┐
│ React + Vite (UI, :5173) │
│ │ │
│ Quarkus REST API (:8080) ──────┼──> PostgreSQL ─────────┐
└───────────────────────────────┘ (IceGuard state) │
│ │
└──────────────────> Iceberg catalogs (REST / Nessie / Polaris)
└──> S3 / object store (data & metadata)
IceGuard's own state (registered catalogs, pipelines, schedules, alerts, execution history) lives in the PostgreSQL you provide; table data & metadata live in your object store via your Iceberg catalogs.
backend/ Quarkus REST API (com.iceguard.*)
frontend/ React + TypeScript SPA
scripts/ seed + helper scripts
docker-compose.yml app stack (frontend + backend; profiles: db / sandbox)
docker-compose.dev.yml advanced multi-catalog sandbox (Nessie, Polaris, Spark)
- The Java executor really runs compaction & orphan-removal, but only on small, append-only tables (hard size/file-count limits; refuses above them) — use the Spark executor for large or merge-on-read tables.
- Polaris + MinIO: browsing works, but writes fail (Polaris ignores the S3-compatible endpoint) — use real AWS S3.
- Catalog credentials are stored in PostgreSQL (not encrypted at rest) — treat the DB as sensitive.
- Test coverage is early-stage. Contributions very welcome 🙂
Contributions, issues and ideas are welcome! Please read CONTRIBUTING.md and our
Code of Conduct. Good first issues are labelled good first issue.
How the pipelines and automation work (CI, releases, Dependabot, CodeQL, Trivy) is documented in docs/CI.md.