The repository deployment workflows now build and deploy the complete service
set, including the private, always-on
casebender-connector-worker. See
Integration Execution Plane for
its required secrets, queue contract, and health checks.Overview
This guide walks you through deploying CaseBender on Google Cloud Run using our pre-built Docker images.Prerequisites
- Google Cloud Account
- Google Cloud CLI installed
- Docker installed
Step 1: Initial Setup
Install Google Cloud CLI
Initialize Project
Step 2: Set Up Cloud Infrastructure
Connect existing Cloud Storage buckets
CaseBender uses customer-owned GCS buckets. It does not create, empty, or delete buckets. Provision separatequarantine, records, and ephemeral
buckets before this deployment.
Use Cloud Run service identity or GKE Workload Identity with Application
Default Credentials. Do not download a service-account key for a new production
deployment.
STORAGE_PROVIDER=gcs, GCS_BUCKET, and
optional GCS_PROJECT_ID. GOOGLE_STORAGE_BUCKET is not a CaseBender storage
variable.
See Enterprise Storage Overview and
Storage Security Baseline.
Set Up Cloud SQL (PostgreSQL)
Set Up Memorystore (Redis)
CaseBender services refuse to start in production when Redis has no password. Create the instance with AUTH and in-transit encryption, then letsecure-redis.sh write REDIS_URL and the server CA to Secret Manager:
./deploy/gcloud/secure-redis.sh
in a maintenance window. It enables AUTH, rewrites REDIS_URL, and stores the
private IP in REDIS_TLS_EXEMPT_HOSTS (in-transit encryption cannot be added
to an existing instance). Clients without the password are rejected as soon
as AUTH is on, so redeploy every service immediately afterwards.
Configure Secret Manager
CaseBender’sweb, api, and worker services require one stable
AUDIT_INTEGRITY_SECRET to authenticate the tamper-evident audit chain. Create
it once, grant the runtime service account access, and bind the same secret to
all three services. Never replace it during a normal deployment.
The repository’s deploy/gcloud/setup-infrastructure.sh and
deploy-gcloud.yml workflow create the secret only when it is absent and
preserve existing values. For a manual deployment:
Step 3: Pull and Push Docker Images
Step 4: Deploy Services
Deploy Main Application
AUDIT_INTEGRITY_SECRET binding to the API and worker
deployments. A missing binding does not necessarily fail a basic health probe;
it fails the first operation that must append a protected audit record.
Configure authenticated Pub/Sub alert pushes
Pub/Sub must attach a Google-signed OIDC token when it pushes alerts to/api/pubsub/alerts. Configure an exact audience and an allow-list that maps
each push service account to an existing organization and active organization
member. CaseBender uses the mapped member as the audit actor and always scopes
created alerts to the mapped organization.
PUBSUB_PUSH_IDENTITY_MAPPINGS must be a JSON array. Service-account emails
must be unique, and every referenced organization and system user must already
exist. The system user must be active and belong to the mapped organization.
Requests with a missing, forged, expired, wrong-audience, or unlisted-identity
token are rejected before the message body is processed.
Deploy Workflow Processor
Deploy Connector Worker
Deploy MISP Processor
Deploy Python Runner (optional)
Python workflow steps run tenant code only on the isolatedpython-runner
service, inside an nsjail sandbox. Skip this section unless you enable Python
steps with WORKFLOW_PYTHON_ENABLED=true.
The runner needs the second-generation execution environment, which supports
the Linux namespaces and seccomp filters nsjail uses. Deploy it without a VPC
connector or Cloud SQL attachment, so code that escapes the sandbox still cannot
reach private databases or Redis. Only the workflow processor’s service
account may invoke it.
WORKFLOW_PYTHON_ENABLED=true on the web service as well, so the workflow
editor offers Python steps.
For a *.run.app runner URL, the workflow processor requests a Google ID token
from the metadata server and sends it in X-Serverless-Authorization; the
runner’s own bearer token stays in Authorization. If a custom domain fronts
the runner, set WORKFLOW_PYTHON_RUNNER_AUDIENCE to the runner’s run.app URL.
The GitHub Actions deployment (.github/workflows/deploy-gcloud.yml) performs
all of these steps automatically. It generates PYTHON_RUNNER_TOKEN when
missing, and the WORKFLOW_PYTHON_ENABLED repository variable controls the
feature.
Step 5: Configure Domain and SSL
Map Custom Domain
Monitoring and Maintenance
Set Up Monitoring
- Navigate to Cloud Monitoring in Google Cloud Console
- Create an uptime check for your service
- Set up alerts for:
- Error rates
- Latency
- Instance count
- Memory usage
View Logs
Update Application
To deploy updates:Cost Optimization
-
Autoscaling Configuration
-
Resource Allocation
Troubleshooting
Common Issues
-
Connection Issues
- Verify Cloud SQL connection
- Check Redis connectivity
- Validate environment variables
-
Performance Problems
- Review instance metrics
- Check resource allocation
- Analyze request patterns
-
Deployment Failures
- Check build logs
- Verify service account permissions
- Review deployment configuration
-
Seed did not run
- Cloud Run web startup must call
ts-node --transpile-onlyforprisma/seed.ts. - Without
--transpile-only, seed typechecksconsole/processand exits withSeed already run or failed (non-critical)while the app still starts.
- Cloud Run web startup must call
-
Uploaded images stay on HTTP 409
GET /attachment/{id}?ready=1returns 409 until the worker marks the objectCLEANand promotes it out of quarantine.- Cloud Run has no ClamAV sidecar. The worker must set
MALWARE_SCANNER_PROVIDER=skip. Without it, logs showA supported external malware scanner must be configuredand uploads never leave quarantine.
Next Steps
- Set up CI/CD pipelines
- Configure backup strategies
- Implement monitoring and alerting
- Review security best practices