Google Cloud Storage Setup
This guide explains how to configure Google Cloud Storage (GCS) as your storage provider for BFFless.
Prerequisites
- Google Cloud Platform (GCP) account
- A GCP project
- Billing enabled on the project
Step 1: Create a Cloud Storage Bucket
- Go to Cloud Storage Console
- Click Create Bucket
- Enter a unique bucket name (e.g.,
my-bffless-storage) - Choose a Location type:
- Region: Single region, lowest latency for that region
- Dual-region: Two regions, better availability
- Multi-region: Multiple regions, highest availability
- Choose a Storage class:
- Standard: Frequently accessed data (recommended)
- Nearline: Accessed less than once a month
- Coldline: Accessed less than once a quarter
- Archive: Accessed less than once a year
- Access control: Choose Uniform (recommended)
- Click Create
Step 2: Configure Bucket CORS
BFFless uploads files from the browser directly to GCS using pre-signed URLs. The browser sends a PUT request to the bucket from your BFFless admin origin (e.g. https://admin.your-domain.com), so the bucket must permit cross-origin requests from that origin — otherwise the upload will fail with a CORS error in the browser console even though the credentials and IAM are correct.
Apply CORS via gcloud
-
Create a file
cors.json:[
{
"origin": ["https://admin.YOUR-DOMAIN.COM", "https://YOUR-DMAIN.COM"],
"method": ["GET", "HEAD", "PUT", "POST"],
"responseHeader": [
"Content-Type",
"Content-MD5",
"Content-Disposition",
"x-goog-resumable",
"x-goog-content-length-range",
"x-goog-meta-*"
],
"maxAgeSeconds": 3600
}
] -
Apply it to your bucket:
gcloud storage buckets update gs://YOUR_BUCKET_NAME --cors-file=cors.json -
Verify it was applied:
gcloud storage buckets describe gs://YOUR_BUCKET_NAME --format="default(cors_config)"
Apply CORS via gsutil (legacy)
gsutil cors set cors.json gs://YOUR_BUCKET_NAME
gsutil cors get gs://YOUR_BUCKET_NAME
List every origin you serve the admin UI from — including any custom domain, www/non-www variants, and your localhost dev URL if you upload from there. Wildcards like https://*.your-domain.com are not supported by GCS; list each origin explicitly.
You can use "*" to allow any origin during testing, but don't leave it that way in production — anyone could upload to your bucket via a leaked pre-signed URL from any site.
If you still see a CORS error after updating, wait 1–2 minutes and hard-refresh the browser (Cmd/Ctrl-Shift-R) to clear any cached preflight response.
Step 3: Create Service Account Credentials
Option A: Service Account Key File (Recommended for non-GCP deployments)
Use this when BFFless runs outside GCP (e.g., AWS, DigitalOcean, self-hosted).
-
Click Create Service Account
-
Enter a name (e.g.,
bffless-storage-sa) -
Click Create and Continue
-
Add the role: Storage Admin (
roles/storage.admin)Why Storage Admin?BFFless's connection test reads bucket metadata (
storage.buckets.get) before reading/writing objects. Storage Object Admin alone is not sufficient — it grantsstorage.objects.*but notstorage.buckets.get, so the test connection will fail with a permission denied error.If you need least-privilege, use Storage Object User (
roles/storage.objectUser) instead — it includes bothstorage.buckets.getand object read/write. -
Click Continue → Done
-
Click on the created service account
-
Go to Keys tab → Add Key → Create new key
-
Select JSON format
-
Click Create - the key file will download automatically
-
Store this file securely - it provides access to your bucket
Option B: Workload Identity (Recommended for GKE)
Use this when BFFless runs on Google Kubernetes Engine.
- Enable Workload Identity on your GKE cluster
- Create a Kubernetes service account
- Create a GCP service account with Storage Admin role (see note above on why Object Admin alone isn't enough)
- Bind the accounts:
gcloud iam service-accounts add-iam-policy-binding \
bffless-storage-sa@PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]"
Option C: Application Default Credentials (for GCE/Cloud Run)
Use this when BFFless runs on Google Compute Engine or Cloud Run.
- Create a service account with Storage Admin role (see note above on why Object Admin alone isn't enough)
- Attach the service account to your GCE instance or Cloud Run service
- BFFless will automatically use the attached credentials
Step 4: Configure in BFFless
Via Setup Wizard
- Navigate to the BFFless setup wizard
- Select Google Cloud Storage as storage provider
- Enter your configuration:
- Project ID: Your GCP project ID
- Bucket Name: Your bucket name
- Authentication Method: Choose one:
- Key File: Upload or paste the JSON key file contents
- Credentials JSON: Paste the JSON credentials object
- Application Default Credentials: For GCE/Cloud Run/GKE
- Click Test Connection & Save
Via Environment Variables
# Storage provider type
STORAGE_TYPE=gcs
# GCS Configuration
GCS_PROJECT_ID=my-gcp-project
GCS_BUCKET=my-bffless-storage
# Option 1: Path to key file
GCS_KEY_FILE=/path/to/service-account-key.json
# Option 2: Inline credentials (JSON string)
GCS_CREDENTIALS='{"type":"service_account","project_id":"...","private_key":"..."}'
# Option 3: Use Application Default Credentials (no additional config needed)
# Just don't set GCS_KEY_FILE or GCS_CREDENTIALS
Storage Classes
GCS offers different storage classes for cost optimization:
| Class | Use Case | Minimum Storage | Retrieval Cost |
|---|---|---|---|
| Standard | Frequently accessed | None | Free |
| Nearline | Monthly access | 30 days | $0.01/GB |
| Coldline | Quarterly access | 90 days | $0.02/GB |
| Archive | Yearly access | 365 days | $0.05/GB |
Troubleshooting
"Permission Denied" Error
- Verify the service account has Storage Admin (or Storage Object User) — Storage Object Admin alone is not sufficient because it does not include
storage.buckets.get - Check that the bucket name matches exactly
- Ensure the service account is in the correct project
- Verify the key file is valid and not expired
If the error mentions storage.buckets.get, the role is the cause — upgrade Object Admin to Storage Admin.
"CORS error" / "Access-Control-Allow-Origin" in browser console on upload
Pre-signed URL uploads are sent directly from the browser to GCS. If the bucket's CORS policy doesn't include your admin origin, the browser blocks the response.
- Confirm Step 2 (Configure Bucket CORS) was applied:
gcloud storage buckets describe gs://YOUR_BUCKET --format="default(cors_config)" - The origin in the error message must appear exactly in the CORS
originlist (scheme + host + port, no trailing slash) - After updating CORS, wait 1–2 minutes and hard-refresh — preflight responses are cached
- For multi-domain setups, list every origin explicitly (GCS doesn't support wildcards in the host part)
"Bucket Not Found" Error
- Verify the bucket exists in the GCS console
- Check for typos in the bucket name
- Ensure you're using the correct project ID
- GCS bucket names are globally unique
"Invalid Credentials" Error
- Verify the key file JSON is valid
- Check that the service account is not disabled
- Ensure the key hasn't been revoked
- Try regenerating the key file
Slow Performance
- Enable BFFless caching to reduce GCS requests
- Choose a region closer to your users
- Use Cloud CDN for edge caching
Security Best Practices
- Never commit key files to version control
- Use Workload Identity or ADC when running on GCP
- Enable uniform bucket-level access for consistent permissions
- Use customer-managed encryption keys (CMEK) for sensitive data
- Enable audit logging via Cloud Audit Logs
- Rotate service account keys regularly (every 90 days)
- Use least-privilege IAM roles - only grant required permissions
- Enable VPC Service Controls for sensitive workloads