# How to deploy to a Quake AI Kubernetes cluster from CI

Source: https://docs.quake.ai/docs/kubernetes/how-to/deploy-from-ci
Markdown: https://docs.quake.ai/docs/kubernetes/how-to/deploy-from-ci.md

---

# How to deploy to a Quake AI Kubernetes cluster from CI

Apply manifests or Helm releases from GitHub Actions or GitLab CI to a [Magnum](/docs/kubernetes/how-to/create-cluster) cluster. Fetch a fresh kubeconfig at job start with application credentials. Do not commit kubeconfig files to the repository.



Store [application credentials](/docs/tools/generate-app-credentials) in CI secrets. Each job runs `openstack coe cluster config` to write a kubeconfig for that run. Rotate credentials on the same schedule you use for other automation accounts.



<PrerequisiteBlock>

- A Magnum cluster in `CREATE_COMPLETE` state
- `python-openstackclient` and `python-magnumclient` in the CI job. `python-magnumclient` registers the `openstack coe` plugin.
- Application credentials with access to the cluster's project
- `kubectl` in the job (and Helm, if you run the Helm workflow). GitHub-hosted `ubuntu-latest` runners include both. The GitLab tab installs them on `python:3.12-slim`.

</PrerequisiteBlock>

## CI secrets for OpenStack auth

| Secret | Maps to |
|---|---|
| `OS_AUTH_URL` | Keystone endpoint |
| `OS_AUTH_TYPE` | `v3applicationcredential` |
| `OS_APPLICATION_CREDENTIAL_ID` | Application credential ID |
| `OS_APPLICATION_CREDENTIAL_SECRET` | Application credential secret |
| `OS_REGION_NAME` | Quake AI region |
| `OS_PROJECT_ID` | Project UUID |
| `CLUSTER_NAME` | Magnum cluster name |

See [OpenTofu CI/CD secrets](/docs/automation/how-to/cicd-integration#secrets-configuration) for the full variable set.

## Workflow: kubectl apply




```yaml
name: Deploy to Kubernetes
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install OpenStack CLI
        run: pip install python-openstackclient python-magnumclient
      - name: Fetch kubeconfig
        env:
          OS_AUTH_URL: ${{ secrets.OS_AUTH_URL }}
          OS_AUTH_TYPE: v3applicationcredential
          OS_APPLICATION_CREDENTIAL_ID: ${{ secrets.OS_APPLICATION_CREDENTIAL_ID }}
          OS_APPLICATION_CREDENTIAL_SECRET: ${{ secrets.OS_APPLICATION_CREDENTIAL_SECRET }}
          OS_REGION_NAME: ${{ secrets.OS_REGION_NAME }}
          OS_PROJECT_ID: ${{ secrets.OS_PROJECT_ID }}
        run: |
          openstack coe cluster config "${{ secrets.CLUSTER_NAME }}" --dir "$RUNNER_TEMP/kube"
          echo "KUBECONFIG=$RUNNER_TEMP/kube/config" >> "$GITHUB_ENV"
      - name: Apply manifests
        run: kubectl apply -f k8s/
```




```yaml
deploy:
  image: python:3.12-slim
  before_script:
    - apt-get update && apt-get install -y --no-install-recommends curl ca-certificates
    - pip install python-openstackclient python-magnumclient
    - curl -fsSL -o /usr/local/bin/kubectl "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/$(dpkg --print-architecture)/kubectl"
    - chmod +x /usr/local/bin/kubectl
    - curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
    - export OS_AUTH_TYPE=v3applicationcredential
    - openstack coe cluster config "$CLUSTER_NAME" --dir "$CI_PROJECT_DIR/.kube"
    - export KUBECONFIG="$CI_PROJECT_DIR/.kube/config"
  script:
    - kubectl apply -f k8s/
```




## Workflow: Helm upgrade

After the kubeconfig step, run:

```bash
helm upgrade --install myapp ./chart \
  --namespace my-namespace --create-namespace \
  --set image.repository=ghcr.io/USERNAME/myapp \
  --set image.tag="${CI_COMMIT_SHA}"
```

Configure [image pull secrets](/docs/kubernetes/how-to/use-container-registry) when the chart pulls from a private registry.

## See also

- [How to manage a Kubernetes cluster](/docs/kubernetes/how-to/manage-cluster)
- [How to deploy a Helm chart on Magnum](/docs/kubernetes/how-to/deploy-helm-chart)
- [How to use a container registry with Quake AI](/docs/kubernetes/how-to/use-container-registry)
- [Kubernetes FAQ](/docs/kubernetes/faq)
