Skip to content

CI/CD with OIDC

Kyku supports OIDC-based authentication for CI/CD platforms. No long-lived access keys needed — temporary credentials are exchanged for each deployment.

Platform AWS GCP Hetzner DO
GitHub Actions ✅ STS AssumeRoleWithWebIdentity ✅ Workload Identity Federation Token env var Token env var
GitLab CI Token env var Token env var
CircleCI Token env var Token env var

Hetzner and DigitalOcean do not support OIDC federation. Use their API tokens as env vars in CI secrets.

.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: oven-sh/setup-bun@v2
- uses: actions/checkout@v4
- name: Drift check
run: kyku plan --env=prod --detailed-exitcode
env:
KYKU_AWS_ROLE_ARN: arn:aws:iam::123456789012:role/KykuDeployRole
KYKU_PASSPHRASE: ${{ secrets.KYKU_PASSPHRASE }}
- name: Deploy
run: kyku apply --env=prod --auto-approve
env:
KYKU_AWS_ROLE_ARN: arn:aws:iam::123456789012:role/KykuDeployRole
KYKU_PASSPHRASE: ${{ secrets.KYKU_PASSPHRASE }}

Kyku auto-detects the GitHub Actions OIDC token at ACTIONS_ID_TOKEN_REQUEST_URL and exchanges it via STS AssumeRoleWithWebIdentity.

Prerequisites:

  1. Create an IAM OIDC identity provider for GitHub (token.actions.githubusercontent.com)
  2. Create a role with a trust policy for your repo
  3. Set KYKU_AWS_ROLE_ARN as a GitHub Actions secret
name: Deploy
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123/locations/global/workloadIdentityPools/my-pool/providers/github
service_account: [email protected]
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Deploy
run: kyku apply --env=prod --auto-approve
env:
KYKU_PASSPHRASE: ${{ secrets.KYKU_PASSPHRASE }}

Or let Kyku handle OIDC directly by setting env vars:

env:
KYKU_GCP_WORKLOAD_PROVIDER: projects/123/...
KYKU_GCP_SERVICE_ACCOUNT: [email protected]
KYKU_GCP_PROJECT_ID: my-project
.gitlab-ci.yml
deploy:
stage: deploy
image: oven/bun:latest
id_tokens:
INFRA_OIDC_TOKEN:
aud: https://gitlab.com
variables:
KYKU_AWS_ROLE_ARN: arn:aws:iam::123456789012:role/KykuDeployRole
script:
- kyku plan --env=prod
- kyku apply --env=prod --auto-approve
version: 2.1
orbs:
aws-cli: circleci/[email protected]
jobs:
deploy:
docker:
- image: oven/bun:latest
steps:
- checkout
- aws-cli/setup:
role-arn: arn:aws:iam::123456789012:role/KykuDeployRole
- run: kyku apply --env=prod --auto-approve

Use plan files for review gates:

- name: Generate Plan
run: kyku plan --env=prod --out=plan.json
- name: Upload Plan Artifact
uses: actions/upload-artifact@v4
with:
name: plan
path: plan.json
- name: Apply (manual trigger)
if: github.event_name == 'workflow_dispatch'
run: kyku apply --env=prod --auto-approve --plan=plan.json
name: Deploy
on:
push:
branches:
- main # → prod
- dev # → dev
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref_name == 'main' && 'prod' || 'dev' }}
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: kyku apply --env=${{ github.ref_name == 'main' && 'prod' || 'dev' }} --auto-approve
env:
KYKU_AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
KYKU_PASSPHRASE: ${{ secrets.KYKU_PASSPHRASE }}

Set KYKU_PASSPHRASE as a secret in your CI platform to enable state encryption:

Terminal window
# During apply, passphrase decrypts secrets for provider operations
export KYKU_PASSPHRASE="your-secure-passphrase"

Without the passphrase, plan works but shows [encrypted] for secret fields.