FluxCD with SOPS Encrypted Secrets
This guide shows how to securely manage Kubernetes secrets using SOPS (Secrets OPerationS) with FluxCD. SOPS allows you to encrypt sensitive data in Git repositories while maintaining GitOps workflows.
Why Use Encrypted Secrets?
Benefits of SOPS with FluxCD:
- Secrets are encrypted at rest in Git
- GitOps workflows remain intact
- Only values are encrypted, keeping YAML structure readable
- Version control for secret management
Prerequisites
- FluxCD installed and configured in your Kubernetes cluster
kubectl
configured to access your cluster- Windows machine with PowerShell or Command Prompt
- Git repository configured with FluxCD
Step 1: Install Required Tools
-
Install SOPS
Terminal window winget install mozilla.sops -
Verify SOPS installation
Terminal window sops -v -
Install Age encryption tools
Terminal window winget install FiloSottile.age -
Verify Age installation
Terminal window age -versionage-keygen -version
Step 2: Generate Age Encryption Keys
-
Create SOPS directory
Terminal window mkdir .sops -
Generate Age key pair
Terminal window age-keygen -o .sops/age.agekeyThis command outputs your public key and saves the private key to the file.
-
Note your public key The command output shows your public key:
Public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -
Secure your private key
- Store the
.sops/age.agekey
file securely (password manager, encrypted backup) - Never commit this file to Git
- Add
.sops/
to your.gitignore
file
- Store the
Step 3: Add Age Keys to Kubernetes
-
Create SOPS secret in Kubernetes
Terminal window kubectl create secret generic sops-age --namespace=flux-system --from-file=age.agekey=.sops\age.agekey -
Verify the secret was created
Terminal window kubectl get secret sops-age -n flux-system
Step 4: Configure SOPS
You can encrypt secrets in two ways:
Option A: Using Command Line Parameters
You can encrypt with the public key as argument:
sops --age=YOUR-PUBLIC-KEY --encrypt --encrypted-regex '^(data|stringData)$' --in-place secret.yaml
Option B: Using .sops.yaml Configuration File (Recommended)
-
Create SOPS configuration In your repository, create
.sops.yaml
:creation_rules:- path_regex: .*.yamlencrypted_regex: ^(data|stringData)$age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxReplace the
age1...
value with your actual public key from Step 2. -
Organize directory structure
fluxcd/├── clusters/│ └── peercluster/│ ├── .sops.yaml # SOPS configuration (age key)│ ├── infrastructure.yaml # Flux Kustomization with SOPS decryption│ └── apps.yaml # Flux Kustomization with SOPS decryption├── infrastructure/└── apps/└── peercluster/└── app-with-secret/└── some-secret.yaml # secret (stringData)
Step 5: Create and Encrypt Secrets
-
Create a sample secret file Create
some-secret.yaml
:apiVersion: v1kind: Secretmetadata:name: some-secrettype: OpaquestringData:this_is_a_secret: SuPeRSeCrEt==? -
Navigate to the directory with .sops.yaml
Terminal window cd clusters\Peercluster -
Encrypt the secret in place
Terminal window sops --encrypt --in-place ..\..\infrastructure\base\app-with-secret\some-secret.yaml -
Verify encryption The file now contains encrypted values:
apiVersion: v1kind: Secretmetadata:name: some-secrettype: OpaquestringData:this_is_a_secret: ENC[AES256_GCM,data:8hcMtCIZ2+cVEQz93cs=,iv:A5UFzGNdWFF+qAm5trPtVx7ZJLd4wx3XIFOhbfmHo3A=,tag:/aTuBIjNPri/H9knB1+big==,type:str]sops:kms: []gcp_kms: []azure_kv: []hc_vault: []age:- recipient: age10kfmy5p7sf744pdvmsfvebbacsd40k9zexe7l6e3se7rcm9j4fvqcpnmgfenc: |-----BEGIN AGE ENCRYPTED FILE-----YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaYVFaVlcreEJsamgwcy8yM05teXE3SVM1V1JRUUszSmVKNFZEWXJXMdRNCjFWcldKdHhmS1JDdHVHMUExUTJFWGZyMXg0bHBJTGozcWk0Q2RrSUpPbm8KLS0tIGRHS1JLRk1JWFRoVnovcEZSZ09pbFRpRGg3Z01pUGlNaTlrcEpjfHhJTXcKhlKemPKREgI15hPrUKRhSVHbootHqZ1VTHPgxbgU1eQG03ZoYJcOoHpyZLwoD+y/ehzTE732Ra80D8rnm8tmTg==-----END AGE ENCRYPTED FILE-----lastmodified: '2025-07-22T14:29:39Z'mac: ENC[AES256_GCM,data:u2ejyiM2FH9nu2q6bARdVx2gE220LdB3MxSGYEI1x9GcfnAbOPm6Mvs2Bt3skwgwf0/oEpc88DXX0EdG/dRK+eeEfFoMze3NKyji6xPA3KB7URsWxgmLj1Nu9UtBQ8pEXKvft1VLLFywf4UrZ2E2w0SAKQ/4+kVAeDBPotR9ehF=,iv:qbqovmsIuq6ltAP/pcjEv1YNss99eqeD2A4PzYrFQb8=,tag:ul8BlQP8hDk+mXFhEG/BGQ==,type:str]pgp: []encrypted_regex: ^(data|stringData)$version: 3.7.3
Step 6: Configure FluxCD Kustomization
-
Create secrets kustomization Create
kustomization.yaml
in your secrets directory:apiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationresources:- some-secret.yaml -
Add decryption to FluxCD Kustomization Update your cluster’s Kustomization to include SOPS decryption:
apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata:name: infrastructurenamespace: flux-systemspec:interval: 10m0spath: ./infrastructure/peerclusterprune: truesourceRef:kind: GitRepositoryname: flux-systemdecryption:provider: sopssecretRef:name: sops-agedependsOn:- name: flux-system -
Commit and push changes
Terminal window git add .git commit -m "Add encrypted secrets with SOPS"git push origin main
Step 7: Deploy and Verify
-
Trigger FluxCD reconciliation
Terminal window flux reconcile kustomization secrets -
Verify secret deployment
Terminal window kubectl get secret some-secret -n flux-system -
Test secret value decryption
Terminal window kubectl get secret some-secret -n flux-system -o jsonpath="{.data.this_is_a_secret}" | ForEach-Object { [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($_)) }
Step 8: Development Workflow Setup (Optional)
-
Install VS Code SOPS extension Install the SOPS extension for easier secret editing:
-
Set up Age key file as vs code setting
"sops.defaults.ageKeyFile": "C:\\...\\.sops\\age.agekey" -
Test the VS Code extension
- Open any encrypted
.yaml
file in VS Code - The extension automatically creates a temporary
.decrypted~
file for editing - Edit the decrypted file - changes are automatically encrypted back to the original file
- The temporary decrypted file is automatically cleaned up
- Open any encrypted
-
Update .gitignore - just to be sure Add these entries to prevent accidental commits:
# SOPS.sops/*.decrypted~*