Learn GitLab CI/CD - Continuous Deployment to Kubernetes with the GitLab Agent
Episode 16 of 21

Learn GitLab CI/CD - Continuous Deployment to Kubernetes with the GitLab Agent

Deploy applications to Kubernetes without opening the API server to the public. You'll connect your cluster via the GitLab Agent, run kubectl and helm upgrade directly from the pipeline, and apply GitOps patterns with ArgoCD and Flux.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In the previous episode 15 we deployed applications to Linux servers via SSH and Ansible. Now we go up a level: Kubernetes. The difference is fundamental — we're no longer talking about one host with one process manager, but a cluster that can move pods between nodes, perform rolling updates, and keep applications alive. But this cluster also has a much larger attack surface: the Kubernetes API server is a door that, if opened to the internet, can be targeted by scanning bots within minutes.

That's why the "old" method — exposing the API server to a public IP and placing a kubeconfig on runners — is no longer recommended. This episode covers the GitLab Agent for Kubernetes (KAS): a secure bridge between GitLab and your cluster, how to deploy manifests and Helm charts through the pipeline, and how ArgoCD and Flux round out the GitOps flow.

Why the API Server Must Not Be Opened to the Public

Imagine your house: the Kubernetes API server is its front door. Anyone who can touch that door can try picking the lock or knocking repeatedly (brute force). Keeping the door closed is a fundamental principle: the control plane and worker nodes should be on a private network, and kubeconfig credentials should never stay in runners used by many projects.

The GitLab Agent solves this with an outbound connection pattern. The agent runs as a pod inside the cluster and makes outbound connections to the GitLab server — not the other way around. The firewall keeps the API server port closed to the internet, yet GitLab pipelines can still send kubectl commands to the cluster. Think of a call center: instead of your team having to come to the company building, the company's staff are ready to be reached over an always-open channel.

Aspectkubeconfig + Public API ServerGitLab Agent (KAS)
Connection directionInbound from runner to clusterOutbound from cluster to GitLab
API server portOpen to the internetClosed from the internet
Credentialskubeconfig stored on the runnerAgent token in a Kubernetes Secret
Audit trailMinimalVisible on the GitLab agent page

Installing the GitLab Agent for Kubernetes

The first step is registering the agent in GitLab. Open Operate → Kubernetes clusters, select Connect a cluster, and choose the project that will use the agent. GitLab generates an agent token. Agent configuration is stored in the repository as a YAML file:

Kubernetes.gitlab/agents/my-agent/config.yaml
ci_access:
  projects:
    - id: my-group/my-app

The ci_access block determines which projects are allowed to use this agent through CI. Without this block, no project can call kubectl from the pipeline — this is what keeps the agent usable only by authorized teams.

Next, install the agent in the cluster using the official Helm chart:

Install the agent in the cluster via Helm
helm repo add gitlab https://charts.gitlab.io
helm repo update
 
helm upgrade --install my-agent gitlab/gitlab-agent \
  --namespace gitlab-agent \
  --create-namespace \
  --set config.kasAddress=wss://kas.gitlab.com \
  --set config.token=<isi-dengan-token-agent>

The config.kasAddress value is the GitLab KAS server address — for GitLab.com it's wss://kas.gitlab.com, while for a self-hosted installation, adjust it to your KAS domain. Once the agent pod appears in the gitlab-agent namespace, the agent status on the Operate page changes to Connected.

Warning

The agent token is privileged credentials. Don't commit the token to a repository, don't put it in logs, and don't write it directly in the install command — use a Kubernetes Secret and reference it via config.secretName. If the token leaks, revoke it on the agent page and repeat the installation.

Deploying Manifests with kubectl

With the agent connected, GitLab pipelines automatically receive the KUBE_CONTEXT and KUBE_NAMESPACE variables — as long as the job runs in a project with ci_access access. These are the magic keys that make kubectl work without storing a kubeconfig anywhere:

Deploy manifest job via the agent
deploy_manifest:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - kubectl apply -f k8s/deployment.yaml
    - kubectl rollout status deployment/my-app --timeout=120s
  environment:
    name: production
    url: https://myapp.example.com

Notice there's no kubectl config set-cluster or kubeconfig file upload — everything is handled through the agent's built-in context. The kubectl rollout status command makes the job wait until the Deployment is truly stable, so if the new image fails to start (CrashLoopBackOff), the job fails and the pipeline stops before declaring success.

Automating Helm Upgrades

For applications with many components (service, ingress, configmap, secret), Helm is a much cleaner choice. We can use the same agent to run helm upgrade --install:

helm upgrade job via the agent
deploy_helm:
  stage: deploy
  image: alpine/helm:latest
  variables:
    KUBE_CONTEXT: my-group/my-app:prod-agent
    KUBE_NAMESPACE: production
  script:
    - helm dependency update ./chart
    - helm upgrade --install my-app ./chart
      --namespace production
      --set image.tag=$CI_COMMIT_SHA
  environment:
    name: production
    url: https://myapp.example.com

The KUBE_CONTEXT variable format is path/main-project:<agent-name> — in this example project my-group/my-app uses the prod-agent agent. The release name is my-app, the chart is in the ./chart folder, and the image tag comes from $CI_COMMIT_SHA so every commit produces a unique version. Because --install is included, the same command works for both the first deploy and subsequent upgrades.

Tip

Use one agent per important environment (for example separate agents for staging and production). That way each agent's ci_access boundary can differ, and a mistake in staging never grants access to production.

GitOps Workflow with GitLab

Besides deploying directly from the pipeline (push-based), there's the GitOps pattern: the Git repository becomes the single source of truth for the cluster state. Operators inside the cluster pull changes from Git, rather than the pipeline pushing them to the cluster.

ArgoCD and Flux CD are the two most popular implementations. Both can take their source directly from a GitLab repository:

ArgoCDArgoCD Application from a GitLab repo
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://gitlab.com/my-group/my-app.git
    targetRevision: main
    path: helm-chart
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

ArgoCD will compare the cluster state with the state in the GitLab repository. Any change merged into main is pulled and synced to the cluster — making GitLab the home of the application code while also serving as the "remote config" for the cluster.

Flux CD uses similar custom resources named GitRepository and Kustomization, with the same principle. This is where the push-based pattern (helm upgrade from the pipeline) and the pull-based pattern (GitOps operators) can be combined: the pipeline keeps building images and tagging versions, while the GitOps operator decides when to apply that version — full control in the operator's hands, not a bot's.

Closing

This episode connected GitLab to Kubernetes in a secure way:

  • The GitLab Agent (KAS) opens an outbound channel from the cluster to GitLab without opening the API server to the internet.
  • The ci_access config in .gitlab/agents/my-agent/config.yaml restricts who may use the agent.
  • The KUBE_CONTEXT and KUBE_NAMESPACE variables make kubectl and helm work without a kubeconfig on the runner.
  • Helm upgrades can be automated from the pipeline for repeatable deploys and upgrades.
  • ArgoCD and Flux CD enable a pull-based GitOps pattern from a GitLab repository.

Now you can deploy to Kubernetes. But deploying alone isn't enough — big releases still carry risk. In the next episode 17 we cover progressive delivery: canary and blue-green deployments, plus automatic rollback when metrics warn of failure. See you there!

Learn GitLab CI/CD - Continuous Deployment to Kubernetes with the GitLab Agent | Learn GitLab CI/CD