This guide covers deploying Infisical on Kubernetes using Helm charts, including the Infisical Operator for bi-directional secret synchronization with your cluster.
Kubernetes deployment is recommended for:
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ infisical Namespace │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Infisical │ │ Infisical │ │ Infisical │ │ │
│ │ │ Pod (Rep1) │ │ Pod (Rep2) │ │ Pod (Rep3) │ │ │
│ │ │ :8080 │ │ :8080 │ │ :8080 │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ └────────────────┼────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────▼──────┐ │ │
│ │ │ Service │ │ │
│ │ │ ClusterIP │ │ │
│ │ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ┌───────────────────────┼───────────────────────┐ │ │
│ │ │ ┌────▼────┐ ┌──────▼──────┐ ┌────▼────┐ │ │ │
│ │ │ │ Redis │ │ PostgreSQL │ │ Operator│ │ │ │
│ │ │ │ State │ │ Stateful │ │ Pod │ │ │ │
│ │ │ │ Set │ │ Set │ │ │ │ │ │
│ │ │ └─────────┘ └─────────────┘ └─────────┘ │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Ingress Controller │ │ │
│ │ │ (nginx/traefik/contour) │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ Load Balancer │ │
│ │ (Cloud LB) │ │
│ └─────────┬─────────┘ │
└──────────────────────────────┼──────────────────────────────────┘
│
┌──────────▼──────────┐
│ Users / Apps │
│ infisical.com │
└─────────────────────┘
| Requirement | Version |
|---|---|
| Kubernetes | 1.25+ |
| Helm | 3.10+ |
| StorageClass | Required for persistent volumes |
| Ingress Controller | nginx, traefik, contour, or similar |
| cert-manager | Optional, for automatic TLS |
kubectl cluster-info
kubectl version --short
helm version
helm repo add infisical https://raw.githubusercontent.com/Infisical/infisical/main/helm-charts
helm repo update
kubectl create namespace infisical
# Generate encryption key
ENCRYPTION_KEY=$(openssl rand -hex 16)
# Generate auth secret
AUTH_SECRET=$(openssl rand -base64 32)
# Generate database password
DB_PASSWORD=$(openssl rand -base64 24 | tr -d '=+')
# Create Kubernetes secrets
kubectl create secret generic infisical-secrets \
-n infisical \
--from-literal=encryption-key="$ENCRYPTION_KEY" \
--from-literal=auth-secret="$AUTH_SECRET" \
--from-literal=db-password="$DB_PASSWORD" \
--from-literal=site-url="https://infisical.example.com"
# values.yaml - Production Configuration
# ===========================================
# Global Settings
# ===========================================
global:
siteUrl: https://infisical.example.com
# ===========================================
# Infisical Application
# ===========================================
infisical:
image:
repository: infisical/infisical
tag: "v0.93.1-postgres"
pullPolicy: IfNotPresent
replicaCount: 3
resources:
limits:
cpu: "2"
memory: "2Gi"
requests:
cpu: "500m"
memory: "512Mi"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# Security context
podSecurityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Environment variables
env:
- name: NODE_ENV
value: "production"
- name: TELEMETRY_ENABLED
value: "false"
# Secrets reference
existingSecret: infisical-secrets
secretKeys:
encryptionKey: encryption-key
authSecret: auth-secret
siteUrl: site-url
# ===========================================
# PostgreSQL Database (Embedded)
# ===========================================
postgresql:
enabled: true
image:
tag: 14-alpine
primary:
persistence:
enabled: true
storageClass: "" # Use default storage class
size: 20Gi
resources:
limits:
cpu: "2"
memory: "2Gi"
requests:
cpu: "500m"
memory: "512Mi"
auth:
database: infisical
username: infisical
existingSecret: infisical-secrets
secretKeys:
userPasswordKey: db-password
# ===========================================
# Redis Cache (Embedded)
# ===========================================
redis:
enabled: true
image:
tag: 7-alpine
architecture: standalone
master:
persistence:
enabled: true
storageClass: ""
size: 5Gi
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "250m"
memory: "256Mi"
# ===========================================
# Ingress Configuration
# ===========================================
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
hosts:
- host: infisical.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: infisical-tls
hosts:
- infisical.example.com
# ===========================================
# Network Policies
# ===========================================
networkPolicies:
enabled: true
# Allow ingress only from ingress controller namespace
ingressFromNamespaces:
- ingress-nginx
# Allow egress to specific destinations
egressRules:
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
# ===========================================
# Pod Disruption Budget
# ===========================================
podDisruptionBudget:
enabled: true
minAvailable: 2
# ===========================================
# Health Checks
# ===========================================
livenessProbe:
httpGet:
path: /api/status
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/status
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
# ===========================================
# Backup Configuration
# ===========================================
backup:
enabled: true
schedule: "0 2 * * *" # Daily at 2 AM
retentionDays: 7
storageClass: ""
size: 10Gi
# Dry run to validate
helm install infisical infisical/infisical \
-n infisical \
-f values.yaml \
--dry-run --debug
# Actual installation
helm install infisical infisical/infisical \
-n infisical \
-f values.yaml
# Check installation status
helm status infisical -n infisical
helm list -n infisical
# Check pods are running
kubectl get pods -n infisical
# Check services
kubectl get svc -n infisical
# Check ingress
kubectl get ingress -n infisical
# View logs
kubectl logs -n infisical -l app=infisical -f
# Check persistent volumes
kubectl get pvc -n infisical
The Infisical Operator enables bi-directional secret synchronization between Infisical and Kubernetes.
# Add Helm repo if not already added
helm repo add infisical https://raw.githubusercontent.com/Infisical/infisical/main/helm-charts
helm repo update
# Install Operator
helm install infisical-operator infisical/operator \
-n infisical-system \
--create-namespace
# infisical-machine.yaml
apiVersion: operator.infisical.com/v1alpha1
kind: InfisicalMachine
metadata:
name: my-app-secrets
namespace: default
spec:
infisical:
domain: https://infisical.example.com
machineIdentity:
secretName: infisical-machine-identity
clientId: "your-client-id"
clientSecret: "your-client-secret"
secrets:
- projectName: "my-app"
environment: "production"
secretPath: "/"
target:
kubernetesSecret:
name: app-secrets
namespace: default
type: Opaque
# Optional: Auto-renewal
renewalPolicy:
type: auto
interval: 5m
# Optional: Template for secret keys
template:
type: GoTemplate
template: |
{{- range $key, $value := .Secrets }}
{{ $key }}: {{ $value | b64enc }}
{{- end }}
# Create machine identity secret first
kubectl create secret generic infisical-machine-identity \
-n default \
--from-literal=clientId="your-client-id" \
--from-literal=clientSecret="your-client-secret"
# Apply InfisicalMachine
kubectl apply -f infisical-machine.yaml
# Verify secret was created
kubectl get secret app-secrets -n default -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- secretRef:
name: app-secrets
# Or mount as files
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: app-secrets
# values-ha.yaml
infisical:
replicaCount: 3
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: infisical
topologyKey: kubernetes.io/hostname
postgresql:
ha:
enabled: true
replications: 2
pgpool:
enabled: true
replicaCount: 2
redis:
architecture: replication
master:
replicaCount: 1
replica:
replicaCount: 2
sentinel:
enabled: true
quorum: 2
# Use managed PostgreSQL service
postgresql:
enabled: false
infisical:
env:
- name: DB_CONNECTION_URI
valueFrom:
secretKeyRef:
name: external-db-secret
key: connection-uri
- name: DB_ROOT_CERT
valueFrom:
secretKeyRef:
name: external-db-secret
key: ca-cert
# Use managed Redis service
redis:
enabled: false
infisical:
env:
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: external-redis-secret
key: redis-url
apiVersion: v1
kind: Namespace
metadata:
name: infisical
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: infisical-network-policy
namespace: infisical
spec:
podSelector:
matchLabels:
app: infisical
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
apiVersion: v1
kind: ServiceAccount
metadata:
name: infisical
namespace: infisical
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT:role/infisical-role
automountServiceAccountToken: false
apiVersion: batch/v1
kind: CronJob
metadata:
name: infisical-backup
namespace: infisical
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: postgres:14-alpine
command:
- /bin/sh
- -c
- |
pg_dump $DB_CONNECTION_URI | gzip > /backup/infisical-$(date +%Y%m%d).sql.gz
find /backup -name "*.gz" -mtime +7 -delete
envFrom:
- secretRef:
name: infisical-secrets
volumeMounts:
- name: backup-volume
mountPath: /backup
volumes:
- name: backup-volume
persistentVolumeClaim:
claimName: infisical-backup-pvc
restartPolicy: OnFailure
# 1. Stop Infisical
helm uninstall infisical -n infisical
# 2. Restore database
kubectl run postgres-client --rm -it --image=postgres:14-alpine --restart=Never --namespace infisical -- \
psql -h infisical-postgresql -U infisical -d infisical < backup.sql
# 3. Restore secrets
kubectl create secret generic infisical-secrets \
-n infisical \
--from-literal=encryption-key="YOUR-KEY" \
--from-literal=auth-secret="YOUR-SECRET"
# 4. Reinstall Infisical
helm install infisical infisical/infisical -n infisical -f values.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: infisical
namespace: infisical
labels:
release: prometheus
spec:
selector:
matchLabels:
app: infisical
endpoints:
- port: http
path: /metrics
interval: 30s
Import the Infisical dashboard from Grafana Labs or create custom panels for:
kubectl get pods -n infisical -o wide
kubectl describe pod <pod-name> -n infisical
# All Infisical pods
kubectl logs -n infisical -l app=infisical -f
# Specific pod
kubectl logs -n infisical <pod-name> -f
# Previous instance (if crashed)
kubectl logs -n infisical <pod-name> --previous
kubectl exec -it -n infisical <infisical-pod> -- \
psql $DB_CONNECTION_URI -c "SELECT 1;"
# Run migrations manually
kubectl run migration --rm -it --image=infisical/infisical:v0.93.1-postgres \
--namespace infisical --restart=Never -- \
npm run migration:latest
# Update Helm repository
helm repo update
# Check available versions
helm search repo infisical/infisical --versions
# Review changelog
# https://infisical.com/docs/changelog/overview
# Upgrade with new values
helm upgrade infisical infisical/infisical \
-n infisical \
-f values.yaml \
--set infisical.image.tag="v0.94.0-postgres"
# Verify upgrade
helm status infisical -n infisical
kubectl rollout status deployment/infisical -n infisical
Any questions?
Feel free to contact us. Find all contact information on our contact page.