Connecting to bucket
mosay360
HOBBYOP

20 days ago

“How do I make my S3 bucket public for static/media file hosting?”

$10 Bounty

2 Replies

Status changed to Open Railway 20 days ago


shaunclark5649
PRO

20 days ago

Is this a Minio Instance?


diegogallovich
PRO

20 days ago

You can't. Railway's managed buckets are private-only by design, there's no "make public" toggle, no public bucket URL, no public-read ACL, and no custom domain you can attach to a bucket. Quoting their docs: "Buckets are private, but you can still work with their files in a few ways."

The two supported patterns

PatternWhen to useCost notePresigned URLs_(recommended)_Profile pics, user uploads, anything that's "public-ish" but you can hand out a per-request URL. Up to 90-day expiry. Zero service egress the file streams directly from the bucket to the browser.Backend proxyWhen you need a stable URL (e.g., /media/foo.jpg) embedded in HTML/email, or want CDN caching, or need auth gates.Costs service egress (counted against your service's bandwidth), but you can put Cloudflare/Bunny in front of your service to amortize that.

How that translates to typical use cases

  • Public website img/href tags → backend proxy route + a CDN in front. Anything you put in raw HTML needs a stable URL, and presigned URLs aren't stable (they expire).
  • App-rendered images (React/Vue/etc.) → presigned URLs generated on page load. Cheapest path.
  • One-off downloads / "share this file" → presigned URL with a 1-hour expiry. Classic.
  • Email-embedded images → backend proxy (presigned URLs may expire before the email is opened).

Generating a presigned URL with Railway's S3 creds

Pull credentials with railway bucket credentials --bucket <name> --json, then any standard S3 SDK works against the bucket's endpoint. Example with aws-sdk JS v3:

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3 = new S3Client({

endpoint: process.env.BUCKET_ENDPOINT,

region: "us-east-1", // any value; Railway ignores it

credentials: {

accessKeyId: process.env.BUCKET_ACCESS_KEY_ID,

secretAccessKey: process.env.BUCKET_SECRET_ACCESS_KEY,

},

forcePathStyle: true, // important for non-AWS S3

});

const url = await getSignedUrl(

s3,

new GetObjectCommand({ Bucket: "linko-photos", Key: "uploads/photo.jpg" }),

{ expiresIn: 3600 } // seconds; max 90 days

);

If you really need a public URL on a stable hostname

Their cleanest path on Railway is a tiny proxy service (~30 lines of Node/Hono/Express): receives GET /media/:key, streams the object through, sets Cache-Control: public, max-age=31536000, immutable, optionally puts Cloudflare in front of the service domain. That gets them effectively-public URLs and CDN caching, at the cost of routing the first uncached request through their service.


Welcome!

Sign in to your Railway account to join the conversation.

Loading...