
You can deploy a Kubernetes cluster in 5 minutes using GitLab CI and Terraform—without storing any secrets in your pipeline. This guide shows you how to use OIDC (OpenID Connect) for secure authentication and deploy a production-ready cluster.
By the end, you’ll have a working pipeline that creates and manages your Kubernetes cluster automatically.
Usually, CI/CD pipelines need API tokens stored as secrets. This causes problems. You must manually rotate tokens, which is easy to forget. You need to store tokens securely in GitLab. It’s hard to see which pipeline used which token. And it’s difficult to limit what each pipeline can do.
OIDC fixes this. Instead of storing secrets, your GitLab CI jobs get short-lived tokens automatically. Each token only works for your specific project and branch. You can see exactly which pipeline used it.
Before you start, you’ll need a Thalassa Cloud account with an organisation set up. You also need a GitLab repository with CI/CD enabled—this works with GitLab.com or your own GitLab server. We use a Terraform module, so you don’t need deep Terraform knowledge. We’ll create the service account together in the steps below.
We are utilising Gitlab for this example, however this also works for any other CI/CD platform that supports OIDC ID Tokens for Jobs (like GitHub or Forgejo).
Create a service account that your GitLab CI will use to manage Kubernetes clusters.
gitlab-ci-kubernetes (or any name you prefer)Kubernetes Admin role (or a custom role that can manage clusters)Tell Thalassa Cloud to trust tokens from GitLab.
gitlab-ci. For the Issuer URL, use https://gitlab.com if you’re using GitLab.com, or your GitLab URL (like https://gitlab.example.com) if you’re using your own server. The JWKS URL fills in automatically.Link your GitLab CI tokens to the service account.
project_path:mygroup/myproject:ref_type:branch:ref:main
Change mygroup/myproject to your GitLab project path, and change main to your branch name (or use * for all branches). To allow all projects in a group, use project_path:mygroup/*:ref_type:branch:ref:*. For API Scopes, select read and write.For more details on subject claims, see the GitLab CI OIDC documentation.
Add your Thalassa Cloud IDs to GitLab so the pipeline can use them.
THALASSA_ORGANISATION_ID. Set the value to your organisation ID (you can find it in the console URL or settings). Make sure to check both Protected and Masked. Then add a second variable with the key THALASSA_SERVICE_ACCOUNT_ID and the value from the service account ID you copied in Step 1. Again, check Protected and Masked.Create the Terraform file that will deploy your Kubernetes cluster. We use a module to make this simple.
Create a file named main.tf in your repository:
terraform {
required_providers {
thalassa = {
source = "thalassa-cloud/thalassa"
version = "~> 0.6.0"
}
}
}
provider "thalassa" {
organisation_id = var.organisation_id
# access_token will be read from THALASSA_ACCESS_TOKEN environment variable
region = "nl-01"
}
variable "organisation_id" {
description = "Thalassa Cloud organisation ID"
type = string
}
# Optional: Create a VPC first if you don't have one
data "thalassa_vpc" "main" {
name = "default-vpc" # Adjust to your VPC name
}
# Deploy Kubernetes cluster using the module
module "kubernetes" {
source = "github.com/thalassa-cloud/terraform-thalassa-kubernetes//modules/kubernetes_cluster?ref=main"
name = "production-cluster"
description = "Production Kubernetes cluster deployed via GitLab CI"
organisation_id = var.organisation_id
region = "nl-01"
vpc_id = data.thalassa_vpc.main.id
# Use a subnet from your VPC
subnet_id = data.thalassa_vpc.main.subnet_ids[0]
# Node pool configuration
nodepools = {
default = {
machine_type = "pgp.medium"
availability_zones = ["nl-01"]
replicas = 3
subnet_id = data.thalassa_vpc.main.subnet_ids[0]
# Enable autoscaling for production
enable_auto_scaling = true
min_replicas = 3
max_replicas = 10
labels = {
environment = "production"
managed-by = "terraform"
}
}
}
labels = {
environment = "production"
managed-by = "terraform"
deployed-by = "gitlab-ci"
}
}
# Output the cluster information
output "cluster_id" {
value = module.kubernetes.cluster_id
description = "The ID of the Kubernetes cluster"
}
output "cluster_endpoint" {
value = module.kubernetes.cluster_endpoint
description = "The API endpoint of the Kubernetes cluster"
sensitive = true
}
For a another example, see the simple example.
Create a .gitlab-ci.yml file in your repository root:
stages:
- plan
- apply
variables:
THALASSA_API: "https://api.thalassa.cloud"
TF_ROOT: ${CI_PROJECT_DIR}
TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_COMMIT_REF_NAME}
.terraform_init: &terraform_init
- terraform init
.oidc_auth: &oidc_auth
id_tokens:
THALASSA_ID_TOKEN:
aud: ${THALASSA_API}
before_script:
- apk add --no-cache curl jq
- |
export THALASSA_ACCESS_TOKEN=$(curl -X POST ${THALASSA_API}/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=${THALASSA_ID_TOKEN}" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
-d "organisation_id=${THALASSA_ORGANISATION_ID}" \
-d "service_account_id=${THALASSA_SERVICE_ACCOUNT_ID}" \
| jq -r '.access_token')
- export TF_VAR_organisation_id="${THALASSA_ORGANISATION_ID}"
terraform:plan:
<<: *oidc_auth
stage: plan
image: hashicorp/terraform:latest
script:
- *terraform_init
- terraform plan -out=tfplan
artifacts:
paths:
- tfplan
expire_in: 1 week
terraform:apply:
<<: *oidc_auth
stage: apply
image: hashicorp/terraform:latest
script:
- *terraform_init
- terraform apply -auto-approve tfplan
when: manual
only:
- main
dependencies:
- terraform:plan
This pipeline runs a plan stage that shows what will change on every push. The apply stage deploys the changes manually, but only on the main branch. The authentication step gets a token from GitLab and uses it to access Thalassa Cloud. Best of all, it only uses your organisation ID and service account ID, which aren’t sensitive secrets.
Commit and push main.tf and .gitlab-ci.yml to your GitLab repository. Then go to CI/CD → Pipelines to watch the pipeline run. Check the terraform:plan job to see what will be created. When you’re ready, click the play button on terraform:apply to deploy your cluster.
Your cluster will be ready in about 5 minutes.
For more details on OIDC setup, see the GitLab CI OIDC documentation. If you’re using GitHub Actions instead, check out the GitHub Actions OIDC setup guide. Explore more Kubernetes Terraform module examples for different configurations. For comprehensive Kubernetes guidance, see the Thalassa Cloud Kubernetes documentation.
Ready to deploy your first cluster? Get started with Thalassa Cloud today!