GITEA_AUTH_HEADER in GCP Secret Manager is the raw base64 credential, not the full header value. The 'Basic ' prefix is required. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backstage Helm Chart
Helm chart for deploying the Backstage Developer Portal for the OnlineSales ephemeral testing platform.
Overview
This chart deploys Backstage with:
- Cloud SQL PostgreSQL database (via Cloud SQL Proxy)
- External Secrets Operator integration for GCP Secret Manager
- Ingress configuration for external access
- ServiceAccount with Workload Identity for GCP integration
Prerequisites
- Kubernetes cluster 1.25+
- Helm 3.0+
- External Secrets Operator installed
- Cloud SQL PostgreSQL instance provisioned
- GCP Service Account for Workload Identity
- ClusterSecretStore configured (see below)
Installation
Quick Install
helm install backstage ./backstage-chart \
--namespace backstage \
--create-namespace \
-f values.yaml
With Custom Values
helm install backstage ./backstage-chart \
--namespace backstage \
--create-namespace \
--set image.tag=latest \
--set ingress.hosts[0].host=backstage.example.com
Configuration
Key Values
| Parameter | Description | Default |
|---|---|---|
image.repository |
Backstage image repository | gcr.io/prj-onlinesales-test-framework/backstage |
image.tag |
Backstage image tag | latest |
replicaCount |
Number of replicas | 1 |
serviceAccount.name |
ServiceAccount name | backstage |
serviceAccount.annotations |
SA annotations (Workload Identity) | {} |
ingress.enabled |
Enable ingress | true |
ingress.hosts |
Ingress hostnames | [] |
externalSecrets.enabled |
Enable External Secrets | true |
externalSecrets.secretStore |
SecretStore reference | gcp-secret-store |
cloudSqlProxy.enabled |
Enable Cloud SQL Proxy sidecar | true |
cloudSqlProxy.instanceConnectionName |
Cloud SQL connection string | "" |
External Secrets Configuration
The chart uses External Secrets Operator to sync secrets from GCP Secret Manager:
externalSecrets:
enabled: true
secretStore: gcp-secret-store # ClusterSecretStore name
refreshInterval: 1h
secrets:
# Backstage database credentials
POSTGRES_HOST: backstage-db/host
POSTGRES_PORT: backstage-db/port
POSTGRES_USER: backstage-db/username
POSTGRES_PASSWORD: backstage-db/password
# Integration credentials
GITEA_URL: backstage-secrets/GITEA_URL
GITEA_USERNAME: backstage-secrets/GITEA_USERNAME
GITEA_PASSWORD: backstage-secrets/GITEA_PASSWORD
ARGOCD_AUTH_TOKEN: backstage-secrets/ARGOCD_AUTH_TOKEN
ARGO_WORKFLOWS_TOKEN: backstage-secrets/ARGO_WORKFLOWS_TOKEN
Cloud SQL Proxy Setup
The chart deploys Cloud SQL Proxy as a sidecar:
cloudSqlProxy:
enabled: true
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.8.0
instanceConnectionName: "project:region:instance"
port: 5432
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
Workload Identity Setup:
# Create GCP service account
gcloud iam service-accounts create backstage-sa \
--display-name="Backstage Service Account"
# Grant Cloud SQL Client role
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:backstage-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
# Configure Workload Identity
gcloud iam service-accounts add-iam-policy-binding \
backstage-sa@$PROJECT_ID.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:$PROJECT_ID.svc.id.goog[backstage/backstage]"
# Annotate Kubernetes SA
kubectl annotate serviceaccount backstage \
--namespace backstage \
iam.gke.io/gcp-service-account=backstage-sa@$PROJECT_ID.iam.gserviceaccount.com
Database Setup
-
Create Cloud SQL Instance:
gcloud sql instances create backstage-db \ --database-version=POSTGRES_14 \ --tier=db-f1-micro \ --region=$REGION \ --root-password=$ROOT_PASSWORD -
Create Database:
gcloud sql databases create backstage --instance=backstage-db -
Create User:
gcloud sql users create backstage_user \ --instance=backstage-db \ --password=$USER_PASSWORD -
Store Credentials in Secret Manager:
echo -n "backstage_user" | gcloud secrets create backstage-db-username --data-file=- echo -n "$USER_PASSWORD" | gcloud secrets create backstage-db-password --data-file=-
RBAC Setup
Backstage requires cluster-wide permissions for environment provisioning:
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: backstage-environment-manager
rules:
- apiGroups: [""]
resources: ["namespaces", "configmaps", "secrets", "services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["argoproj.io"]
resources: ["applications", "workflows", "cronworkflows"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: backstage-environment-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: backstage-environment-manager
subjects:
- kind: ServiceAccount
name: backstage
namespace: backstage
EOF
Troubleshooting
Pod not starting
# Check pod status
kubectl get pods -n backstage
# Check logs
kubectl logs -f -n backstage -l app.kubernetes.io/name=backstage
# Check events
kubectl describe pod -n backstage -l app.kubernetes.io/name=backstage
External Secrets not syncing
# Check ExternalSecret status
kubectl get externalsecret -n backstage
# Describe for errors
kubectl describe externalsecret backstage-secrets -n backstage
# Check SecretStore
kubectl get clustersecretstore gcp-secret-store
Cloud SQL Proxy connection issues
# Check proxy logs
kubectl logs -n backstage -l app.kubernetes.io/name=backstage -c cloud-sql-proxy
# Test connectivity
kubectl exec -n backstage -it <pod-name> -c backstage -- \
psql -h localhost -U backstage_user -d backstage -c "SELECT 1"
Database migration issues
# Check Backstage logs for migration errors
kubectl logs -n backstage -l app.kubernetes.io/name=backstage | grep -i migration
Upgrading
helm upgrade backstage ./backstage-chart \
--namespace backstage \
-f values.yaml
Uninstalling
helm uninstall backstage --namespace backstage
kubectl delete namespace backstage
Related Documentation
License
Proprietary - OnlineSales.ai
Description
Languages
Smarty
100%