This is where our images
actually live.
Iceberg is a small, self-hosted CDN we run on top of Cloudflare R2.
Every image used across katalyst-crm.com
is stored here and served straight from Cloudflare's edge network — no per-GB bandwidth bill, no vendor lock-in.
Quick start
Three ways to use Iceberg — pick the one that matches the work you're doing.
Drop the URL straight into any <img>, <video>, CSS background-image, or fetch — no auth required.
<img src="https://cdn.katalyst-crm.com/t1/cloudinary/sample.jpg" alt="…" />
Convert an existing Cloudinary URL: drop res.cloudinary.com/dcuswyfur/image/upload/<transforms>/v123/ and prepend cdn.katalyst-crm.com/t1/cloudinary/.
CLI reference
Every iceberg subcommand.
| Command | Purpose |
|---|---|
| iceberg login | Authenticate and cache an API token at ~/.iceberg/config.json |
| iceberg whoami | Show the account behind the saved token |
| iceberg config show | Print saved url + token + tenant + cdn |
| iceberg config set --cdn X | Pin the public CDN base URL |
| iceberg upload <file> --key K | Presigned PUT directly to R2; promotes catalog row to ready |
| iceberg ls --limit N | List assets in your tenant |
| iceberg url <key> | Print the public CDN URL for embedding |
| iceberg rm <key> | Delete both the storage object and the catalog row |
| iceberg migrate cloudinary | Bulk import from a Cloudinary account (dry-run by default) |
How it's built
The architecture is intentionally boring. Each layer has one job.
Cloudflare R2
Object storage that's S3-compatible and bills $0.015/GB stored. Egress to the internet via Cloudflare's edge is free.
cdn.katalyst-crm.com
Custom domain on the R2 bucket. Every read goes through Cloudflare's edge cache. The API is never in the byte path.
Go API + Postgres
Small Go binary on Railway. Owns auth, tenant policy, presigned-URL minting, and the metadata rows. Egress here stays flat regardless of image traffic.
Direct-to-R2 presigned
Client → R2 directly. The API hands out a one-time signed URL and verifies completion. A 5 GB upload costs the same in server bandwidth as a 5 KB one.
Common questions
What if a URL returns 404?
Most likely you're missing the /t1/cloudinary/ path prefix, or the page hasn't had its old Cloudinary URLs swapped yet. Paste the URL into Slack and someone can confirm the asset is in the bucket.
Can I resize an image on the fly?
Not yet. Phase 3 of the build wires up Cloudflare Image Transformations so URLs like cdn-cgi/image/width=800,format=auto/<key> serve the right variant at the edge.
What does this cost monthly?
Around $5/month total — fixed compute for the API + Postgres + a few cents of R2 storage. The bill does not scale with image traffic; that's the whole point of the architecture.