Kubernetes Cluster provisioning, without hardcoded secrets

2026-05-27
Thalassa Cloud
6 min read

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.

Why Use OIDC?

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.

What You Need

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).

Setting up the CI/CD flow

Step 1: Create a Service Account

Create a service account that your GitLab CI will use to manage Kubernetes clusters.

  1. Go to the Thalassa Cloud Console and log in
  2. Open Identity & Access ManagementService Accounts
  3. Click Create Service Account
  4. Name it gitlab-ci-kubernetes (or any name you prefer)
  5. Give it the Kubernetes Admin role (or a custom role that can manage clusters)
  6. Copy the Service Account ID - you’ll need it in Step 4

Step 2: Set Up OIDC Identity Provider

Tell Thalassa Cloud to trust tokens from GitLab.

  1. In the console, go to Identity & Access ManagementOIDCIdentity Providers
  2. Click Create Identity Provider
  3. Fill in the name as 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.
  4. Click Create Identity Provider

Step 3: Create Federated Identity

Link your GitLab CI tokens to the service account.

  1. Open your service account → Federated IdentitiesCreate Federated Identity
  2. Choose the GitLab provider from Step 2. For the Subject Claim, which controls which GitLab projects can use this identity, use this pattern:
    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.
  3. Click Create Federated Identity

For more details on subject claims, see the GitLab CI OIDC documentation.

Step 4: Add GitLab CI Variables

Add your Thalassa Cloud IDs to GitLab so the pipeline can use them.

  1. In your GitLab project, go to SettingsCI/CD
  2. Expand Variables
  3. Click Add variable and create the first variable with the key 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.

Step 5: Create Terraform Configuration

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.

Step 6: Create GitLab CI Pipeline

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.

Step 7: Deploy Your Cluster

Commit and push main.tf and .gitlab-ci.yml to your GitLab repository. Then go to CI/CDPipelines 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.

What You Get

  • Your cluster includes high availability with multiple nodes for reliability.
  • It has autoscaling that automatically adds or removes nodes between 3-10 nodes based on your workload.
  • Kubernetes updates happen automatically, so you don’t need to manage versions.
  • The cluster runs in your VPC with proper network isolation for security. You get complete Kubernetes API access for deploying your workloads.

Learn More

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!


Related posts