DEV Community

Alex Georgiev
Alex Georgiev

Posted on AI-assisted

Tigris marks every read with the region and cache layer that actually served it

Disclosure: I have no affiliation with Tigris. Nobody asked me to write this and nobody paid for it. I signed up and generated an access key like anyone else would.

I created a bucket with no region argument at all, put one object in it, then read it back and looked at the response headers instead of just the body. Three of them named something most S3-compatible services never tell you: X-Tigris-Regions: fra, X-Tigris-Served-From: fra, X-Tigris-Read-Source: cache. The read wasn't served from an abstraction, it was served from Frankfurt, from a cache layer, and the API told me so without being asked.

What Tigris is

Tigris is S3-compatible object storage built around one specific pitch: write from anywhere, read from anywhere, and the data follows access patterns globally instead of living in one region you have to pick upfront. It's a $25M Series A company (Spark Capital led, a16z participating, October 2025), founded 2021, and despite claiming 4,000+ customers, its dev.to and Hacker News presence is almost entirely other people's tutorials using it as an S3/R2 alternative inside broader posts, not dedicated coverage of the company itself.

Global by default, pinnable if you want it

Creating a bucket with the plain S3 API and no location constraint:

aws s3api create-bucket --bucket my-bucket --endpoint-url https://t3.storage.dev
Enter fullscreen mode Exit fullscreen mode
aws s3api get-bucket-location --bucket my-bucket --endpoint-url https://t3.storage.dev
Enter fullscreen mode Exit fullscreen mode
{
    "LocationConstraint": "global"
}
Enter fullscreen mode Exit fullscreen mode

global is the default, not a special mode you opt into. If you want the opposite, standard single-region behavior, that's one parameter away:

aws s3api create-bucket --bucket my-pinned-bucket \
  --create-bucket-configuration LocationConstraint=iad \
  --endpoint-url https://t3.storage.dev
Enter fullscreen mode Exit fullscreen mode
aws s3api get-bucket-location --bucket my-pinned-bucket --endpoint-url https://t3.storage.dev
Enter fullscreen mode Exit fullscreen mode
{
    "LocationConstraint": "iad"
}
Enter fullscreen mode Exit fullscreen mode

Both requests succeeded on the first try, no special account flag or enterprise-tier gate to reach either mode.

What the read path actually reveals

Back on the global bucket, head-object and get-object both return the same three headers on every response, not just the first one:

aws s3api get-object --bucket my-bucket --key test/object.txt /tmp/out.txt --debug 2>&1 | grep -oE "X-Tigris-[A-Za-z-]+': '[a-z]+'"
Enter fullscreen mode Exit fullscreen mode
X-Tigris-Read-Source': 'cache'
X-Tigris-Regions': 'fra'
X-Tigris-Served-From': 'fra'
Enter fullscreen mode Exit fullscreen mode

I ran that same request three times in a row and got the identical answer each time: fra, cache, fra. That's the honest limit of what a single test from a single network location can show, this confirms the metadata is real and consistently exposed, not that I've proven true global read-latency optimization, which would need requests actually originating from multiple regions to test properly. What it does prove is that the abstraction isn't a black box: if a read from your application looks slow, the response headers tell you which physical region and cache layer actually handled it, instead of leaving you to guess.

Presigned URLs work the way you'd expect and don't require the requester to have any credentials at all:

aws s3 presign s3://my-bucket/test/object.txt --expires-in 300
Enter fullscreen mode Exit fullscreen mode

Fetching that URL with a plain curl, no AWS credentials anywhere on that machine, returned the exact same bytes I'd uploaded.

What it gets wrong

The S3 API surface isn't complete, and where it's incomplete, the failure mode is more confusing than it needs to be. Trying to enable versioning on an existing bucket:

aws s3api put-bucket-versioning --bucket my-bucket --versioning-configuration Status=Enabled --endpoint-url https://t3.storage.dev
Enter fullscreen mode Exit fullscreen mode
An error occurred (BucketAlreadyExists) when calling the PutBucketVersioning operation:
The requested bucket name is not available. The bucket namespace is shared by
all users of the system. Specify a different name and try again.
Enter fullscreen mode Exit fullscreen mode

That error is for bucket creation, not versioning, on a bucket that already exists and that I already own. It reads like the versioning call got routed into the same handler as CreateBucket rather than into real versioning logic, or rejected into whatever error path was closest at hand. Either way, get-bucket-versioning before and after the failed call returned an empty response both times, so versioning is not silently on, but a user hitting this would spend real time debugging a bucket-naming problem that doesn't exist before concluding the feature just isn't there.

The zero-egress claim

Tigris's own pricing page states $0.02/GB/month for standard storage and zero egress fees, with the same rate everywhere rather than per-region pricing. I didn't generate enough traffic in a short test to see a real bill, so I can't independently confirm the egress number the way I confirmed the region headers or the versioning error, this is what their pricing page says, not something I measured. Worth stating plainly rather than repeating it as if I'd verified it myself.

Run it yourself

Sign up at tigrisdata.com, generate an access key from the dashboard, then:

export AWS_ACCESS_KEY_ID=your_key
export AWS_SECRET_ACCESS_KEY=your_secret
aws s3api create-bucket --bucket your-test-bucket --endpoint-url https://t3.storage.dev
aws s3api get-bucket-location --bucket your-test-bucket --endpoint-url https://t3.storage.dev
echo "test" > test.txt
aws s3api put-object --bucket your-test-bucket --key test.txt --body test.txt --endpoint-url https://t3.storage.dev
aws s3api get-object --bucket your-test-bucket --key test.txt out.txt --debug --endpoint-url https://t3.storage.dev 2>&1 | grep -i tigris
Enter fullscreen mode Exit fullscreen mode

Clean up when you're done:

aws s3 rm s3://your-test-bucket --recursive --endpoint-url https://t3.storage.dev
aws s3api delete-bucket --bucket your-test-bucket --endpoint-url https://t3.storage.dev
Enter fullscreen mode Exit fullscreen mode

If you're weighing other object storage

DigitalOcean Spaces and AWS S3 are the two most likely comparison points if you're evaluating this space, and they price the global-vs-regional tradeoff very differently from Tigris.

DigitalOcean Spaces charges $5/month for a base subscription covering 250GiB of storage and 1,024GiB of outbound transfer, with $0.02/GiB for additional storage and $0.01/GiB for additional egress beyond that. Egress is only free in specific same-region Spaces-to-Droplet pairs (for example Spaces in FRA1 to Droplets in FRA1), not globally, so a Spaces bucket is a regional resource you pay to move data out of once you leave that pairing.

AWS S3 Standard charges $0.023/GB for the first 50TB of storage per month in us-east-1, and egress to the internet is free only for the first 100GB per month (aggregated across all AWS services), then $0.09/GB for the next roughly 10TB, stepping down at higher volume tiers. Both storage and egress are priced per region, and moving data between AWS regions costs more again.

Tigris's pitch is specifically that neither of those tradeoffs applies: one global price, zero egress, and the region-pinning option still there if you genuinely need data to stay put. Whether that's worth it depends on how much of your bill today is egress versus storage, that's a question only your own traffic pattern can answer, not something a single test bucket can tell you.

Top comments (0)