Deployment

Secrets Management on GCP

Never store Open Astra credentials in environment files, Docker images, or source code. GCP Secret Manager is the correct place for all sensitive values. Both Firebase App Hosting and Cloud Run can pull secrets from Secret Manager at runtime — no .env file needed on the server.

What to store in Secret Manager

Secret name (suggested)Env varDescription
astra-database-urlDATABASE_URLPostgreSQL connection string (includes password)
astra-jwt-secretJWT_SECRETMin 32 chars — signs all user tokens
astra-typesense-api-keyTYPESENSE_API_KEYTypesense admin key
astra-typesense-urlTYPESENSE_URLInternal URL of your Typesense instance
astra-internal-api-keyINTERNAL_API_KEYService-to-service auth key
astra-openai-api-keyOPENAI_API_KEYAdd one per LLM provider you use

Step 1 — Create the secrets

Run this once after creating your GCP project:

bash
# Create every secret Open Astra needs
# Run once per GCP project — values are stored encrypted in Secret Manager

echo -n "postgresql://user:pass@host:5432/astra" \
  | gcloud secrets create astra-database-url --data-file=-

echo -n "$(openssl rand -hex 32)" \
  | gcloud secrets create astra-jwt-secret --data-file=-

echo -n "your-typesense-api-key" \
  | gcloud secrets create astra-typesense-api-key --data-file=-

echo -n "http://your-typesense-host:8108" \
  | gcloud secrets create astra-typesense-url --data-file=-

echo -n "$(openssl rand -hex 32)" \
  | gcloud secrets create astra-internal-api-key --data-file=-

# LLM provider keys (add the ones you use)
echo -n "sk-..." | gcloud secrets create astra-openai-api-key --data-file=-
echo -n "gsk_..." | gcloud secrets create astra-groq-api-key --data-file=-

Firebase App Hosting

Firebase App Hosting reads secrets declared in apphosting.yaml at the root of your repository. Each entry maps a secret name in Secret Manager to an environment variable in the runtime container.

apphosting.yaml

yaml
# apphosting.yaml — Firebase App Hosting
# Place this file at the root of your repository.
# Secrets are pulled from GCP Secret Manager at build and runtime.

runConfig:
  concurrency: 80

env:
  - variable: DATABASE_URL
    secret: astra-database-url
    availability: [BUILD, RUNTIME]

  - variable: JWT_SECRET
    secret: astra-jwt-secret
    availability: [RUNTIME]

  - variable: TYPESENSE_API_KEY
    secret: astra-typesense-api-key
    availability: [RUNTIME]

  - variable: TYPESENSE_URL
    secret: astra-typesense-url
    availability: [RUNTIME]

  - variable: INTERNAL_API_KEY
    secret: astra-internal-api-key
    availability: [RUNTIME]

  # LLM provider keys — add the ones you use
  - variable: OPENAI_API_KEY
    secret: astra-openai-api-key
    availability: [RUNTIME]

Grant access

The App Hosting service account must have roles/secretmanager.secretAccessor on each secret. Run this after creating your secrets and before deploying:

bash
# Grant the App Hosting service account access to each secret
# Replace PROJECT_ID and BACKEND_ID with your values.
# BACKEND_ID is shown in the Firebase console under App Hosting.

SA="firebase-app-hosting-compute@PROJECT_ID.iam.gserviceaccount.com"

for SECRET in \
  astra-database-url \
  astra-jwt-secret \
  astra-typesense-api-key \
  astra-typesense-url \
  astra-internal-api-key \
  astra-openai-api-key
do
  gcloud secrets add-iam-policy-binding $SECRET \
    --member="serviceAccount:$SA" \
    --role="roles/secretmanager.secretAccessor"
done
After updating apphosting.yaml and granting IAM, push a commit to trigger a new build. Firebase App Hosting resolves secret values at build time for BUILD availability and injects them at container start for RUNTIME only.

Cloud Run

For the Terraform + Cloud Run deployment path, pass secrets as environment variable references using --set-secrets. Each reference is resolved at container start — the plaintext value never appears in the deploy command's output.

Deploy with secrets

bash
# Cloud Run — inject secrets as environment variables at deploy time
gcloud run deploy astra-gateway \
  --image us-central1-docker.pkg.dev/PROJECT_ID/astra-images/gateway:latest \
  --region us-central1 \
  --set-secrets=DATABASE_URL=astra-database-url:latest \
  --set-secrets=JWT_SECRET=astra-jwt-secret:latest \
  --set-secrets=TYPESENSE_API_KEY=astra-typesense-api-key:latest \
  --set-secrets=TYPESENSE_URL=astra-typesense-url:latest \
  --set-secrets=INTERNAL_API_KEY=astra-internal-api-key:latest \
  --set-secrets=OPENAI_API_KEY=astra-openai-api-key:latest

Grant access

bash
# Grant the Cloud Run service account access to secrets
SA="astra-gateway-sa@PROJECT_ID.iam.gserviceaccount.com"

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:$SA" \
  --role="roles/secretmanager.secretAccessor"

Rotating secrets

Secret Manager is versioned — adding a new version does not delete the old one. Services keep running on the old version until they are redeployed or restarted.

bash
# Update a secret value (creates a new version, old version stays)
echo -n "new-value" | gcloud secrets versions add astra-jwt-secret --data-file=-

# For Cloud Run: force a new revision to pick up the new version
gcloud run services update astra-gateway --region us-central1

# For Firebase App Hosting: redeploy from the Firebase console or push a commit

BUILD vs RUNTIME availability

AvailabilityWhen injectedUse for
BUILDDuring npm run buildVariables needed at SSR build time (e.g. public API URLs)
RUNTIMEWhen the container startsAll credentials — never expose at build time
[BUILD, RUNTIME]Both phasesDATABASE_URL if migrations run at build time

See also: GCP Deployment for the full Cloud Run + Cloud SQL setup, Workspace Secrets for runtime secret injection into agents.