<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashish</title>
    <description>The latest articles on DEV Community by Ashish (@ashish_1dfa9598b1ed1eb541).</description>
    <link>https://dev.to/ashish_1dfa9598b1ed1eb541</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4003422%2F49e12d2c-0d1d-4ee6-be51-fc7073087376.png</url>
      <title>DEV Community: Ashish</title>
      <link>https://dev.to/ashish_1dfa9598b1ed1eb541</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashish_1dfa9598b1ed1eb541"/>
    <language>en</language>
    <item>
      <title>How to Manage Secrets in GitOps Without Exposing Them in Git</title>
      <dc:creator>Ashish</dc:creator>
      <pubDate>Fri, 26 Jun 2026 06:56:17 +0000</pubDate>
      <link>https://dev.to/ashish_1dfa9598b1ed1eb541/how-to-manage-secrets-in-gitops-without-exposing-them-in-git-jp8</link>
      <guid>https://dev.to/ashish_1dfa9598b1ed1eb541/how-to-manage-secrets-in-gitops-without-exposing-them-in-git-jp8</guid>
      <description>&lt;p&gt;One of the first questions beginners ask when learning GitOps is:&lt;/p&gt;

&lt;p&gt;"If everything goes in Git, where do I put passwords and API keys?"&lt;/p&gt;

&lt;p&gt;You can't just commit a Kubernetes Secret to Git. It's base64 encoded — not encrypted. Anyone with repo access can decode it in seconds.&lt;br&gt;
This tutorial shows you exactly how to solve this using Sealed Secrets and Flux — two tools that let you safely store secrets in Git without exposing sensitive values.&lt;/p&gt;

&lt;p&gt;What You'll Learn&lt;/p&gt;

&lt;p&gt;Why plain Kubernetes Secrets are unsafe in Git&lt;br&gt;
What Sealed Secrets is and how it works&lt;br&gt;
How to install Sealed Secrets with Flux&lt;br&gt;
How to create and deploy your first sealed secret&lt;br&gt;
Best practices to avoid common beginner mistakes&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;br&gt;
Before starting, make sure you have:&lt;/p&gt;

&lt;p&gt;A running Kubernetes cluster (local with kind or minikube works fine)&lt;br&gt;
kubectl installed and configured&lt;br&gt;
Flux CLI installed — install guide here&lt;br&gt;
A Git repository (GitHub, GitLab, etc.) bootstrapped with Flux&lt;/p&gt;

&lt;p&gt;If you haven't bootstrapped Flux yet, run:&lt;br&gt;
bashflux bootstrap github \&lt;br&gt;
  --owner= \&lt;br&gt;
  --repository= \&lt;br&gt;
  --branch=main \&lt;br&gt;
  --path=./clusters/my-cluster \&lt;br&gt;
  --personal&lt;/p&gt;

&lt;p&gt;Why You Can't Just Commit a Kubernetes Secret&lt;br&gt;
A standard Kubernetes Secret looks like this:&lt;br&gt;
yamlapiVersion: v1&lt;br&gt;
kind: Secret&lt;br&gt;
metadata:&lt;br&gt;
  name: my-app-secret&lt;br&gt;
type: Opaque&lt;br&gt;
data:&lt;br&gt;
  password: c3VwZXJzZWNyZXQ=&lt;br&gt;
That c3VwZXJzZWNyZXQ= value looks like gibberish. But it's just base64 encoding — not encryption. Decode it in your terminal:&lt;br&gt;
bashecho "c3VwZXJzZWNyZXQ=" | base64 --decode&lt;/p&gt;

&lt;h1&gt;
  
  
  Output: supersecret
&lt;/h1&gt;

&lt;p&gt;Anyone who can read your Git repo can decode every secret in it. This is the core problem GitOps beginners run into.&lt;/p&gt;

&lt;p&gt;What Is Sealed Secrets?&lt;br&gt;
Sealed Secrets is a Kubernetes controller created by Bitnami. It solves this problem with a simple approach:&lt;/p&gt;

&lt;p&gt;You encrypt your secret using a public key — producing a SealedSecret resource&lt;br&gt;
You commit the SealedSecret to Git — it's safe because it's encrypted&lt;br&gt;
When Flux applies it to your cluster, the Sealed Secrets controller decrypts it using its private key (which never leaves the cluster)&lt;br&gt;
Kubernetes gets the actual Secret — your app works normally&lt;/p&gt;

&lt;p&gt;The critical point: only the controller inside your cluster can decrypt it. Even if someone steals your Git repo, they can't decrypt the sealed secrets without access to the cluster's private key.&lt;/p&gt;

&lt;p&gt;Step 1: Install the Sealed Secrets Controller via Flux&lt;br&gt;
With GitOps, you don't install things manually with helm install. You declare everything in Git and let Flux apply it.&lt;br&gt;
Create a HelmRepository source file:&lt;br&gt;
yaml# clusters/my-cluster/sealed-secrets-source.yaml&lt;br&gt;
apiVersion: source.toolkit.fluxcd.io/v1beta2&lt;br&gt;
kind: HelmRepository&lt;br&gt;
metadata:&lt;br&gt;
  name: sealed-secrets&lt;br&gt;
  namespace: flux-system&lt;br&gt;
spec:&lt;br&gt;
  interval: 1h&lt;br&gt;
  url: &lt;a href="https://bitnami-labs.github.io/sealed-secrets" rel="noopener noreferrer"&gt;https://bitnami-labs.github.io/sealed-secrets&lt;/a&gt;&lt;br&gt;
Create a HelmRelease to install the controller:&lt;br&gt;
yaml# clusters/my-cluster/sealed-secrets-release.yaml&lt;br&gt;
apiVersion: helm.toolkit.fluxcd.io/v2beta1&lt;br&gt;
kind: HelmRelease&lt;br&gt;
metadata:&lt;br&gt;
  name: sealed-secrets&lt;br&gt;
  namespace: flux-system&lt;br&gt;
spec:&lt;br&gt;
  interval: 1h&lt;br&gt;
  chart:&lt;br&gt;
    spec:&lt;br&gt;
      chart: sealed-secrets&lt;br&gt;
      version: "&amp;gt;=1.15.0-0"&lt;br&gt;
      sourceRef:&lt;br&gt;
        kind: HelmRepository&lt;br&gt;
        name: sealed-secrets&lt;br&gt;
        namespace: flux-system&lt;br&gt;
  targetNamespace: kube-system&lt;br&gt;
Commit and push both files to your Git repo:&lt;br&gt;
bashgit add clusters/my-cluster/sealed-secrets-source.yaml&lt;br&gt;
git add clusters/my-cluster/sealed-secrets-release.yaml&lt;br&gt;
git commit -m "feat: add sealed secrets controller"&lt;br&gt;
git push&lt;br&gt;
Flux will detect the change and install the controller. Verify it's running:&lt;br&gt;
bashkubectl get pods -n kube-system | grep sealed-secrets&lt;/p&gt;

&lt;h1&gt;
  
  
  sealed-secrets-controller-xxxxx   1/1   Running   0   1m
&lt;/h1&gt;

&lt;p&gt;Step 2: Install the kubeseal CLI&lt;br&gt;
kubeseal is the command-line tool you use to encrypt secrets before committing them.&lt;br&gt;
macOS:&lt;br&gt;
bashbrew install kubeseal&lt;br&gt;
Linux:&lt;br&gt;
bashKUBESEAL_VERSION=$(curl -s &lt;a href="https://api.github.com/repos/bitnami-labs/sealed-secrets/tags" rel="noopener noreferrer"&gt;https://api.github.com/repos/bitnami-labs/sealed-secrets/tags&lt;/a&gt; \&lt;br&gt;
  | jq -r '.[0].name' | cut -c 2-)&lt;br&gt;
curl -OL "&lt;a href="https://github.com/bitnami-labs/sealed-secrets/releases/download/v$%7BKUBESEAL_VERSION%7D/kubeseal-$%7BKUBESEAL_VERSION%7D-linux-amd64.tar.gz" rel="noopener noreferrer"&gt;https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz&lt;/a&gt;"&lt;br&gt;
tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal&lt;br&gt;
sudo install -m 755 kubeseal /usr/local/bin/kubeseal&lt;br&gt;
Verify:&lt;br&gt;
bashkubeseal --version&lt;/p&gt;

&lt;p&gt;Step 3: Create and Seal Your First Secret&lt;br&gt;
Start with a plain Kubernetes Secret. Don't commit this file. It's just an intermediate step.&lt;br&gt;
yaml# secret-plain.yaml  &amp;lt;-- DO NOT commit this file&lt;br&gt;
apiVersion: v1&lt;br&gt;
kind: Secret&lt;br&gt;
metadata:&lt;br&gt;
  name: my-app-secret&lt;br&gt;
  namespace: default&lt;br&gt;
type: Opaque&lt;br&gt;
stringData:&lt;br&gt;
  database-password: "supersecret123"&lt;br&gt;
  api-key: "myapikey456"&lt;br&gt;
Now seal it using kubeseal:&lt;br&gt;
bashkubeseal \&lt;br&gt;
  --controller-name=sealed-secrets \&lt;br&gt;
  --controller-namespace=kube-system \&lt;br&gt;
  --format yaml \&lt;br&gt;
  &amp;lt; secret-plain.yaml \&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;sealed-secret.yaml&lt;br&gt;
The output sealed-secret.yaml will look like this:&lt;br&gt;
yamlapiVersion: bitnami.com/v1alpha1&lt;br&gt;
kind: SealedSecret&lt;br&gt;
metadata:&lt;br&gt;
  name: my-app-secret&lt;br&gt;
  namespace: default&lt;br&gt;
spec:&lt;br&gt;
  encryptedData:&lt;br&gt;
    database-password: AgBy8hW...long-encrypted-string...&lt;br&gt;
    api-key: AgCtr9X...another-long-encrypted-string...&lt;br&gt;
  template:&lt;br&gt;
    metadata:&lt;br&gt;
      name: my-app-secret&lt;br&gt;
      namespace: default&lt;br&gt;
This is safe to commit. The encrypted strings are useless without the controller's private key.&lt;br&gt;
bash# Delete the plain secret file — never commit it&lt;br&gt;
rm secret-plain.yaml&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Commit the sealed secret
&lt;/h1&gt;

&lt;p&gt;git add sealed-secret.yaml&lt;br&gt;
git commit -m "feat: add sealed app secret"&lt;br&gt;
git push&lt;/p&gt;

&lt;p&gt;Step 4: Verify the Secret Was Decrypted&lt;br&gt;
Once Flux applies the SealedSecret to your cluster, the controller decrypts it and creates a regular Kubernetes Secret:&lt;br&gt;
bash# Check the SealedSecret was applied&lt;br&gt;
kubectl get sealedsecret my-app-secret -n default&lt;/p&gt;

&lt;h1&gt;
  
  
  Check the resulting Kubernetes Secret exists
&lt;/h1&gt;

&lt;p&gt;kubectl get secret my-app-secret -n default&lt;/p&gt;

&lt;h1&gt;
  
  
  Verify the values (for debugging only — don't do this in production)
&lt;/h1&gt;

&lt;p&gt;kubectl get secret my-app-secret -n default -o jsonpath='{.data.database-password}' | base64 --decode&lt;br&gt;
Your app can now reference the secret normally:&lt;br&gt;
yamlenv:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name: DATABASE_PASSWORD
valueFrom:
  secretKeyRef:
    name: my-app-secret
    key: database-password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common Beginner Mistakes to Avoid&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accidentally committing the plain Secret
Add this to your .gitignore:
&lt;em&gt;-plain.yaml
*-secret-plain.yaml
secret-plain&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Sealing without specifying namespace
Sealed Secrets are namespace-scoped by default. A secret sealed for namespace: default will not decrypt in namespace: production. Always set the correct namespace when sealing.&lt;/li&gt;
&lt;li&gt;Losing the controller's private key
If you delete your cluster and lose the private key, you cannot decrypt your sealed secrets. Back it up:
bashkubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key \
-o yaml &amp;gt; sealed-secrets-master-key-backup.yaml
Store this backup somewhere secure and separate from Git — a password manager, a vault, or encrypted cold storage.&lt;/li&gt;
&lt;li&gt;Rotating secrets manually
When you need to change a secret value, create a new plain secret, seal it again, and commit the updated SealedSecret. The controller handles the rest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How This Fits Into Your GitOps Workflow&lt;br&gt;
Here's the full picture:&lt;br&gt;
Developer machine&lt;br&gt;
│&lt;br&gt;
├── Creates plain Secret (never committed)&lt;br&gt;
├── Runs kubeseal → produces SealedSecret&lt;br&gt;
└── Commits SealedSecret to Git&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    Git Repository (safe — only encrypted values)&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    Flux detects change → applies SealedSecret to cluster&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    Sealed Secrets Controller decrypts → creates real Secret&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    Your app reads the Secret normally&lt;br&gt;
Your Git repo becomes the single source of truth for your entire cluster — including secrets — without ever exposing sensitive values.&lt;/p&gt;

&lt;p&gt;Summary&lt;/p&gt;

&lt;p&gt;Plain Kubernetes Secrets in Git are a security risk — base64 is not encryption&lt;br&gt;
Sealed Secrets encrypts your secrets so only your cluster can decrypt them&lt;br&gt;
Flux + Sealed Secrets gives you a fully GitOps-native secrets workflow&lt;br&gt;
Back up your controller's private key — losing it means losing access to all your sealed secrets&lt;/p&gt;

&lt;p&gt;What's Next?&lt;br&gt;
Once you're comfortable with this setup, consider exploring:&lt;/p&gt;

&lt;p&gt;Secret rotation strategies — how to update secrets without downtime&lt;br&gt;
Flux alerts — get notified when a SealedSecret fails to decrypt&lt;br&gt;
HashiCorp Vault + Flux — for teams that need centralized secret management across multiple clusters&lt;/p&gt;

</description>
      <category>gitops</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
