Production GitOps with GitLab: Continuous Delivery with the GitLab Agent for Kubernetes
For years, continuous delivery to Kubernetes from CI/CD pipelines relied on a push-based model:
- You created a long-lived
kubeconfigor ServiceAccount token inside your cluster. - You pasted that secret into your CI/CD repository settings.
- Your CI/CD runner reached through your firewall into the Kubernetes API server to run
kubectl apply.
While push-based deployment is simple to set up, it introduces severe enterprise security liabilities:
- Cluster Exposure: Your Kubernetes API server must be reachable from the internet or public CI runners.
- Credential Sprawl: Storing cluster-admin credentials inside CI variables creates a high-value attack vector if a pipeline or dependency is compromised.
- Configuration Drift: If someone manually changes a pod or service with
kubectl edit, the cluster silently drifts away from Git.
To solve this, GitLab introduced the GitLab Agent for Kubernetes (KAS) — a modern, pull-based GitOps engine.
In this guide, I will show you how to configure the GitLab Agent to manage your Kubernetes deployments securely with zero open inbound firewall ports.
1. Push vs. Pull-Based GitOps Architecture
+-------------------------------------------------------------+ | TRADITIONAL PUSH-BASED DEPLOYMENT | | | | GitLab CI Runner ---(Exposed API Port 6443)---> K8s API | | * High attack surface; credentials stored in CI runner. | +-------------------------------------------------------------+ vs. +-------------------------------------------------------------+ | PULL-BASED GITOPS (GITLAB AGENT / KAS) | | | | GitLab Repository (GitOps Manifests / Kustomize) | | ^ | | | Outbound Secure gRPC/WebSocket | | | (No inbound firewall openings) | | v | | In-Cluster GitLab Agent (Polls & Syncs State Directly) | | - Reconciles desired state vs. actual cluster state | | - Prevents configuration drift automatically | +-------------------------------------------------------------+ 2. Setting Up the Agent Configuration in Your Repository
In GitLab, the Agent for Kubernetes is configured entirely via version-controlled files inside your repository.
Create the configuration file at the mandatory path:
.gitlab/agents/<agent-name>/config.yaml
# .gitlab/agents/production-agent/config.yaml gitops: manifest_projects: - id: "my-org/cloud-infrastructure" paths: - glob: "manifests/**/*.{yaml,yml}" reconcile_timeout: "5m" prune: true # Automatically deletes resources removed from Git! ci_access: projects: - id: "my-org/microservices-app" Key Parameters:
-
prune: true: Ensures declarative synchronization. If you delete adeployment.yamlfrom Git, the agent automatically deletes the deployment from the cluster. -
ci_access: Allows authorized GitLab CI pipelines to securely interact with the cluster via the agent without storing raw cluster credentials.
3. Installing the Agent Inside Your Kubernetes Cluster
Step A: Register the Agent in GitLab UI
- Navigate to your GitLab project $\rightarrow$ Operate $\rightarrow$ Kubernetes clusters.
- Click Connect a cluster (agent).
- Select your agent name (
production-agent) from the dropdown. - GitLab displays a registration token and an auto-generated Helm command.
Step B: Install via Helm
Run the Helm command directly inside your cluster (or via your local kubectl admin context):
helm repo add gitlab https://charts.gitlab.io helm repo update helm upgrade --install production-agent gitlab/gitlab-agent \ --namespace gitlab-agent-production \ --create-namespace \ --set image.tag="v17.5.0" \ --set config.token="YOUR_AGENT_REGISTRATION_TOKEN" \ --set config.kasAddress="wss://kas.gitlab.com" Within seconds, the agent pod connects outbound to GitLab over secure WebSockets (wss://). In your GitLab dashboard, the cluster status instantly turns Connected (Green).
4. Declarative Synchronization in Action
Create a simple deployment manifest in your Git repository under manifests/api-service.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: api-service namespace: production labels: app: api-service spec: replicas: 3 selector: matchLabels: app: api-service template: metadata: labels: app: api-service spec: containers: - name: web image: registry.gitlab.com/my-org/microservices-app:v2.1.0 ports: - containerPort: 8080 resources: limits: cpu: "500m" memory: "512Mi" requests: cpu: "100m" memory: "128Mi" Commit and push this file to main:
git add manifests/api-service.yaml git commit -m "feat(deploy): provision api-service with 3 replicas" git push origin main The GitLab Agent detects the commit within seconds, validates the manifest, and applies it to the cluster.
5. Drift Detection & Self-Healing
What happens if an engineer enters the cluster and accidentally deletes a pod or modifies replicas?
# Simulating accidental drift kubectl scale deployment api-service -n production --replicas=1 Because GitOps treats Git as the single source of truth, the GitLab Agent detects that the cluster state (replicas: 1) diverges from Git (replicas: 3).
Within the configured reconcile_timeout window, the agent automatically scales the deployment back to 3 replicas, self-healing the cluster without human intervention!
6. Secure CI/CD Pipeline Access (Hybrid GitOps)
Sometimes a CI/CD job needs to run an imperative command against the cluster (like executing database migrations or running integration smoke tests).
With the agent configured under ci_access, your .gitlab-ci.yml can interact with the cluster securely:
deploy_migration: stage: deploy image: name: bitnami/kubectl:latest entrypoint: [""] script: # Set context to the registered GitLab Agent - kubectl config get-contexts - kubectl config use-context my-org/cloud-infrastructure:production-agent - kubectl run db-migration --image=registry.gitlab.com/my-org/db-tools:latest --restart=Never No tokens are exposed in pipeline logs, and permissions are strictly bounded by Kubernetes RBAC.
7. Key Takeaways
- Zero Inbound Attack Surface: The agent connects outbound over WebSockets (
wss://), allowing private clusters to remain completely shielded behind firewalls. - Immutable Git Audit Trail: Every deployment, scale event, or configuration change is tracked in Git commits with cryptographic signatures.
- Automated Self-Healing: Configuration drift is reconciled automatically to match Git.
Resources & Links
- GitLab Kubernetes Agent Documentation: docs.gitlab.com/ee/user/clusters/agent/
- GitLab Profile: gitlab.com/hrlpavan
- GitLab Contributor Profile: contributors.gitlab.com/users/hrlpavan
- GitHub: @hrlpavan
Are you using push or pull-based deployment for your Kubernetes clusters? Share your architecture in the comments below!
