Managing Secrets with Infisical and Kubernetes
A step-by-step guide to setting up Infisical for secret management, integrating with External Secrets Operator in Kubernetes, and using secrets in .NET applications.
Infisical is an open-source secret management platform that provides a central location for storing, syncing, and managing secrets across your infrastructure. This recipe shows how to integrate Infisical with Kubernetes using External Secrets Operator.
flowchart LR
INF["Infisical\n(Secret Store)"] -->|"sync"| CSS["ClusterSecretStore\n(ESO connection)"]
CSS --> ES1["ExternalSecret\n(archives-api)"]
CSS --> ES2["ExternalSecret\n(archives-workers)"]
CSS --> ES3["ExternalSecret\n(auth / TLS certs)"]
ES1 --> S1["K8s Secret"]
ES2 --> S2["K8s Secret"]
ES3 --> S3["K8s Secret"]
S1 -->|"env vars"| P1["Archives API Pod"]
S2 -->|"env vars"| P2["Workers Pod"]
S3 -->|"volume mount"| P3["Authelia Pod"]
style INF fill:#1a2744,stroke:#6366f1,color:#e2e8f0
style CSS fill:#1a2744,stroke:#f59e0b,color:#e2e8f0
Prerequisites
- Infisical account (cloud or self-hosted)
- Kubernetes cluster with External Secrets Operator installed
- kubectl access to your cluster
Step 1: Create an Infisical Project
- Log into Infisical (https://app.infisical.com)
- Create a new project named
my-project - Note the Project ID from Settings → Project ID
Step 2: Configure Environments
Create environments matching your deployment stages:
- Navigate to Settings → Environments
- Create:
dev— Development environmentstaging— Staging environmentprod— Production environment
Step 3: Add Secrets
Add your application secrets to each environment:
APP_DB_PASSWORD=your-secure-password
NATS_PASSWORD=another-secure-password
MINIO_ACCESS_KEY=minio-access-key
MINIO_SECRET_KEY=minio-secret-key
OIDC_CLIENT_SECRET=authelia-client-secret
ENCRYPTION_KEY=32-byte-base64-encoded-key
Step 4: Create Machine Identity
For Kubernetes integration, create a machine identity:
[Infisical Universal Auth] — Infisical , 2024- Navigate to Access Control → Machine Identities
- Click Create Machine Identity
- Name it
k8s-external-secrets - Select Universal Auth method
- Generate credentials and save:
- Client ID
- Client Secret
Grant access to your project:
- Go to Project Settings → Access Control
- Add the machine identity
- Grant Read access to all environments
Step 5: Install External Secrets Operator
[External Secrets Operator] — External Secrets , 2024If not already installed:
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=true
Step 6: Create Kubernetes Credentials Secret
Store the machine identity credentials in your cluster:
# infrastructure/secrets/infisical-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
name: infisical-credentials
namespace: external-secrets
type: Opaque
stringData:
clientId: "your-client-id"
clientSecret: "your-client-secret"
Apply with:
kubectl apply -f infrastructure/secrets/infisical-credentials.yaml
Step 7: Configure ClusterSecretStore
Create a cluster-wide secret store for Infisical:
# infrastructure/secrets/infisical-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: infisical-store
spec:
provider:
infisical:
host: https://app.infisical.com
auth:
universalAuth:
credentialsRef:
clientId:
key: clientId
name: infisical-credentials
namespace: external-secrets
clientSecret:
key: clientSecret
name: infisical-credentials
namespace: external-secrets
Apply:
kubectl apply -f infrastructure/secrets/infisical-store.yaml
Verify:
kubectl get clustersecretstore infisical-store
# Should show: STATUS: Valid
Step 8: Create ExternalSecrets
Define which secrets to sync from Infisical:
# apps/myapp-api/staging/external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: archives-secrets
namespace: myapp-staging
spec:
refreshInterval: 5m
secretStoreRef:
kind: ClusterSecretStore
name: infisical-store
target:
name: archives-secrets
creationPolicy: Owner
data:
# Map Infisical secrets to Kubernetes secret keys
- secretKey: DATABASE_PASSWORD
remoteRef:
key: APP_DB_PASSWORD
property: value
conversionStrategy: Default
decodingStrategy: None
metadataPolicy: None
sourceRef:
storeRef:
kind: ClusterSecretStore
name: infisical-store
generatorRef: null
- secretKey: NATS_PASSWORD
remoteRef:
key: NATS_PASSWORD
property: value
- secretKey: MINIO_ACCESS_KEY
remoteRef:
key: MINIO_ACCESS_KEY
property: value
- secretKey: MINIO_SECRET_KEY
remoteRef:
key: MINIO_SECRET_KEY
property: value
- secretKey: OIDC_CLIENT_SECRET
remoteRef:
key: WEB_STAGING_OIDC_SECRET
property: value
# Specify Infisical project and environment
dataFrom: []
Environment-Specific Configuration
For different environments, create separate ExternalSecrets pointing to different Infisical environments:
# apps/myapp-api/production/external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: archives-secrets
namespace: myapp-prod
spec:
refreshInterval: 10m # Less frequent in production
secretStoreRef:
kind: ClusterSecretStore
name: infisical-store
target:
name: archives-secrets
data:
- secretKey: DATABASE_PASSWORD
remoteRef:
key: APP_DB_PASSWORD
property: value
# ... other secrets
Step 9: Use Secrets in Deployments
Reference the synced secret in your deployment:
# apps/myapp-api/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-api
spec:
template:
spec:
containers:
- name: api
envFrom:
- secretRef:
name: archives-secrets
env:
# Or reference individual keys
- name: ConnectionStrings__AppDb
value: "Host=postgres.data-layer;Port=5432;Database=myapp_staging;Username=myapp_staging;Password=$(DATABASE_PASSWORD)"
Step 10: Verify Sync
Check that secrets are syncing correctly:
# Check ExternalSecret status
kubectl get externalsecret -n myapp-staging
# NAME STORE REFRESH INTERVAL STATUS
# archives-secrets infisical-store 5m SecretSynced
# Verify the Kubernetes secret was created
kubectl get secret archives-secrets -n myapp-staging -o yaml
# Check for sync errors
kubectl describe externalsecret archives-secrets -n myapp-staging
Local Development Setup
For local development, use the Infisical CLI:
Install CLI
# macOS
brew install infisical/get-cli/infisical
# Or via npm
npm install -g @infisical/cli
Authenticate
infisical login
Run with Secrets
# Run a command with secrets injected as environment variables
infisical run --env=dev -- dotnet run --project src/MyApp.Api
# Or generate a .env file
infisical export --env=dev > .env.local
Development Script
Create a helper script:
#!/bin/bash
# scripts/generate-env.sh
set -e
echo "🔐 Generating .env.local from Infisical (dev environment)..."
# Export secrets to .env format
infisical export --env=dev --format=dotenv > .env.local
# Add local-specific overrides
cat >> .env.local << EOF
# Local Development Overrides
DATABASE_HOST=192.168.0.6
DATABASE_PORT=30432
NATS_URL=nats://192.168.0.6:30422
MINIO_ENDPOINT=192.168.0.6:30900
EOF
echo "✅ .env.local generated successfully"
Rotating Secrets
To rotate a secret:
- Update the value in Infisical
- Wait for refresh interval (or force sync)
- Restart pods to pick up new values
Force immediate sync:
# Annotate to trigger refresh
kubectl annotate externalsecret archives-secrets \
-n myapp-staging \
force-sync=$(date +%s) --overwrite
Restart pods:
kubectl rollout restart deployment/myapp-api -n myapp-staging
Troubleshooting
Secret Not Syncing
# Check ExternalSecret events
kubectl describe externalsecret archives-secrets -n myapp-staging
# Check External Secrets Operator logs
kubectl logs -n external-secrets deploy/external-secrets -f
Authentication Errors
# Verify credentials secret exists
kubectl get secret infisical-credentials -n external-secrets
# Check ClusterSecretStore status
kubectl describe clustersecretstore infisical-store
Common Issues
| Issue | Solution |
|---|---|
SecretSyncedError | Check Infisical permissions for machine identity |
InvalidProviderConfig | Verify ClusterSecretStore YAML syntax |
| Secret key mismatch | Ensure remoteRef.key matches Infisical secret name exactly |
| Rate limiting | Increase refreshInterval to reduce API calls |
Security Best Practices
- Least privilege: Grant machine identities only the permissions they need
- Rotate credentials: Rotate machine identity credentials periodically
- Audit access: Review Infisical audit logs regularly
- Encrypt at rest: Enable Infisical’s encryption features
- Network policies: Restrict which pods can access the secrets
# Example NetworkPolicy for secrets access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-secrets-access
namespace: myapp-staging
spec:
podSelector:
matchLabels:
app: myapp-api
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: external-secrets
Summary
You now have:
✅ Infisical project with multi-environment secrets
✅ Machine identity for Kubernetes authentication
✅ ClusterSecretStore connecting to Infisical
✅ ExternalSecrets syncing to Kubernetes Secrets
✅ Applications consuming secrets via environment variables
✅ Local development setup with Infisical CLI
This approach keeps secrets out of Git while providing a central, auditable location for secret management.
The biggest lesson from integrating Infisical into our cluster was that secret rotation is the hard part, not initial deployment. Setting up the ClusterSecretStore took an afternoon; building confidence in automated rotation took weeks of testing. Start with long-lived secrets and add rotation gradually once your ExternalSecret sync is bulletproof.
Next Steps
- External Secrets with Infisical in Kubernetes — advanced patterns for multi-namespace secret distribution.
- GitOps with Flux CD — how secrets fit into the broader GitOps deployment model.
- Zero Trust Architecture — the security model that makes secret management critical.