Complete Self-Hosting Setup
Step-by-step guide to deploy the complete Scry stack.
Overview
We'll deploy in this order:
- Firebase (database & auth)
- Cloudflare R2 & KV (storage)
- Upload Service (Workers)
- CDN Service (Workers)
- Dashboard (Vercel)
- DNS configuration
- Verification
Estimated time: 2-3 hours
Step 1: Firebase Setup
Create Project
- Go to Firebase Console
- Click "Add project"
- Name:
scry-self-hosted(or your choice) - Disable Google Analytics (optional)
- Create project
Enable Firestore
- Go to Firestore Database
- Click "Create database"
- Select "Start in production mode"
- Choose region closest to your users
- Create
Enable Authentication
- Go to Authentication → Sign-in method
- Enable "GitHub"
- Note the callback URL (you'll need it for GitHub OAuth)
- Leave Client ID/Secret empty for now
Create GitHub OAuth App
- Go to GitHub → Settings → Developer settings → OAuth Apps
- Click "New OAuth App"
- Fill in:
- Name: Scry Dashboard
- Homepage URL:
https://dashboard.yourdomain.com - Callback URL: (from Firebase, looks like
https://your-project.firebaseapp.com/__/auth/handler)
- Create application
- Copy Client ID
- Generate and copy Client Secret
Complete GitHub Auth Setup
- Back in Firebase → Authentication → GitHub
- Enter Client ID and Client Secret
- Save
Generate Service Account
- Go to Project Settings → Service accounts
- Click "Generate new private key"
- Save as
serviceAccount.json
Note Configuration Values
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_API_KEY=your-web-api-key
FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.comStep 2: Cloudflare Setup
Create R2 Buckets
bash
# Production bucket
wrangler r2 bucket create scry-static-sites
# Staging bucket (for development)
wrangler r2 bucket create scry-static-sites-stagingEnable Public Access
- Go to Cloudflare Dashboard → R2
- Select
scry-static-sites - Settings → Public access → Allow Access
- Note the public URL:
https://pub-xxx.r2.dev
Create R2 API Token
- R2 → Manage R2 API Tokens
- Create token with "Admin Read & Write" permissions
- Note Access Key ID and Secret Access Key
Create KV Namespaces
bash
# Production
wrangler kv:namespace create CDN_CACHE
# Note the ID
# Preview
wrangler kv:namespace create CDN_CACHE --preview
# Note the preview_idNote Cloudflare Values
CLOUDFLARE_ACCOUNT_ID=your-account-id
R2_BUCKET_NAME=scry-static-sites
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
KV_NAMESPACE_ID=your-kv-id
KV_PREVIEW_ID=your-preview-idStep 3: Deploy Upload Service
Clone Repository
bash
git clone https://github.com/epinnock/scry-storybook-upload-service.git
cd scry-storybook-upload-service
npm installConfigure wrangler.toml
toml
name = "scry-upload-service"
main = "dist/entry.worker.js"
compatibility_date = "2024-01-01"
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "scry-static-sites"
[vars]
R2_BUCKET_NAME = "scry-static-sites"Set Secrets
bash
# R2 credentials
wrangler secret put R2_ACCOUNT_ID
wrangler secret put R2_S3_ACCESS_KEY_ID
wrangler secret put R2_S3_SECRET_ACCESS_KEY
# Firebase credentials (from serviceAccount.json)
wrangler secret put FIREBASE_PROJECT_ID
wrangler secret put FIREBASE_CLIENT_EMAIL
wrangler secret put FIREBASE_PRIVATE_KEYDeploy
bash
npm run build
wrangler deployNote the worker URL: https://scry-upload-service.your-subdomain.workers.dev
Step 4: Deploy CDN Service
Clone Repository
bash
cd ..
git clone https://github.com/epinnock/scry-cdn-service.git
cd scry-cdn-service
npm installConfigure wrangler.toml
toml
name = "scry-cdn-service"
main = "cloudflare/index.ts"
compatibility_date = "2024-01-01"
[[r2_buckets]]
binding = "STATIC_SITES"
bucket_name = "scry-static-sites"
[[kv_namespaces]]
binding = "CDN_CACHE"
id = "your-kv-namespace-id"
preview_id = "your-preview-id"
routes = [
{ pattern = "view-*.yourdomain.com/*", zone_name = "yourdomain.com" }
]Deploy
bash
npm run build:cloudflare
wrangler deployStep 5: Deploy Dashboard
Clone Repository
bash
cd ..
git clone https://github.com/epinnock/scry-developer-dashboard.git
cd scry-developer-dashboard
pnpm installConfigure Environment
bash
cp .env.local.example .env.localEdit .env.local:
bash
NEXT_PUBLIC_FIREBASE_API_KEY=your-api-key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your-sender-id
NEXT_PUBLIC_FIREBASE_APP_ID=your-app-id
NEXT_PUBLIC_USE_AUTH=trueDeploy to Vercel
bash
# Install Vercel CLI
npm install -g vercel
# Deploy
vercel
# Add environment variables
vercel env add NEXT_PUBLIC_FIREBASE_API_KEY
# ... add all other variablesOr deploy via Vercel dashboard:
- Import repository
- Add environment variables
- Deploy
Step 6: DNS Configuration
Custom Domain for CDN
Add DNS record:
| Type | Name | Content | Proxy |
|---|---|---|---|
| AAAA | view-* | 100:: | Yes |
Or CNAME:
| Type | Name | Content | Proxy |
|---|---|---|---|
| CNAME | * | scry-cdn-service.workers.dev | Yes |
Custom Domain for Dashboard
Configure in Vercel:
- Go to project settings → Domains
- Add
dashboard.yourdomain.com - Configure DNS as instructed
Update Worker Routes
If using custom domain, update wrangler.toml:
toml
routes = [
{ pattern = "view-*.yourdomain.com/*", zone_name = "yourdomain.com" }
]Redeploy CDN Service:
bash
wrangler deployStep 7: Verification
Test Health Endpoints
bash
# Upload Service
curl https://scry-upload-service.workers.dev/health
# CDN Service
curl https://view-test.yourdomain.com/healthTest Authentication
- Open Dashboard URL
- Click "Sign in with GitHub"
- Complete OAuth flow
- Verify you see the dashboard
Test Upload
bash
# Create test project in Dashboard
# Get API key
# Create test ZIP
echo "test" > test.txt
zip test.zip test.txt
# Upload
curl -X POST \
-H "X-API-Key: scry_proj_xxx" \
-H "Content-Type: application/zip" \
--data-binary @test.zip \
https://scry-upload-service.workers.dev/upload/test-project/v1.0.0
# View
curl https://view-test-project.yourdomain.com/test.txtConfiguration Reference
Upload Service Environment
| Variable | Description |
|---|---|
R2_ACCOUNT_ID | Cloudflare account ID |
R2_BUCKET_NAME | R2 bucket name |
R2_S3_ACCESS_KEY_ID | R2 access key |
R2_S3_SECRET_ACCESS_KEY | R2 secret key |
FIREBASE_PROJECT_ID | Firebase project ID |
FIREBASE_CLIENT_EMAIL | Service account email |
FIREBASE_PRIVATE_KEY | Service account private key |
CDN Service Environment
| Variable | Description |
|---|---|
R2_BUCKET_NAME | R2 bucket name (via binding) |
CDN_CACHE | KV namespace (via binding) |
Dashboard Environment
| Variable | Description |
|---|---|
NEXT_PUBLIC_FIREBASE_* | Firebase configuration |
NEXT_PUBLIC_USE_AUTH | Enable authentication |
Troubleshooting
"Firebase authentication error"
- Verify GitHub OAuth callback URL
- Check Firebase API key is correct
- Ensure GitHub provider is enabled
"R2 access denied"
- Check R2 API token permissions
- Verify bucket name matches
- Enable public access for downloads
"CDN returns 404"
- Verify file was uploaded successfully
- Check subdomain routing configuration
- Verify KV namespace binding
Next Steps
- Monitoring - Set up observability
- Scaling - Architecture for scaling