I was on call when a “sync failed” alert lit up our Slack at 02:17 AM. The Harness Delegate pod had silently dropped a GitOps task, and the downstream service rolled back to an older image—causing a brief outage that the on‑call engineer had to scramble to fix. The root cause? The delegate lost its TLS certificate mid‑rotation and never retried the API call. I spent the next three hours digging through logs, adding exponential back‑off, and finally wiring up a dead‑letter queue to catch the orphaned syncs. The lesson? Deploying a Harness GitOps agent isn’t just “kubectl apply‑and‑walk‑away.” You need a production‑grade strategy for connectivity, sizing, and placement before the first alert fires.
- Generate a secure delegate token in Harness Manager and apply the latest root‑less delegate manifest.
- Choose in‑cluster vs. external deployment based on security, latency, and cost.
- Size the delegate (CPU/Memory) using benchmark tables: ~2 CPU/4 Gi for ≤100 services, 8 CPU/16 Gi for 500+ services.
- Implement exponential back‑off, dead‑letter queues, and Prometheus alerts for loss of connectivity.
- Enable drift detection, self‑healing, and policy‑as‑code integration with OPA Gatekeeper or Kyverno.
Before you start: Kubernetes 1.30+, kubectl 1.31, Helm v4, a Harness account with Admin rights, a Git repo (GitHub, GitLab, or Bitbucket) containing your manifests, and a service mesh (Istio 1.25 or Linkerd 2.15) if you want traffic‑safe rollouts.
Harness GitOps Agent for Kubernetes: quick‑start deployment in 2026
To implement a Harness agent for GitOps with Kubernetes, generate a delegate token from Harness Manager, apply the provided Kubernetes YAML manifest to your cluster, and verify pod health. You then connect it to your Git repo to enable automated synchronization and drift detection of your Kubernetes manifests.
Why GitOps dominates modern Kubernetes workflows
GitOps turned the “run kubectl apply” ritual into a declarative, auditable process. Your Git repository becomes the single source of truth; every commit triggers a reconciliation loop that nudges the cluster back to the desired state. In 2026, most platform teams pair GitOps with continuous verification—canary analysis, automated rollbacks, and policy enforcement—so the system can self‑heal without human intervention.
The role of the Harness agent in your CI/CD pipeline
Harness CD (formerly Continuous Delivery) centralizes approvals, feature‑flag gating, and verification. The **Harness Delegate** (sometimes called the GitOps agent) is the runtime component that talks to the Harness Manager API, pulls manifest changes from Git, and applies them to your cluster. Think of it as the Glue that lets Harness orchestrate the same “sync” you’d get from Argo CD or Flux, but with extra context: approval policies, canary metrics, and integrated security scans.
**My take:** Most teams treat Argo CD or Flux as “the GitOps engine” and add Harness on top for approvals. I argue the opposite—run Harness’s Delegate as the *engine* and use Argo/Flux only for visual diffing when you need a quick glance. The Delegate’s deep integration with Harness’s verification suite pays off at scale.
—
Prerequisites and architecture planning for 2026
Kubernetes cluster and tooling requirements (2026 edition)
| Component | Minimum version | Why it matters |
|---|---|---|
| Kubernetes | 1.30+ (1.31 recommended) | Supports root‑less containers, CRD v2, and improved PodSecurityAdmission. |
| kubectl | 1.31 | Aligns with API server changes; needed for `kubectl auth reconcile`. |
| Helm | v4.0.0 | New chart‑dependency resolver and OCI registry support. |
| Harness CLI | 1.16.2 | Used for token generation and delegate registration. |
| Service Mesh | Istio 1.25 **or** Linkerd 2.15 | Provides traffic‑safe rollouts and observability hooks. |
| Prometheus Operator | 0.74 | Enables custom metrics like `harness_delegate_task_capacity`. |
**Tip:** If you already have a *production* cluster with Istio sidecars, plan to run the delegate **outside** that mesh to avoid circular dependencies during mesh upgrades.
Choosing between in‑cluster and external agent deployment models
| Aspect | In‑Cluster Delegate | External (admin) Delegate |
|---|---|---|
| Latency | Sub‑ms (same network) | Slightly higher (cross‑VPC) |
| Security surface | Shares node IAM; tighter RBAC needed | Isolated network; easier to enforce FIPS build |
| HA handling | Simple `ReplicaSet` scaling | Needs separate load‑balancer or Service Mesh ingress |
| Cost | No extra VMs, but consumes cluster resources | Additional EC2/VM cost, but can be sized independently |
**Warning:** Running the delegate in the same namespace as your workloads can expose it to noisy‑neighbor effects. In high‑throughput environments (500+ services), I recommend a dedicated admin cluster.
Security and networking pre‑checks (NetworkPolicies, RBAC)
- **NetworkPolicy** – lock down egress to only the Harness Manager endpoint (`manager.harness.io:443`) and your Git provider’s API. Example:
# apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: delegate-egress
spec:
podSelector:
matchLabels:
app: harness-delegate
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 35.190.0.0/16 # Harness manager CIDR (as of 2026)
ports:
- protocol: TCP
port: 443
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443 # Git provider
- **RBAC** – grant the delegate only the `get`, `list`, `watch`, `apply`, and `patch` verbs on the namespaces it manages. Use a `ClusterRole` with a `RoleBinding` per‑environment:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: harness-delegate-role
rules:
- apiGroups: [""]
resources: ["pods","services","configmaps","secrets"]
verbs: ["get","list","watch","apply","patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: harness-delegate-binding
subjects:
- kind: ServiceAccount
name: harness-delegate-sa
namespace: harness
roleRef:
kind: ClusterRole
name: harness-delegate-role
apiGroup: rbac.authorization.k8s.io
**Tip:** Enable `PodSecurityAdmission` with `restricted` level for the delegate’s namespace to enforce non‑root execution (the delegate now ships as a root‑less image).
—
Step‑by‑step: Installing the Harness Delegate on your cluster
Generating a secure delegate token in Harness Manager
- Install Harness CLI (version 1.16.2) on a workstation with admin access:
# harness-cli v1.16.2
curl -L https://cli.harness.io/download | sudo bash
harness login --account my-account-id
- Create a token scoped to the “GitOps” module:
# Generates a 256‑bit JWT, valid for 90 days
harness delegate token create \
--name gitops-delegate \
--module GITOPS \
--expires-in 2160h \
--description "Production GitOps delegate for prod-cluster"
Copy the token; you’ll need it in the manifest.
Applying the Kubernetes manifest (customizing for 2026 versions)
Harness now publishes a **root‑less** delegate image (`harness/delegate:2026.1.0-rootless`). The sample manifest pulls the latest tag, sets resource limits, and mounts a sidecar for certificate rotation.
# apiVersion: apps/v1
# version: v1
apiVersion: apps/v1
kind: Deployment
metadata:
name: harness-delegate
namespace: harness
labels:
app: harness-delegate
spec:
replicas: 2 # HA – see FAQ for scaling
selector:
matchLabels:
app: harness-delegate
template:
metadata:
labels:
app: harness-delegate
spec:
serviceAccountName: harness-delegate-sa
securityContext:
runAsNonRoot: true
runAsUser: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: delegate
image: harness/delegate:2026.1.0-rootless
args:
- --manager-url=https://manager.harness.io
- --delegate-token=$(DELEGATE_TOKEN)
env:
- name: DELEGATE_TOKEN
valueFrom:
secretKeyRef:
name: harness-delegate-secret
key: token
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
ports:
- containerPort: 9090
name: metrics
volumeMounts:
- name: certs
mountPath: /etc/ssl/certs
- name: cert-rotator
image: harness/cert-rotator:2026.0.3
args:
- --rotate-interval=24h
volumeMounts:
- name: certs
mountPath: /etc/ssl/certs
volumes:
- name: certs
emptyDir: {}
Create the secret that holds the token:
kubectl create secret generic harness-delegate-secret \
--from-literal=token='<PASTE_TOKEN_HERE>' \
-n harness
Apply the manifest:
kubectl apply -f harness-delegate.yaml
Verifying delegate pod health and connectivity
Use the built‑in health endpoint (`/healthz`) and Prometheus metric `harness_delegate_up`:
kubectl -n harness get pods -l app=harness-delegate -o wide
kubectl -n harness exec -it $(kubectl -n harness get pod -l app=harness-delegate -o jsonpath='{.items[0].metadata.name}') -- curl -s http://localhost:9090/healthz
You should see `{“status”:”healthy”}`. In Prometheus, the query:
harness_delegate_up{namespace="harness"} == 0
should return no results. If it does, check the pod logs for TLS handshake failures.
—
Architectural trade‑offs and configuration for scale
Sizing your delegate for optimal performance vs. cost
Benchmarks from Harness’s 2025 whitepaper (re‑tested in 2026) show a linear relationship between **tasks per second** and CPU cores, up to a saturation point at ~8 cores. Below is a quick sizing cheat sheet:
| Managed services | Expected sync frequency | Recommended CPU | Recommended Memory |
|---|---|---|---|
| ≤10 services (dev) | Every 5 min | 1 CPU | 2 Gi |
| 10‑100 services (stage) | Every 1 min | 2 CPU | 4 Gi |
| 100‑500 services (prod) | Every 30 s | 4 CPU | 8 Gi |
| >500 services (large SaaS) | < 15 s | 8 CPU | 16 Gi |
**My take:** Don’t over‑provision the delegate for “future growth.” Deploy a **ReplicaSet** of small delegates (2 CPU each) and let Harness’s scheduler distribute tasks. This reduces the impact of a single pod OOM and makes rolling upgrades painless.
Managing state: immutable vs. persistent delegate configurations
The delegate is **stateless**—it fetches its config from Harness Manager on start‑up. However, you can persist **cache** and **certificate** data on a `emptyDir` (as shown) or an `ephemeral` volume. For clusters that undergo frequent node drains, prefer a **PersistentVolumeClaim** with `ReadWriteMany` backed by an EFS‑like storage, so the delegate retains cached Git refs across restarts, cutting down clone time by ~30%.
| Config type | Pros | Cons |
|---|---|---|
| Immutable (no PVC) | Simpler; Pods are truly disposable | Cold starts on each restart; higher Git bandwidth |
| Persistent (PVC) | Faster sync after node churn | Needs extra storage; watch for PV snapshot security |
High‑availability and disaster‑recovery patterns for production
- **Replica HA** – Deploy at least two replicas in separate node pools. Harness automatically load‑balances tasks.
- **Cross‑region failover** – Run a secondary delegate in a different region (e.g., us‑west‑2). Use a **ClusterIP** with a `topologyKeys` rule to prefer local pods but fall back to remote if none are healthy.
- **Backup & restore** – Export the delegate’s configuration via the Harness API (`GET /delegate-config`) daily and store it in an encrypted bucket. In a disaster, you can spin up a fresh delegate and re‑apply the config in seconds.
—
Real‑world production gotchas and advanced error handling
Debugging common 2026 network and permission failures
| Symptom | Likely cause | Fix |
|---|---|---|
| `TLS handshake timeout` in pod logs | Expired cert rotation (root‑less image) | Ensure `cert-rotator` sidecar is running; check its logs for `rotation failed`. Increase `rotate-interval` if hitting rate limits. |
| `403 Forbidden` from Harness Manager | Delegate token missing `GITOPS` scope | Regenerate token with `–module GITOPS`. |
| `Failed to list pods` | RBAC missing `watch` on `pods` | Add `watch` verb to the `ClusterRole`. |
| `NetworkPolicy denies egress` | Incorrect `ipBlock` CIDR | Verify Harness manager CIDR via `dig manager.harness.io`. |
Implementing robust retry logic for intermittent API failures
The delegate’s internal task runner now supports **exponential back‑off** out of the box, but you can fine‑tune it via environment variables:
env:
- name: DELEGATE_RETRY_MAX_ATTEMPTS
value: "8"
- name: DELEGATE_RETRY_BASE_MS
value: "250"
If you need to capture unrecoverable failures, set up a **dead‑letter Queue (DLQ)** using a sidecar that writes to an S3 bucket:
- name: dlq-writer
image: harness/dlq-writer:2026.0.1
env:
- name: DLQ_BUCKET
value: s3://my-harness-dlq
volumeMounts:
- name: dlq
mountPath: /var/log/dlq
The delegate writes JSON payloads of failed tasks to `/var/log/dlq`, and the sidecar pushes them upstream. Alert on non‑empty DLQ via Prometheus:
sum by (delegate) (increase(dlq_entries_total[5m])) > 0
Monitoring and alerting strategies beyond pod restarts
Relying solely on `restartCount` blinds you to silent sync failures. Create a Prometheus alert for **sync latency**:
harness_delegate_sync_duration_seconds{status="failed"} > 30
Pair that with a Grafana Dashboard that plots:
- `Task Capacity` (`harness_delegate_task_capacity`)
- `Sync Success Rate`
- `Dead‑Letter Queue size`
Here’s a quick Grafana panel definition (JSON omitted for brevity). It helped a Fortune 500 fintech rollout cut drift incidents by **73 %** (Gartner 2025).
—
Integrating the agent with your 2026 GitOps workflow
Connecting to Git repositories (GitHub, GitLab, Bitbucket)
Create a **Harness Git connector** with a fine‑grained PAT (Personal Access Token) that has **repo** and **read:org** scopes. In the Harness UI:
- Settings → Git Connectors → New → Choose provider.
- Paste the PAT, select “SSH” (recommended for FIPS compliance), and enable **auto‑rotate** (Harness will rotate every 30 days).