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: deployer@my-project.iam.gserviceaccount.com
- 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: deployer@my-project.iam.gserviceaccount.com
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/aws-cli@5.0
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.