> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbifrost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# GCP Deployment

> Deploy Bifrost Enterprise on GCP using Artifact Registry with Workload Identity

Bifrost Enterprise images for GCP customers are distributed through GCP Artifact Registry, enabling native Workload Identity for secure, keyless authentication.

## Architecture

```mermaid theme={null}
flowchart LR
    subgraph GCP[GCP Project]
        subgraph GKE[GKE Cluster]
            Pod[Bifrost Pod]
            KSA[K8s ServiceAccount]
        end
        GSA[GCP Service Account]
        AR[Artifact Registry<br/>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

<Note>
  Contact the Bifrost team with your GCP project ID and service account email to get access configured.
</Note>

## 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 theme={null}
# 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 theme={null}
# 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 theme={null}
kubectl create namespace bifrost
```

```yaml theme={null}
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 theme={null}
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 theme={null}
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
```

<Warning>
  Replace `REGION` with your Artifact Registry region (e.g., `us-central1`).
</Warning>

### Step 7: Deploy Bifrost

```yaml theme={null}
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 theme={null}
# 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 theme={null}
# 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 theme={null}
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)

<Warning>
  Service account keys are not recommended for production. Use Workload Identity instead.
</Warning>

For environments that cannot use Workload Identity:

```bash theme={null}
# 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 theme={null}
# 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 theme={null}
# 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 theme={null}
# 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 theme={null}
# 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
