Silo Docs

Deploying Silo

How to deploy Silo to your own infrastructure.

Overview

Silo runs on Cloudflare Workers with Cloudflare R2 as the storage backend. The frontend is Next.js running on Vercel.

Steps

Prerequisites

  • A Cloudflare account
  • A Vercel account
  • A Redis instance. You can get one for free from Upstash.
  • A PostgreSQL database. You can get one for free from Supabase
  • Node.js v24.15.0+ and pnpm (npm i -g pnpm)
  • The wrangler CLI (npm i -g wrangler)

Fork the repository

Fork the repository to your own GitHub account. (also give it a star!)

Clone & install dependencies

Clone the repository and install dependencies with pnpm. Replacd your-org with your GitHub username/organization.

git clone https://github.com/your-org/silo.git
cd silo
pnpm install

Create CloudFlare resources

Silo makes an R2 bucket and two Queues. Make sure to run all of these before deploying.

# R2 buckets (one for prod, one for local dev)
wrangler r2 bucket create silo-uploads
wrangler r2 bucket create silo-uploads-preview
 
# Queues (producer + dead-letter queue)
# If you are on the free plan, queues have a 24 hour message retention period.
# You must set the --message-retention-period-secs flag. Omit if you are on workers paid
wrangler queues create silo-delete-prefix --message-retention-period-secs 86400
wrangler queues create silo-delete-prefix-dlq --message-retention-period-secs 86400

The queue and bucket names are referenced in wrangler.toml

Enable Cloudflare image transforms for your zone before deploying. In the Cloudflare dashboard, go to Media > Images > Transformations, select your zone, and enable transformations for that zone.

Configure wrangler.toml

Open apps/cf-worker/wrangler.toml and fill in your values.

[vars]
WORKER_DOMAIN = "worker.your-domain.com"   # public hostname for the Worker
PROJECT_ROUTE_MODE = "subdomain"           # "subdomain" or "path"
 
[env.production]
vars.WORKER_DOMAIN = "worker.your-domain.com"
vars.PROJECT_ROUTE_MODE = "subdomain"
vars.NEXTJS_CALLBACK_URL = "https://your-silo-app.com"  # where the nextjs app is hosted (vercel)
vars.ENV = "production"

Use subdomain mode for project-slug.worker.your-domain.com URLs (requires wildcard DNS/Advanced Certificate Manager), or path mode for worker.your-domain.com/p/project-slug/... URLs. Path mode always starts with /p/*

Set secrets

These secrets are required for production. Keep them long, random, and out of VCS. You can generate them with:

openssl rand -base64 32
# Secret used to authenticate internal callbacks
wrangler secret put CALLBACK_SECRET --env production

# Secret used to sign upload tokens (must match SIGNING_SECRET in your app)
wrangler secret put SIGNING_SECRET --env production

# Secret used by the Worker to bypass Vercel deployment protection
# Save this value; you'll add it in Vercel in Step 6.
wrangler secret put VERCEL_AUTOMATION_BYPASS_SECRET --env production

For local development, create apps/cf-worker/.dev.vars (already in .gitignore).

CALLBACK_SECRET=dev-callback-secret
SIGNING_SECRET=dev-signing-secret
VERCEL_AUTOMATION_BYPASS_SECRET=dev-vercel-bypass-secret

Deploy the Worker

Run the deploy command from the repo root. Wrangler will bundle and upload the Worker to Cloudflare.

# Deploy to production
pnpm --filter cf-worker deploy --env production

# Or deploy from the worker directory directly
cd apps/cf-worker
wrangler deploy --env production

Wrangler will print the Worker's URL when the deployment is complete. Set your custom domain (e.g. files.your-domain.com) in the CF dashboard under Workers > Your Worker > Custom Domains

Make sure your domain's SSL/TLS mode is set to Full (Strict). Otherwise, the Worker may run into issues contacting the next app.

Deploy the Next.js app on Vercel

In the Vercel dashboard, import your GitHub repo as a new project. This repo is a turborepo, so make sure to set the Root Directory to apps/nextjs.

If install fails because workspace packages are missing, set the Install Command to: cd ../.. && pnpm install so pnpm links dependencies from the repo root correctly.

Under Settings > Environment Variables, add the Silo-related values for Production (and Preview if you use preview deployments). For local dev, keep the same keys in .env.local at the repo root.

# This is the database URL for the local postgres server running via docker
POSTGRES_URL="postgresql://dev:devpass@localhost:5432/appdb"

# The Upstash URL is used to connect to your Upstash project.
# This is the preconfigured url for the local redis server running via docker
UPSTASH_REDIS_REST_URL="http://localhost:8079"
UPSTASH_REDIS_REST_TOKEN="dev_token"

# You can generate the secret via 'openssl rand -base64 32'
# @see https://www.better-auth.com/docs/installation
AUTH_SECRET='supersecret'

# Preconfigured GitHub OAuth provider, works out-of-the-box
# @see https://www.better-auth.com/docs/authentication/github
AUTH_GITHUB_ID=''
AUTH_GITHUB_SECRET=''

# Cloudflare Worker URL for file uploads/downloads
WORKER_URL="http://localhost:8787"
WORKER_DOMAIN="ingest.your-domain.com" # public hostname for the Worker
PROJECT_ROUTE_MODE="subdomain" # "subdomain" => {projectSlug}.{WORKER_DOMAIN}, "path" => {WORKER_DOMAIN}/p/{projectSlug} (fixed /p prefix)

# Signing secret for generating signed URLs (generate via 'openssl rand -hex 32')
SIGNING_SECRET="your-secure-random-secret-here"
CALLBACK_SECRET="your-secure-random-secret-here"
CRON_SECRET="your-secure-random-secret-here"

# Do we want to disable organization creation?
NEXT_PUBLIC_DISABLE_ORG_CREATION=false

# Do we want to disable signup?
DISABLE_SIGNUP="false"

In Vercel, go to Settings > Deployment Protection > Protection Bypass for Automation, and add the same token you set for VERCEL_AUTOMATION_BYPASS_SECRET in step 6.

Click "Deploy", then copy your production URL. It must match NEXTJS_CALLBACK_URL in wrangler.toml for the Worker. Make sure to update that value and redeploy the Worker if the Vercel URL ever changes.

You're Done!

You're done! You can now use Silo to upload and serve files from your Cloudflare R2 bucket!

Local Development

Silo is set up to run locally using docker compose for the database and redis, wrangler for the worker, and nextjs frot he frontend.

# Start the Next.js app + docker
pnpm run dev

# Start the Worker in local dev mode
pnpm run dev:worker

The worker binds to http://lvh.me:8787 by default (configured via WORKER_DOMAIN in the development env). Your Next.js app should point NEXT_PUBLIC_SILO_WORKER_URL at that address.

On this page