Skip to content

Upload Service Configuration ​

Environment variables and configuration options for the Upload Service.

Environment Variables ​

Required Variables ​

VariableDescriptionExample
R2_ACCOUNT_IDCloudflare account IDabc123def456
R2_BUCKET_NAMER2 bucket namemy-storybooks-production
R2_S3_ACCESS_KEY_IDR2 S3 API access keyxxx
R2_S3_SECRET_ACCESS_KEYR2 S3 API secret keyxxx

Firebase Variables (Node.js) ​

VariableDescription
GOOGLE_APPLICATION_CREDENTIALSPath to service account JSON
FIRESTORE_SERVICE_ACCOUNT_IDService account identifier

Firebase Variables (Workers) ​

VariableDescription
FIREBASE_PROJECT_IDFirebase project ID
FIREBASE_CLIENT_EMAILService account email
FIREBASE_PRIVATE_KEYService account private key
FIRESTORE_SERVICE_ACCOUNT_IDService account identifier

Optional Variables ​

VariableDefaultDescription
PORT3000Server port (Node.js only)
LOG_LEVELinfoLogging level

Configuration Files ​

.env (Node.js) ​

bash
# .env
PORT=3000

# R2 Storage
R2_ACCOUNT_ID=your-account-id
R2_BUCKET_NAME=my-storybooks-production
R2_S3_ACCESS_KEY_ID=your-access-key
R2_S3_SECRET_ACCESS_KEY=your-secret-key

# Firebase
GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json
FIRESTORE_SERVICE_ACCOUNT_ID=upload-service

.dev.vars (Workers Local Development) ​

bash
# .dev.vars
R2_ACCOUNT_ID="your-account-id"
R2_S3_ACCESS_KEY_ID="your-access-key"
R2_S3_SECRET_ACCESS_KEY="your-secret-key"
R2_BUCKET_NAME="my-storybooks-staging"

# Firebase (extract from serviceAccount.json)
FIREBASE_PROJECT_ID="your-project-id"
FIREBASE_CLIENT_EMAIL="service-account@project.iam.gserviceaccount.com"
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n"
FIRESTORE_SERVICE_ACCOUNT_ID="upload-service"

WARNING

The FIREBASE_PRIVATE_KEY must include literal \n characters, not actual newlines.

wrangler.toml ​

toml
name = "storybook-upload-service"
main = "dist/entry.worker.js"
compatibility_date = "2024-01-01"

# R2 Bucket Binding
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-storybooks-production"
preview_bucket_name = "my-storybooks-staging"

# Environment Variables
[vars]
R2_BUCKET_NAME = "my-storybooks-production"
LOG_LEVEL = "info"

# Production Environment
[env.production]
name = "storybook-upload-service"
[[env.production.r2_buckets]]
binding = "BUCKET"
bucket_name = "my-storybooks-production"

# Staging Environment
[env.staging]
name = "storybook-upload-service-staging"
[[env.staging.r2_buckets]]
binding = "BUCKET"
bucket_name = "my-storybooks-staging"

Environment Separation ​

EnvironmentR2 BucketPurpose
Local Developmentmy-storybooks-stagingDeveloper testing
PR Previewmy-storybooks-stagingPR deployments
Productionmy-storybooks-productionLive service

Secrets Management ​

Cloudflare Workers ​

Set secrets using Wrangler CLI:

bash
# Interactive
wrangler secret put R2_ACCOUNT_ID

# From stdin
echo "your-value" | wrangler secret put R2_S3_ACCESS_KEY_ID

# For specific environment
wrangler secret put R2_ACCOUNT_ID --env production

List existing secrets:

bash
wrangler secret list

GitHub Actions ​

Add secrets in repository settings:

  1. Settings → Secrets and variables → Actions
  2. Click New repository secret
  3. Add each required secret

Use in workflows:

yaml
env:
  R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
  R2_S3_ACCESS_KEY_ID: ${{ secrets.R2_S3_ACCESS_KEY_ID }}
  R2_S3_SECRET_ACCESS_KEY: ${{ secrets.R2_S3_SECRET_ACCESS_KEY }}

Firebase Configuration ​

Service Account Setup ​

  1. Go to Firebase Console → Project Settings
  2. Navigate to Service Accounts tab
  3. Click "Generate new private key"
  4. Save the JSON file securely

Using Service Account ​

Node.js:

bash
export GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json

Cloudflare Workers:

Extract values from JSON and set as secrets:

bash
# Extract from serviceAccount.json
PROJECT_ID=$(jq -r '.project_id' serviceAccount.json)
CLIENT_EMAIL=$(jq -r '.client_email' serviceAccount.json)
PRIVATE_KEY=$(jq -r '.private_key' serviceAccount.json)

# Set as secrets
wrangler secret put FIREBASE_PROJECT_ID <<< "$PROJECT_ID"
wrangler secret put FIREBASE_CLIENT_EMAIL <<< "$CLIENT_EMAIL"
wrangler secret put FIREBASE_PRIVATE_KEY <<< "$PRIVATE_KEY"

Validation ​

The service validates configuration on startup:

[upload-service] Starting...
[upload-service] R2 Account ID: abc***
[upload-service] R2 Bucket: my-storybooks-production
[upload-service] Firebase Project: my-firebase-project
[upload-service] ✓ Configuration valid
[upload-service] Listening on port 3000

Missing required variables cause startup failure:

[upload-service] Error: Missing required environment variable: R2_ACCOUNT_ID

Next Steps ​

Cloudflare bindings and secrets ​

Running on Workers, the service takes these in addition to the variables above:

NameKindPurpose
STORYBOOK_BUCKETR2 bindingWhere build archives are written
BUILD_PROCESSING_QUEUEQueue bindingPublishes the build event that triggers indexing
CLEANUP_TOKENSecretAuthorizes DELETE /cleanup/:project/:version
FIREBASE_PROJECT_IDSecretFirestore project for build records and API keys
FIREBASE_CLIENT_EMAILSecretService account for Firestore
FIREBASE_PRIVATE_KEYSecretService account key
SENTRY_DSNSecretError reporting. SENTRY_ENVIRONMENT, SENTRY_RELEASE and SENTRY_TRACES_SAMPLE_RATE tune it

Without BUILD_PROCESSING_QUEUE bound, uploads still succeed and are still served — they are simply never indexed, which looks like a search problem rather than a configuration one.

Deploy stamp ​

SCRY_ENV, SCRY_SERVICE, SCRY_COMMIT, SCRY_BRANCH, SCRY_BUILD_TIME, SCRY_DEPLOY_ID and SCRY_ACTOR are injected at build time and reported by /healthz, so a running deployment can say exactly what it is.