Cloud Run Deployment
Deploy textrawl to Google Cloud Run
Deploy textrawl as a serverless container on Google Cloud Run.
Prerequisites
- Google Cloud account with billing enabled
gcloudCLI installed and authenticated- Docker installed
- Artifact Registry API enabled
Quick Deploy (Recommended)
Use the provided deployment script:
export GCP_PROJECT_ID=your-project-id
export GCP_REGION=us-central1 # optional, defaults to us-central1
./scripts/deploy.shManual Deployment
1. Set Up Artifact Registry
# Enable the API
gcloud services enable artifactregistry.googleapis.com
# Create a repository
gcloud artifacts repositories create textrawl \
--repository-format=docker \
--location=us-central1
# Configure Docker authentication
gcloud auth configure-docker us-central1-docker.pkg.dev2. Build and Push
# Set variables
PROJECT_ID=your-project-id
REGION=us-central1
IMAGE=$REGION-docker.pkg.dev/$PROJECT_ID/textrawl/textrawl
# Build and push
docker build -t $IMAGE:latest .
docker push $IMAGE:latest3. Deploy
gcloud run deploy textrawl \
--image $IMAGE:latest \
--platform managed \
--region $REGION \
--allow-unauthenticated \
--set-secrets="DATABASE_URL=textrawl-database-url:latest,OPENAI_API_KEY=textrawl-openai-key:latest,API_BEARER_TOKEN=textrawl-api-token:latest" \
--min-instances 0 \
--max-instances 10 \
--memory 512Mi \
--cpu 1 \
--cpu-boostTip: For production, pin secrets to specific versions instead of
:latestfor stability (e.g.,textrawl-api-token:1).
Secret Manager
Store secrets in Google Secret Manager:
# Create secrets (use textrawl- prefix for organization)
echo -n "postgresql://user:pass@ep-xxx-pooler.neon.tech/textrawl?sslmode=require" | gcloud secrets create textrawl-database-url --data-file=-
echo -n "sk-your-openai-key" | gcloud secrets create textrawl-openai-key --data-file=-
echo -n "your-64-char-token" | gcloud secrets create textrawl-api-token --data-file=-Grant Cloud Run Access to Secrets
Cloud Run needs permission to read each secret. First, get your project number (not project ID):
# Get your project number
gcloud projects describe YOUR_PROJECT_ID --format="value(projectNumber)"Then grant the default Compute Engine service account access to each secret:
PROJECT_NUMBER=123456789012 # Replace with your project number
# Grant access to each secret
for SECRET in textrawl-database-url textrawl-openai-key textrawl-api-token; do
gcloud secrets add-iam-policy-binding $SECRET \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
doneNote: PROJECT_NUMBER is a numeric ID (e.g.,
123456789012), different from PROJECT_ID (e.g.,my-project). You can also find it in the Cloud Console under IAM & Admin > Settings.
Configuration
Cloud Run settings:
- Memory: 512MB minimum
- CPU: 1 vCPU
- Max instances: 10 (adjust for traffic)
- Min instances: 0 (scale to zero)
- Timeout: 60s
Large uploads (GCS)
Uploads above MAX_SINGLE_FILE_SIZE_MB use a resumable workflow: the browser
PUTs bytes directly to a GCS bucket (never through Cloud Run or Vercel), then
the server verifies and processes the object. Storage is selected at runtime —
set GCS_UPLOAD_BUCKET to enable GCS; leave it unset to fall back to the
in-memory fake (local dev only).
# Point the service at the provisioned bucket
gcloud run services update <service-name> --region <your-region> \
--update-env-vars GCS_UPLOAD_BUCKET=<your-upload-bucket>
# GCS_PROJECT_ID is optional — auto-detected from the runtime service account.The bucket itself (creation, IAM, CORS for the dashboard origin, and the
abandoned-upload lifecycle rule) is provisioned and version-controlled under
infra/gcs/ —
see its README.md for the exact commands. Key points:
- The Cloud Run runtime service account needs
roles/storage.objectAdminon the bucket (granted via ADC — no keys). - Bucket CORS must allow the dashboard origin with methods
PUT/POST/GET/HEADand headersContent-Type, Content-Range, x-goog-*. - A lifecycle rule deletes abandoned objects after 1 day; soft-delete is disabled so deleted upload bytes are not retained/billed.
Custom Domain
gcloud run domain-mappings create \
--service textrawl \
--domain api.yourdomain.com \
--region us-central1Monitoring
View metrics in Cloud Console:
- Request count
- Latency
- Error rate
- Instance count