---
title: "GCP Deployment"
description: "Deploy Bifrost Enterprise on GCP using Artifact Registry with Workload Identity"
icon: "google"
---
Bifrost Enterprise images for GCP customers are distributed through GCP Artifact Registry, enabling native Workload Identity for secure, keyless authentication.
## Architecture
```mermaid
flowchart LR
subgraph GCP[GCP Project]
subgraph GKE[GKE Cluster]
Pod[Bifrost Pod]
KSA[K8s ServiceAccount]
end
GSA[GCP Service Account]
AR[Artifact Registry
Bifrost Images]
end
KSA -->|Workload Identity| GSA
Pod -->|Impersonates| GSA
GSA -->|Pull Permission| AR
AR -->|Image| Pod
```
## Prerequisites
- GKE cluster (v1.24+) with Workload Identity enabled
- `gcloud` CLI configured with appropriate permissions
- `kubectl` configured for your GKE cluster
- Your GCP project allowlisted by Bifrost team
Contact the Bifrost team with your GCP project ID and service account email to get access configured.
## Workload Identity (Recommended)
Workload Identity provides the most secure authentication method for GKE deployments by eliminating the need for service account keys.
### Step 1: Enable Workload Identity on GKE
If not already enabled, enable Workload Identity on your cluster:
```bash
# For existing cluster
gcloud container clusters update YOUR_CLUSTER_NAME \
--region=YOUR_REGION \
--workload-pool=YOUR_PROJECT_ID.svc.id.goog
# Verify Workload Identity is enabled
gcloud container clusters describe YOUR_CLUSTER_NAME \
--region=YOUR_REGION \
--format="value(workloadIdentityConfig.workloadPool)"
```
### Step 2: Create GCP Service Account
Create a service account that will be used to pull images:
```bash
# Create service account
gcloud iam service-accounts create bifrost-pull-sa \
--display-name="Bifrost Image Pull SA" \
--project=YOUR_PROJECT_ID
```
### Step 3: Request Access from Bifrost Team
Provide the following to the Bifrost team:
- Your GCP project ID
- Service account email: `bifrost-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com`
The Bifrost team will grant the necessary permissions to pull images from the registry.
### Step 4: Create Namespace and ServiceAccount
```bash
kubectl create namespace bifrost
```
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bifrost-sa
namespace: bifrost
annotations:
iam.gke.io/gcp-service-account: bifrost-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com
```
### Step 5: Bind Kubernetes SA to GCP SA
Allow the Kubernetes ServiceAccount to impersonate the GCP Service Account:
```bash
gcloud iam service-accounts add-iam-policy-binding \
bifrost-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:YOUR_PROJECT_ID.svc.id.goog[bifrost/bifrost-sa]"
```
### Step 6: Create Image Pull Secret with Token Refresh
Artifact Registry tokens expire after 60 minutes. Use a CronJob to refresh the imagePullSecret:
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: refresh-ar-secret
namespace: bifrost
spec:
schedule: "*/30 * * * *" # Every 30 minutes
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
serviceAccountName: bifrost-sa
containers:
- name: token-refresh
image: google/cloud-sdk:slim
command: ["/bin/bash", "-c"]
args:
- |
set -e
# Get access token using Workload Identity
TOKEN=$(gcloud auth print-access-token)
# Delete existing secret if it exists
kubectl delete secret ar-pull-secret --ignore-not-found -n bifrost
# Create new imagePullSecret
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=oauth2accesstoken \
--docker-password="$TOKEN" \
-n bifrost
echo "Secret refreshed at $(date)"
restartPolicy: OnFailure
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-manager
namespace: bifrost
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secret-manager-binding
namespace: bifrost
subjects:
- kind: ServiceAccount
name: bifrost-sa
namespace: bifrost
roleRef:
kind: Role
name: secret-manager
apiGroup: rbac.authorization.k8s.io
```
Replace `REGION` with your Artifact Registry region (e.g., `us-central1`).
### Step 7: Deploy Bifrost
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: bifrost
namespace: bifrost
spec:
replicas: 2
selector:
matchLabels:
app: bifrost
template:
metadata:
labels:
app: bifrost
spec:
serviceAccountName: bifrost-sa
imagePullSecrets:
- name: ar-pull-secret
containers:
- name: bifrost
image: REGION-docker.pkg.dev/BIFROST_PROJECT/YOUR_HUB_SLUG/bifrost:latest
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumeMounts:
- name: config
mountPath: /app/data/config.json
subPath: config.json
volumes:
- name: config
secret:
secretName: bifrost-config
---
apiVersion: v1
kind: Service
metadata:
name: bifrost
namespace: bifrost
spec:
selector:
app: bifrost
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
```
### Bootstrap: Initial Secret Creation
Before the first deployment, manually create the initial imagePullSecret:
```bash
# Authenticate gcloud
gcloud auth login
# Create initial secret
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=oauth2accesstoken \
--docker-password="$(gcloud auth print-access-token)" \
-n bifrost
```
## Service Account Impersonation
For cross-project deployments or when you need to use an existing service account:
### Configure Impersonation
```bash
# Grant impersonation permission
gcloud iam service-accounts add-iam-policy-binding \
BIFROST_PROVIDED_SA@BIFROST_PROJECT.iam.gserviceaccount.com \
--role=roles/iam.serviceAccountTokenCreator \
--member="serviceAccount:bifrost-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com"
```
### Token Refresh with Impersonation
Update the CronJob to use impersonation:
```yaml
args:
- |
set -e
# Get access token by impersonating the Bifrost SA
TOKEN=$(gcloud auth print-access-token \
--impersonate-service-account=BIFROST_PROVIDED_SA@BIFROST_PROJECT.iam.gserviceaccount.com)
kubectl delete secret ar-pull-secret --ignore-not-found -n bifrost
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=oauth2accesstoken \
--docker-password="$TOKEN" \
-n bifrost
```
## Service Account Key (Legacy)
Service account keys are not recommended for production. Use Workload Identity instead.
For environments that cannot use Workload Identity:
```bash
# Create key (provided by Bifrost team)
# Store key securely
# Create imagePullSecret
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=_json_key \
--docker-password="$(cat sa-key.json)" \
-n bifrost
```
## Verifying Access
### Test Artifact Registry Authentication
```bash
# Configure docker for Artifact Registry
gcloud auth configure-docker REGION-docker.pkg.dev
# Pull test (requires impersonation or direct access)
docker pull REGION-docker.pkg.dev/BIFROST_PROJECT/YOUR_HUB_SLUG/bifrost:latest
```
### Verify Workload Identity Configuration
```bash
# Check ServiceAccount annotation
kubectl get sa bifrost-sa -n bifrost -o yaml
# Verify pod can authenticate
kubectl exec -it deployment/bifrost -n bifrost -- \
gcloud auth print-access-token
# Check token refresh CronJob
kubectl get cronjob refresh-ar-secret -n bifrost
kubectl get jobs -n bifrost
```
## Troubleshooting
### ImagePullBackOff Errors
1. **Check imagePullSecret exists**: `kubectl get secret ar-pull-secret -n bifrost`
2. **Verify token is valid**: Check if CronJob ran successfully
3. **Check Workload Identity binding**: Ensure GCP SA is bound to K8s SA
```bash
# Check pod events
kubectl describe pod -l app=bifrost -n bifrost
# Manually refresh token
kubectl create job --from=cronjob/refresh-ar-secret manual-refresh -n bifrost
```
### Workload Identity Issues
```bash
# Verify Workload Identity pool
gcloud container clusters describe YOUR_CLUSTER_NAME \
--region=YOUR_REGION \
--format="value(workloadIdentityConfig.workloadPool)"
# Check IAM binding
gcloud iam service-accounts get-iam-policy \
bifrost-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com
```
### Token Expiration
If pods fail to pull images after 60 minutes:
1. Verify CronJob is running: `kubectl get cronjob -n bifrost`
2. Check CronJob logs: `kubectl logs -l job-name=refresh-ar-secret -n bifrost`
3. Manually trigger refresh: `kubectl create job --from=cronjob/refresh-ar-secret manual-refresh -n bifrost`
## Next Steps
- Configure [Bifrost settings](/quickstart/gateway/setting-up) for your use case
- Set up [observability](/features/observability/default) for monitoring
- Enable [clustering](/enterprise/clustering) for high availability