Documentation / khadim
Khadim
A lean Helm chart for deploying worker and daemon containers on Kubernetes. Khadim (Arabic for “servant”) is designed for non-HTTP workloads: queue workers, background processors, cron consumers, and long-running daemons.
Unlike Safeena (which includes ingress, Kong plugins, and TLS certificates), Khadim has no networking by default — no Ingress, no Kong, no cert-manager. It deploys a Deployment with optional Service, HPA, and health probes.
Prerequisites
- Kubernetes 1.23+
- Helm 3.x
Architecture
┌─────────────────────────────────┐
│ Deployment │
│ ┌───────────┐ ┌───────────┐ │
│ │ worker-0 │ │ worker-1 │ │ (replicas controlled by replicaCount or HPA)
│ └───────────┘ └───────────┘ │
└─────────────────────────────────┘
│ (optional)
┌────▼────┐
│ Service │ (only when service.enabled = true, e.g., for health check endpoints)
└─────────┘
- Each pod runs your worker image as PID 1.
- No ingress or external access by default.
- Pods are spread across nodes via
topologySpreadConstraints(built-in,maxSkew: 1,ScheduleAnyway). - Optional Service for exposing a health check port (e.g., for liveness probes or internal monitoring).
Installation
# Basic worker
helm install my-worker charts/khadim \
--set image.repository=my-registry/my-worker \
--set image.tag=latest
# With a fixed resource name, independent of the release name
helm install my-worker charts/khadim \
--set fullnameOverride=my-worker \
--set image.repository=my-registry/my-worker \
--set image.tag=v1.2.3
# With multiple replicas and autoscaling
helm install my-worker charts/khadim \
--set image.repository=my-registry/my-worker \
--set autoscaling.enabled=true \
--set autoscaling.minReplicas=2 \
--set autoscaling.maxReplicas=10 \
--set autoscaling.targetCPUUtilizationPercentage=70
Configuration
Environment variables
Inject environment variables directly or from Kubernetes Secrets/ConfigMaps:
env:
- name: APP_ENV
value: production
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-db-credentials
key: password
envFrom:
- secretRef:
name: my-app-secrets
- configMapRef:
name: my-app-config
Volumes and volume mounts
Mount ConfigMaps, Secrets, PVCs, or emptyDirs into the worker container:
volumes:
- name: config
configMap:
name: my-worker-config
- name: tmp
emptyDir: {}
volumeMounts:
- name: config
mountPath: /etc/my-worker
readOnly: true
- name: tmp
mountPath: /tmp
Health probes
Probes are disabled by default since many workers don’t expose HTTP endpoints. Enable them when your worker has a health check:
service:
enabled: true
port: 8080
probes:
enabled: true
path: /healthz
port: http
bootstraptime: 30 # seconds to wait before failing startup probe
When probes are enabled:
- A startup probe polls the health endpoint every 10 seconds, allowing up to
bootstraptime / 10failures before killing the pod. - A liveness probe continues checking after startup succeeds.
- The Service must also be enabled to expose the health check port.
Autoscaling
Horizontal Pod Autoscaler based on CPU and/or memory utilisation:
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80 # optional
When autoscaling is enabled, the replicaCount value is ignored — the HPA manages replica count.
Pod topology spread
Khadim includes a built-in topologySpreadConstraints rule that spreads pods across nodes (maxSkew: 1, ScheduleAnyway). This is not configurable — it’s always applied to avoid hotspotting worker pods on a single node.
Connecting to MySQL with TLS
When a MySQL instance deployed with the mysql chart has TLS enabled, the MySQL chart publishes the CA certificate as a ConfigMap named <mysql-release>-tls-ca. Workers deployed via Khadim can mount this ConfigMap to verify the MySQL server’s identity and establish encrypted connections.
volumes:
- name: mysql-ca
configMap:
name: my-db-tls-ca # <mysql-release>-tls-ca
volumeMounts:
- name: mysql-ca
mountPath: /etc/mysql-tls
readOnly: true
env:
- name: MYSQL_SSL_CA
value: /etc/mysql-tls/ca.crt
The worker then reads the MYSQL_SSL_CA environment variable and passes it to its MySQL driver. See the MySQL chart README for driver-specific examples (PHP/Kipchak, Node.js, Python, Go).
Examples
Queue worker
fullnameOverride: "billing-worker"
image:
repository: registry.example.com/acme/queue-workers
tag: "v1.5.0"
replicaCount: 2
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
env:
- name: WORKER_QUEUE
value: billing
envFrom:
- secretRef:
name: billing-worker-env
Worker with health check and autoscaling
fullnameOverride: "email-sender"
image:
repository: my-registry/email-sender
tag: "latest"
service:
enabled: true
port: 8080
probes:
enabled: true
path: /healthz
port: http
bootstraptime: 60
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 5
targetCPUUtilizationPercentage: 80
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 250m
memory: 512Mi
Worker with MySQL TLS and Secrets
fullnameOverride: "data-processor"
image:
repository: my-registry/data-processor
tag: "v2.0.0"
replicaCount: 3
env:
- name: DB_HOST
value: my-db
- name: MYSQL_SSL_CA
value: /etc/mysql-tls/ca.crt
envFrom:
- secretRef:
name: data-processor-env
volumes:
- name: mysql-ca
configMap:
name: my-db-tls-ca
volumeMounts:
- name: mysql-ca
mountPath: /etc/mysql-tls
readOnly: true
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: 500m
memory: 1Gi
Parameters
Image
| Parameter | Description | Default |
|---|---|---|
image.repository |
Container image repository | nginx |
image.tag |
Container image tag (defaults to appVersion) | Set in each release |
image.pullPolicy |
Image pull policy | IfNotPresent |
imagePullSecrets |
Image pull secrets | [{name: 1x-ax}] |
Deployment
| Parameter | Description | Default |
|---|---|---|
replicaCount |
Number of replicas (ignored when autoscaling is enabled) | 1 |
nameOverride |
Override the chart name | "" |
fullnameOverride |
Override the full release name | "" |
podAnnotations |
Additional pod annotations | {} |
podLabels |
Additional pod labels | {} |
podSecurityContext |
Pod-level security context | {} |
securityContext |
Container-level security context | {} |
Service
| Parameter | Description | Default |
|---|---|---|
service.enabled |
Create a ClusterIP Service | false |
service.type |
Service type | ClusterIP |
service.port |
Service port | 8080 |
Probes
| Parameter | Description | Default |
|---|---|---|
probes.enabled |
Enable startup and liveness probes | false |
probes.path |
HTTP path for health checks | /healthz |
probes.port |
Port name for health checks | http |
probes.bootstraptime |
Seconds to wait for startup before failing | 30 |
Resources
| Parameter | Description | Default |
|---|---|---|
resources.limits.cpu |
CPU limit | 100m |
resources.limits.memory |
Memory limit | 256Mi |
resources.requests.cpu |
CPU request | 20m |
resources.requests.memory |
Memory request | 64Mi |
Autoscaling
| Parameter | Description | Default |
|---|---|---|
autoscaling.enabled |
Enable HPA | false |
autoscaling.minReplicas |
Minimum replicas | 1 |
autoscaling.maxReplicas |
Maximum replicas | 3 |
autoscaling.targetCPUUtilizationPercentage |
CPU target for scaling | 100 |
autoscaling.targetMemoryUtilizationPercentage |
Memory target for scaling (optional) | — |
Environment
| Parameter | Description | Default |
|---|---|---|
env |
Environment variables for the container | [] |
envFrom |
Environment variable sources (Secrets, ConfigMaps) | [] |
volumes |
Additional volumes | [] |
volumeMounts |
Additional volume mounts | [] |
Service account
| Parameter | Description | Default |
|---|---|---|
serviceAccount.create |
Create a ServiceAccount | false |
serviceAccount.automount |
Auto-mount API credentials | true |
serviceAccount.annotations |
ServiceAccount annotations | {} |
serviceAccount.name |
ServiceAccount name (auto-generated if empty) | "" |
Scheduling
| Parameter | Description | Default |
|---|---|---|
nodeSelector |
Node selector labels | {} |
tolerations |
Pod tolerations | [] |
affinity |
Pod affinity rules | {} |