GitOps Secrets Management

GitOps makes the repo the source of truth, so a committed secret deploys itself. Compare Sealed Secrets, External Secrets Operator, and SOPS, and audit manifests first.

ZERO UPLOAD · ALL LOCAL
  1. Paste a Kubernetes, Terraform, or Crossplane manifest into the input area, or drop a .yaml, .yml, .json, or .tf file.
  2. Findings appear instantly — each document is analyzed separately, with severity-coded rows showing the exact key path and type of each secret or smell.
  3. Review the Sanitized Output area — all detected secrets have been replaced with safe placeholders like __REDACTED_AWS_ACCESS_KEY_1__.
  4. Click Download .yaml to get the clean manifest with placeholders, safe to share or commit.
  5. Click Download .env template to get a ready-to-fill .env file listing all stripped keys — paste in your real values and keep it out of version control.

Worked examples for this use case

A Secret manifest is found committed in the GitOps repo

Before
kind: Secret
data:
  api-token: c2stbGl2ZS1YOWsybTRxN3c=
After
kind: Secret
data:
  api-token: __REDACTED_KUBERNETES_SECRET_1__

Every .data value is critical by location. Rotate the token, move the value to your secret store or seal it, and rewrite history if the repo is shared.

Choosing a pattern for a single-cloud production platform

External Secrets Operator with the cloud's native secret manager is the common default: git holds only ExternalSecret references, rotation happens at the store, and no encryption keys need managing in the repo or cluster beyond provider access.

A Helm values file needs review before entering the repo

Paste it into the sanitizer first. Inline credentials surface as critical or high findings with exact paths, telling you which values must become ExternalSecret references or sealed values before the file is committed.

Drop .yaml / .yml / .json / .tf here

or click to browse

── or paste below ──

SANITIZED OUTPUT

GitOps Secrets Management: Keeping Credentials Out of the Repo

GitOps turns every committed secret into a deployed secret. When a controller like Argo CD or Flux continuously applies whatever the repository contains,1 the repo stops being a place code waits and becomes the live definition of your cluster, which sharpens an old problem: a plaintext credential in a GitOps repo is not just leaked to readers, it is actively distributed to workloads. The ecosystem's answer is to keep real values out of git entirely and commit references or ciphertext instead. Three approaches dominate in 2026: Sealed Secrets, External Secrets Operator, and SOPS. Whichever you adopt, manifests need auditing before they enter the repo, because the migration is only as good as its stragglers. A sanitization pass on each file, in the browser and before the commit, is the cheapest form of that audit.

Why plain Kubernetes Secrets fail the GitOps test

Base64 is a costume, not a lock. A Kubernetes Secret manifest stores its .data values base64-encoded, and decoding them requires one command and no key, so committing Secret manifests to a GitOps repo publishes their contents to everyone with repository read access, every CI job, and every laptop clone.2 The Kubernetes documentation itself lists keeping Secret manifests out of general version control among its good practices.2

Flagged by location, not by content

That is a deliberate design choice, not an oversight: inspecting content first would mean occasionally missing a Secret value that happens to look ordinary. Consequently, the sanitizer refuses to negotiate here: every value inside a Secret's .data or .stringData map is flagged critical unconditionally, without inspecting the content, because the location alone proves sensitivity.

If a Secret manifest must be shared for debugging, the sanitized version with placeholders is the only version that should travel. Yet teams new to GitOps frequently miss this distinction, treating a Secret manifest as just another YAML file because nothing about its syntax signals danger, which is exactly the gap the unconditional rule closes.

The three patterns: Sealed Secrets, ESO, and SOPS

Each pattern moves trust somewhere different. Sealed Secrets encrypts each secret against a controller's key inside your cluster, so the committed SealedSecret ciphertext is safe in public and only that cluster can decrypt it; the trade-off is cluster lock-in for recovery.3 External Secrets Operator inverts the flow, committing only a reference to a value held in a cloud secret manager and syncing it into a Kubernetes Secret at runtime; with support for dozens of provider backends it has become the default choice for teams already using a cloud secret store, and rotation happens at the store without touching git.4 SOPS encrypts values within YAML files using keys from KMS services, fitting mixed environments beyond Kubernetes.5

Picking one pattern and writing it down

None of the three is objectively best; the wrong choice is running two of them side by side without anyone deciding that on purpose. Building on this, many organizations run ESO for cloud-backed production and Sealed Secrets or SOPS where a secret manager is unavailable.

Whichever pattern you standardize on, document it once for the whole platform team, since a repository mixing two unrelated approaches is harder to audit than one committed to a single convention. Auditing a mixed repository later means learning two decryption paths instead of one, which is exactly the kind of complexity a single early decision avoids.

Auditing the repo before and during migration

Migration plans meet reality in the oldest directories. A GitOps repo accumulated over years contains Helm values files with inline passwords, ConfigMaps carrying connection strings, and example manifests that stopped being examples, and none of the three encryption patterns fixes what nobody has found. Pasting each manifest into the sanitizer inventories the problem: critical findings mark provider tokens, connection strings, and Secret data; high findings mark the random-looking values that are probably custom credentials; the .env template enumerates what needs a new home in the secret store.

The audit keeps running during the migration

A migration in progress is itself a risk window, since files move between people and tools more than they do once the pattern is settled. Yet the audit is not a one-time event. Files pasted into reviews, tickets, and AI assistants during the migration carry the same risks, so the redaction habit protects the transition itself, not only the end state.

A repository-wide inventory also gives migration owners a concrete completion metric, since the count of remaining plaintext credentials falls to zero rather than the migration simply being declared done. Treat that number the way you would treat any other burndown metric, checked on a schedule until it actually reaches zero rather than stalling near the end.

When to use this

Use this approach when adopting Argo CD or Flux, when inheriting a GitOps repository of unknown hygiene, or when choosing between Sealed Secrets, External Secrets Operator, and SOPS for a new platform. Sanitize individual manifests at every boundary crossing during the migration, since the transition period is exactly when files move between people and tools most often, and it is the fastest way to check whether a Secret's data is actually encrypted. It also applies during periodic platform audits, well after the initial migration, when new contributors or copied examples can quietly reintroduce plaintext credentials.

Examples

A Secret manifest is found committed in the GitOps repo

Before
kind: Secret
data:
  api-token: c2stbGl2ZS1YOWsybTRxN3c=
After
kind: Secret
data:
  api-token: __REDACTED_KUBERNETES_SECRET_1__

Every .data value is critical by location. Rotate the token, move the value to your secret store or seal it, and rewrite history if the repo is shared.

Choosing a pattern for a single-cloud production platform

External Secrets Operator with the cloud's native secret manager is the common default: git holds only ExternalSecret references, rotation happens at the store, and no encryption keys need managing in the repo or cluster beyond provider access.

A Helm values file needs review before entering the repo

Paste it into the sanitizer first. Inline credentials surface as critical or high findings with exact paths, telling you which values must become ExternalSecret references or sealed values before the file is committed.

Sources
  1. 1.

    Argo CD, "Argo CD - Declarative GitOps CD for Kubernetes," argo-cd.readthedocs.io, accessed July 2026. https://argo-cd.readthedocs.io/en/stable/

  2. 2.

    Kubernetes, "Good Practices for Kubernetes Secrets," kubernetes.io, accessed July 2026. https://kubernetes.io/docs/concepts/security/secrets-good-practices/

  3. 3.

    Bitnami, "Sealed Secrets for Kubernetes," github.com, accessed July 2026. https://github.com/bitnami-labs/sealed-secrets/blob/main/README.md

  4. 4.

    External Secrets Operator, "Introduction - External Secrets Operator," external-secrets.io, accessed July 2026. https://external-secrets.io/latest/

  5. 5.

    SOPS, "SOPS: Secrets OPerationS," github.com, accessed July 2026. https://github.com/getsops/sops

FAQ