Production Best Practices
This guide outlines enterprise-grade security, scalability, and reliability patterns for deploying QMigrator in production environments using Helm on Kubernetes.
Component placeholder mapping
In this document, <component> means a component key from your Helm values file.
Common keys are: app, eng, db, msg, asses, convs, migrt, and airflow.*.
Replace <component> in samples with the exact key used in your values.yaml.
Prerequisites
This guide assumes familiarity with:
- Kubernetes concepts (RBAC, NetworkPolicy, resource management)
- Helm and values overrides
- External secret manager integration
- Container security best practices
1. Secret Manager Synchronization
Integrate a secret manager with Kubernetes for secure, centralized credential management.
Use Official Cloud Secret Managers
Use the Secrets Store CSI Driver with the official cloud provider integration:
- Azure Key Vault provider
- AWS Secrets Manager provider
- Google Secret Manager provider
Installation scope
Installing CSI Driver and provider plugins is out of scope for this guide.
Use official docs:
Secret Object Sync
Create a SecretProviderClass to fetch values from your cloud secret manager and sync to a Kubernetes Secret object.
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: qmigrator-secrets
namespace: qmigrator
spec:
provider: <azure|aws|gcp>
parameters: # (1)!
# Parameters go here.
objects: | # (2)!
# Object format goes here.
secretObjects:
- secretName: qmig-secret
type: Opaque
data:
- key: postgres-password
objectName: postgres-password
- key: redis-password
objectName: redis-password
- key: airflow-secret-key
objectName: airflow-secret-key
- key: airflow-fernet-key
objectName: airflow-fernet-key
- key: airflow-password
objectName: airflow-password
- key: connection
objectName: connection
- Configure provider-specific fields in
spec.parametersfor your cloud driver. - Configure provider-specific object format and object names in
spec.parameters.objects.
Mount the CSI volume in a workload so secret sync is triggered:
app:
extraVolumes:
- name: qmig-secret
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "qmigrator-secrets" # (1)!
extraVolumeMounts:
- name: qmig-secret
mountPath: "/mnt/secrets-store"
readOnly: true
- Must match the
SecretProviderClassname defined insecret-sync.yaml.
Update Helm Values for Synced Secrets
secret:
create: false
secretName: "qmig-secret" # (1)!
airflow:
secret:
create: false
secretName: "qmig-secret"
- This secret is synced from the cloud secret manager
Secret Rotation
Rotation and sync behavior depend on your CSI provider configuration and polling interval.
2. Image Security & Registry Configuration
Private Registry Authentication
Use pre-created Docker pull secrets:
kubectl create secret docker-registry qmig-docker \
-n qmigrator \
--docker-server=<registry-server> \
--docker-username='<username>' \
--docker-password='<password>'
Reference in values:
imageCredentials:
create: false
secretName: "qmig-docker" # (1)!
global:
imagePullSecrets:
- name: qmig-docker
- Use the same pre-created registry secret name in both
imageCredentials.secretNameandglobal.imagePullSecrets.
Image Pull Policy
- Prefer cached; pull if missing
3. Kubernetes Network Policies
Implement zero-trust networking by defining explicit ingress rules.
Network Policy Architecture
Enable NetworkPolicy in Helm values:
NetworkPolicy Pattern
Use this sample as a base and adapt matchLabels, namespaces, and ports for your deployment:
<component>:
networkPolicy:
ingress:
from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: gateway-system # (1)!
- podSelector:
matchLabels:
app.kubernetes.io/name: qmigrator # (2)!
- Match the namespace label of your Gateway API or Ingress controller namespace.
- Match labels used by QMigrator pods allowed to call this component.
Policy rollout
Start with allow app-to-app ports, then tighten rules incrementally.
4. Resource Requests and Limits
Define CPU and memory allocations to ensure cluster stability and prevent resource contention.
Resource Pattern
Resource Sizing Guidance
| Component | Recommended | Large Scale | Notes |
|---|---|---|---|
| App | 128Mi | 256Mi | Web UI and API layer |
| Engine | 512Mi | 1.5Gi | CPU-intensive migration workloads |
| Database | 1Gi | 2Gi | Project metadata; depends on history retention |
| Cache | 128Mi | 256Mi | job caching |
| Assessment/Conversion/Migration | 512Mi | 1Gi | On-demand job runs |
| Airflow | 1.5Gi | 4Gi | Optional scheduler/workers |
5. Pod Security Standards
Enforce container security best practices at pod creation time.
PSS (Pod Security Standards) Configuration
Add labels to enforce PSS in the namespace:
kubectl label namespace qmigrator \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restricted
Secure Pod Configuration in values.yaml
Use securityContext and podSecurityContext to run containers with reduced privileges, drop unnecessary Linux capabilities, and keep filesystem ownership consistent across mounted volumes.
<component>:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
podSecurityContext:
runAsNonRoot: true
runAsUser: 5000 # (1)!
runAsGroup: 4000 # (2)!
fsGroup: 4000
seccompProfile:
type: RuntimeDefault
- Default QMigrator runtime user ID (UID):
5000. - Default QMigrator runtime group ID (GID):
4000.
QMigrator UID and GID
Keep these defaults unless your cluster, storage class, or security policy requires different IDs.
6. High Availability & Disaster Recovery
Pod Disruption Budgets
Protect critical components during node maintenance:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: qmigrator-app-pdb
namespace: qmigrator
spec:
minAvailable: 1
selector:
matchLabels:
component: app
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: qmigrator-db-pdb
namespace: qmigrator
spec:
minAvailable: 1
selector:
matchLabels:
component: db
Pod Affinity for Resilience
Spread components across nodes:
<component>:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions: # (1)!
- key: component
operator: In
values:
- <component>
topologyKey: kubernetes.io/hostname
- Match component labels consistently so anti-affinity spreads replicas across nodes.
7. Cost Optimization
Resource Quotas by Namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: qmigrator-quota
namespace: qmigrator
spec:
hard:
requests.cpu: "8"
requests.memory: "16Gi"
limits.cpu: "16"
limits.memory: "32Gi"
pods: "50"
services: "10"
persistentvolumeclaims: "10"
Use Lower-Cost Node Pools for Non-Critical Workloads
For non-critical workloads, schedule to a dedicated lower-cost node pool:
8. Deployment Checklist
Before going to production, verify:
- Secrets Store CSI Driver and cloud provider plugin configured
- All secrets stored in secret manager (no plaintext in values.yaml)
- NetworkPolicies enabled and tested for all components
- Resource requests/limits configured for all pods
- Pod Security Standards enforced on namespace
- RBAC roles created with least-privilege permissions
- Image pull secrets created and referenced
- Container images scanned for vulnerabilities
- Pod Disruption Budgets created for critical components
- Pod affinity rules configured for resilience
- Ingress/Gateway API TLS certificates installed
- Cluster and node autoscaling enabled