Why are there no public buckets on Railway?
I’ve just released Storage Buckets on Railway. Right now, you can’t make them public. Some users are rightfully asking why we would only launch with private buckets, and were bummed to see they’re not public. So I wanted to give more insights because I always enjoy these “peeking behind the curtain” stories. I think this stuff is always pretty interesting!
Why I worked on buckets in the first place
Adding Storage Buckets was my onboarding project at Railway. When you start as an engineer here, the final three weeks of your onboarding phase are reserved for building whatever you want to add to the platform. It’s a pretty neat opportunity to ship something impactful right out of the gate.
I did some user research and saw that Object Storage had been requested numerous times in the forums. I even found reviews and YouTube videos complaining about the lack of built-in buckets. So that’s what I landed on!
Cutting Scope
Three weeks isn’t a lot of time to go from “no buckets” to “users can run them and pay for them.”
The first few days of working on it were very overwhelming. I had no idea where to start, and I saw the hours passing without any real progress. My head was just full of questions, from how tf the infrastructure will work to how pricing should work.
So I went for a long walk to think things through.
One of the first things that came to my mind was static sites. Public buckets are great for static sites, and I’m sure users want to use them for it. But hosting a static site on Railway is already easy: connect your GitHub Repo, we detected it as a static site and deploy it inside a Caddy server. Sure, going entirely serverless would be great, but I would spend a substantial amount of time implementing a use case that is already mostly solved.
I thought about more things (which I will explain below), and came to the conclusion that I need to significantly cut scope to ship at least anything. Because shipping anything is better than nothing. And shipping it quickly is even better.
I jotted down my decisions to cut scope into my Discord channel, got positive feedback, wrote an RFC and started building.
You can use a private bucket to view files publicly
Even with private buckets, we can still support use cases that require sharing files publicly through presigned URLs or proxying. To make this easier for developers, I added two templates: one for a small service that exposes a public URL and redirects it to a presigned URL, and another one for an image proxy that exposes a public URL and optimizes images on the fly.
Because we can still support these usecases with a private bucket, it allowed me to cut down the scope significantly while still delivering a ton of value.
You might just need a private bucket
Let’s look at a common use case and a popular framework: uploading user-generated content like images using Next.js. Love it or hate it, it’s what a lot of people use. Let’s assume a support app where users upload a profile picture and attach a screenshot to a support ticket. Then you use Next.js’s <Image> component, it automatically optimizes the image, and… you have a proxy. In this case, it doesn’t really matter if the bucket itself is public or private.
I have a hypothesis that a fair amount of public buckets out there should actually be private. It’s not uncommon to hear about security incidents involving a bucket that was accidentally left open to the public.
It’s easy to create a bucket, make it public because you don’t plan on storing anything sensitive in it, and then completely forget about that setting down the road, when you start uploading files that shouldn’t be exposed.
That’s a real UX challenge. Frankly, I didn’t want to feel responsible for a major data leak just because I shipped a massive footgun.
Thoughts on pricing
From the very beginning, I wanted to keep bucket pricing very simple. Railway already had four clear prices: CPU, Memory, Volume Storage, and Egress. I didn’t want to join the company and immediately introduce four more for Object Storage, Class A/B operations, and Bucket Egress.
“Hi, I’m Timo, I’m new here and I ruined your nice pricing page.”
It felt wrong to join a team and, as my first act, overcomplicate their straightforward pricing model.
I wanted to add just a single price for the actual storage, and not for operations or egress. As it turns out, we can offer exactly that for private buckets.
But I’m not sure we can do the same for public buckets. It’s as simple as that: I just don’t know yet, and there’s a high risk that we’d have to raise prices down the road. Public buckets will naturally incur far more operations (since every file request counts as one), and “more of something” usually means higher costs. Users hate price increases, and it always leads to bad press. I didn’t want to make a short-sighted decision that I knew will bite me later.
So, my strategy here was to start with private buckets because we already knew how we can price them. Then I can dig more into how to price public buckets safely. If it turns out they require a different price, it will just be introduced as a new, separate feature with its own pricing.
Can’t you add a simple “public” checkbox?
It sound like a minor task to just add a checkbox, and the checkbox itself is the easy part. But the real effort lies in everything that comes next, which is what I wanted to show in this post. Static sites, footguns, prices… and there’s more I thought about:
- Abuse and reputation: Public buckets are frequently used for static sites by scammers and malicious popup networks. Because we want buckets to be available on our free tier, this would immediately attract bad actors. It isn’t a reason to never offer it, but we would still need to integrate more abuse-reporting processes to protect our domain- and brand reputation.
- Illegal material: Public platforms have a high risk of being used to host illegal material. Again, keeping buckets available on our free tier means ensuring we don’t accidentally build something that makes hosting malicious content or CSAM incredibly easy. It doesn’t feel great to me to build something without also building mechanisms against this.
- File management: How would users actually upload public files? I intentionally cut a file explorer from the initial scope, but it becomes even more a necessity if you’re managing a public website bucket.
- Feature creep: Buckets configured for static site hosting require a bunch of additional configuration settings that I’d also need to build.
All of these thoughts in isolation weren’t a big deal. But when everything comes together, it’s just… too much to do in your onboarding project.
The Ugly
It still sucks to only have a private bucket if all you want is to store an OpenGraph image or some avatar. Proxying or presigning has a learning curve. It’s more effort, especially when you compare it to other cloud providers where you can just drag-and-drop stuff into a browser and it’s public. It makes a worse Developer Experience. There’s no denying that.
It’s not a feature that public buckets are missing. It’s just that: a missing feature which will eventually be filled.
For a three-week onboarding project, I of course would’ve loved to launch with public buckets, a file explorer, lifecycle, events, website config, … and it sucks to cut so many features. But I’m also still proud to have shipped what I shipped.
Comment: hello@timomeh.de

