---
title: "Deploy public-buckets"
description: "Make your Railway Buckets public and geo-distributed."
category: "Storage"
url: https://railway.com/deploy/public-buckets
---

# Deploy public-buckets

Make your Railway Buckets public and geo-distributed.

**[Deploy public-buckets on Railway](https://railway.com/template/public-buckets)**

- **Creator:** upd
- **Category:** Storage
- **Total deploys:** 23

## Template content

### webserver https://devicons.railway.app/golang

- **Image:** xlab/cdn-s3-go:v1
- **Start command:** `/app/cdn-s3-go start`
- **Health check:** /health
- **Public domain:** Yes

### url-cache https://cdn.sanity.io/images/sy1jschh/production/0ce0bfdcfbdbf69662b1116671f97c2dd788b655-157x157.svg

- **Image:** redis:8.2.1
- **Start command:** `/bin/sh -c "rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH"`

## Buckets

- **example-bucket**

## Documentation

# Deploy and Host Public Buckets
![cdn-s3-go](https://upd-dev.t3.storage.dev/uploads/cdn-s3-go.webp)

Railway Buckets provide an excellent offering to store static files, however there are 2 major limitations:
* They are not public-accessible
* They are not geo distributed (region of the bucket is fixed)

**public-buckets** template deploys a webserver that and wraps any S3 compatible buckets and makes a CDN like access layer. It's geo distributed, generates pre-signed URLs to avoid exposing buckets to public, and most importantly - utilizes `HTTP 302` redirects to return content without proxying the data.

## Dependencies for public-buckets Hosting

The backbone is [cdn-s3-go](https://github.com/xlab/cdn-s3-go), a super fast, open-sourced Go service that does the job. It has very little amount of external dependencies:
![external deps](https://upd-dev.t3.storage.dev/uploads/deps.png)

There is an optional **Redis** service, that provides a caching layer for pre-signed URLs. If you see that it negatively affects performance - just delete it.

## Demo Project

There is a demo project available for this template. It serves the following URLs from `demo` bucket:

* https://public-buckets.up.railway.app/demo/images/eagle.jpg
* https://public-buckets.up.railway.app/demo/images/croissant.jpg

Each one image exists only in one regional bucket: one in US and another in EU. Guess which one is where.

## Configuration

Template automatically creates an example bucket and example environment variables. There is the main gist of it:

Webserver config accepts list of bucket names as env variable `CDN_BUCKET_PUBLIC_NAMES` like `lfs,avatars`. In this template we have `example`.

It also accepts list of region aliases as env variable `CDN_BUCKET_REGION_ALIASES` like `us,eu`. Template deploys only `us`, you are free to change it.

For every bucket name in the list, it tries to load S3 / Railway Buckets compatible credentials:

```bash
CDN_BUCKET_ENDPOINT_{public_bucket_name}_{region_alias}
CDN_BUCKET_REGION_{public_bucket_name}_{region_alias}
CDN_BUCKET_NAME_{public_bucket_name}_{region_alias}
CDN_BUCKET_PATH_PREFIX_{public_bucket_name}_{region_alias}
CDN_BUCKET_ACCESS_KEY_ID_{public_bucket_name}_{region_alias}
CDN_BUCKET_SECRET_ACCESS_KEY_{public_bucket_name}_{region_alias}
```

Here for instance how demo project was linked:

![bucket links in demo](https://upd-dev.t3.storage.dev/uploads/bucket-links.png)

See the full reference for variables in [.env.example](https://github.com/xlab/cdn-s3-go/blob/master/.env.example).

## CORS

Sometimes you need additional configuration for CORS of your public buckets. 

```
CDN_CORS_PERMISSIVE=true
CDN_CORS_ALLOW_ORIGIN=*
CDN_CORS_ALLOW_METHODS=GET,HEAD,OPTIONS,PUT,POST,PATCH,DELETE
CDN_CORS_ALLOW_HEADERS=*
CDN_CORS_EXPOSE_HEADERS=*
CDN_CORS_ALLOW_CREDENTIALS=false
CDN_CORS_MAX_AGE=86400
```

Note: when `CDN_CORS_PERMISSIVE=true`, it intentionally forces wildcard values and ignores stricter custom values.

## About Hosting public-buckets

Deploying a Go based docker image, that can be replicated across regions, as it's a stateless gateway. The gateway manages auth to Railway Buckets and acts like a public CDN layer. There is a Redis cache involved, but completely optional, it is included in the template by default, but can be removed in few clicks.

## Common Use Cases

- Hosting static files for your next SaaS, for example - avatars and customer data
- Distribution of large blobs, for example, model weights or UGC content as part of the AI processing pipeline
- Upd.dev uses this CDN layer for Git hosting

### Deployment Dependencies

* Docker image [xlab/cdn-s3-go](https://hub.docker.com/r/xlab/cdn-s3-go)
* No other external dependencies

## Why Deploy public-buckets on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it. In this case, infinite amount of S3 compatible buckets, balanced via Metal-hosted edge layer with little help of redirect magic.

By deploying **public-buckets** and using **cdn-s3-go** on Railway, you are enabling your own CDN layer to deliver static across the globe to customers, overseas AI infra, while keeping egress costs zero.

Who needs R2 after that!?


## Similar templates

- [Garage S3 Storage](https://railway.com/deploy/garage-s3-storage) — Ultra-light S3 server: fast, open-source, plug-and-play.
- [Redis](https://railway.com/deploy/redis-1) — Self Host Latest Redis with Railway
- [Postgres Backup to Cloudflare R2 (S3-Compatible)](https://railway.com/deploy/postgres-to-r2-backup) — Automated PostgreSQL backups to S3-compatible storage with encryption

Open this page in a browser: https://railway.com/deploy/public-buckets
