Documentation / safeena
Safeena
A Helm chart for deploying HTTP services on Kubernetes behind the Kong ingress controller. Safeena (Arabic for “ship”) handles ingress, TLS certificates, Kong plugins, autoscaling, and health probes out of the box.
The chart requires the Kong ingress controller (IngressClass kong), which the Bahriya Kubernetes Engine installs. cert-manager is required only when the certificates list is used, and then with a ClusterIssuer of your choosing.
The chart’s App Version does not change, because the chart deploys whatever application image it is given rather than one particular application.
Prerequisites
- Kubernetes 1.23+
- Helm 3.x
- Kong Ingress Controller (IngressClass
kong) - cert-manager and a ClusterIssuer, if the
certificateslist is used - Valkey / Redis instance for rate limiting (if rate limiting is enabled)
Architecture
┌──────────────────┐
Internet ──────────► │ Kong Ingress │
│ (HTTPS, plugins)│
└────────┬─────────┘
│
┌────────▼─────────┐
│ Service (ClusterIP) │
└────────┬─────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌────▼───┐ ┌────▼───┐ ┌────▼───┐
│ Pod 0 │ │ Pod 1 │ │ Pod 2 │ (HPA-managed replicas)
└────────┘ └────────┘ └────────┘
- Kong terminates TLS and enforces HTTPS with 301 redirects.
- Pods are spread across nodes via
topologySpreadConstraints(built-in,maxSkew: 1,ScheduleAnyway). - HPA scales based on CPU utilisation (enabled by default, target 100%).
- Optional Kong plugins: basic auth, rate limiting, IP restriction (whitelist/blacklist).
- Optional www-to-root domain redirection via Kong redirect plugin.
Installation
# Basic deployment
helm install my-api charts/safeena \
--set image.repository=my-registry/my-api \
--set image.tag=v1.0.0
# With a fixed resource name, independent of the release name
helm install my-api charts/safeena \
--set fullnameOverride=my-api \
--set image.repository=my-registry/my-api \
--set image.tag=v1.0.0
# With autoscaling tuning
helm install my-api charts/safeena \
--set image.repository=my-registry/my-api \
--set autoscaling.minReplicas=2 \
--set autoscaling.maxReplicas=10 \
--set autoscaling.targetCPUUtilizationPercentage=70
Configuration
Ingress, hostnames, and TLS
Ingress is enabled by default with Kong as the ingress class. HTTPS is enforced via Kong annotations that redirect HTTP to HTTPS with a 301 status code.
Each hostname is an entry in ingress.hosts, and each TLS secret an entry in ingress.tls. Certificates are issued by explicit cert-manager Certificate resources, listed in certificates, rather than by ingress annotations. Each names a ClusterIssuer in your cluster, so hostnames in different DNS zones can use different issuers. A www. variant can be redirected to its apex through a per-host redirect Service.
ingress:
enabled: true
forceHttps: true
className: "kong"
hosts:
- host: example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www: { enabled: false }
- host: www.example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www:
enabled: true
serviceName: redirect-www-my-api-example-com
tls:
- secretName: my-api-tls
hosts:
- example.com
- www.example.com
certificates:
- secretName: my-api-tls
issuer: letsencrypt-prod # any ClusterIssuer in your cluster
dnsNames:
- example.com
- www.example.com
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 container:
volumes:
- name: config
configMap:
name: my-api-config
- name: tmp
emptyDir: {}
volumeMounts:
- name: config
mountPath: /etc/my-api
readOnly: true
- name: tmp
mountPath: /tmp
Health probes
Two probes are configured by default:
- Startup probe: HTTP GET to
/on porthttp, every 10 seconds, 6 failures allowed (60s total bootstrap time). - Liveness probe: HTTP GET to
/on porthttpafter startup succeeds.
Override the probe paths for APIs with custom health endpoints:
startupProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
failureThreshold: 6
livenessProbe:
httpGet:
path: /healthz
port: http
Autoscaling
Horizontal Pod Autoscaler is enabled by default targeting 100% CPU utilisation:
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 100
# targetMemoryUtilizationPercentage: 80
When autoscaling is enabled, the replicaCount value is ignored — the HPA manages replica count.
WWW → apex redirection
redirect.www is a list — one entry per apex hostname that opted into a www. → apex redirect. Each entry renders:
- A dedicated
Service(name:from the entry) annotated withkonghq.com/plugins: <name>. - A
KongPlugin(also named<name>) ofplugin: redirect, withkeep_incoming_path: trueso the path and query string are preserved. - A matching
ingress.hostsentry forwww.<apex>whosepaths[].redirect.www.serviceNamepoints at that Service.
redirect:
www:
- name: redirect-www-my-site-example-com
url: https://example.com
- name: redirect-www-my-site-customer-org
url: https://customer.org
ingress:
hosts:
- host: example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www: { enabled: false }
- host: www.example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www:
enabled: true
serviceName: redirect-www-my-site-example-com
- host: customer.org
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www: { enabled: false }
- host: www.customer.org
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www:
enabled: true
serviceName: redirect-www-my-site-customer-org
The www.<apex> host must appear in ingress.tls and certificates alongside its apex. The chart does not derive the redirect Service’s name: choose one, and use the same value in redirect.www[].name and in ingress.hosts[].paths[].redirect.www.serviceName. Each apex has its own Service and KongPlugin, so each can redirect to its own canonical URL.
Kong plugins
Basic authentication
kong:
basicAuth:
enabled: true
hideCredentials: true
credentials:
- username: admin
password: s3cret
When enabled, creates a KongPlugin (basic-auth), KongConsumer, and credential Secret for each entry. The plugin is automatically added to the ingress annotations.
Rate limiting
kong:
rateLimiting:
enabled: true
requestsPerMinute: 60
requestsPerHour: 1000
Uses Kong’s rate-limiting plugin backed by Valkey/Redis (valkey-ratelimiting-primary:6379). The plugin is fault-tolerant — if Redis is unavailable, requests are allowed through.
IP restriction
Whitelist or blacklist IP addresses:
kong:
ipRestriction:
whitelist:
enabled: true
ips:
- 203.0.113.0/24
- 198.51.100.10
blacklist:
enabled: false
ips: []
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. Containers deployed via Safeena can mount this ConfigMap to verify the MySQL server’s identity and establish encrypted connections.
Safeena values configuration
Use volumes, volumeMounts, and env in your Safeena values to wire the CA certificate into your application:
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 application then reads the MYSQL_SSL_CA environment variable and passes it to its MySQL driver as the CA certificate path. See the MySQL chart README for driver-specific examples (PHP/Kipchak, Node.js, Python, Go, CLI).
Examples
Kipchak API with autoscaling
fullnameOverride: "my-api"
image:
repository: registry.example.com/acme/my-api
tag: "v2.1.0"
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 8
targetCPUUtilizationPercentage: 70
env:
- name: APP_ENV
value: production
envFrom:
- secretRef:
name: my-api-env
certificates:
- secretName: my-api-tls
issuer: letsencrypt-prod
dnsNames:
- api.example.com
ingress:
hosts:
- host: api.example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www:
enabled: false
tls:
- secretName: my-api-tls
hosts:
- api.example.com
Website with a www-redirect
fullnameOverride: "my-website"
image:
repository: registry.example.com/acme/my-website
tag: "v1.0.0"
redirect:
www:
- name: redirect-www-my-website-example-com
url: https://example.com
certificates:
- secretName: my-website-tls
issuer: letsencrypt-prod
dnsNames:
- example.com
- www.example.com
ingress:
hosts:
- host: example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www: { enabled: false }
- host: www.example.com
paths:
- path: /
pathType: ImplementationSpecific
redirect:
www:
enabled: true
serviceName: redirect-www-my-website-example-com
tls:
- secretName: my-website-tls
hosts:
- example.com
- www.example.com
API with rate limiting and IP whitelist
fullnameOverride: "admin-api"
image:
repository: registry.example.com/acme/admin-api
tag: "v3.0.0"
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 250m
memory: 512Mi
kong:
rateLimiting:
enabled: true
requestsPerMinute: 30
requestsPerHour: 500
ipRestriction:
whitelist:
enabled: true
ips:
- 203.0.113.0/24
- 198.51.100.10
env:
- name: APP_ENV
value: production
envFrom:
- secretRef:
name: admin-api-env
API with MySQL TLS
fullnameOverride: "billing-api"
image:
repository: registry.example.com/acme/billing-api
tag: "v1.2.0"
volumes:
- name: mysql-ca
configMap:
name: billing-db-tls-ca
volumeMounts:
- name: mysql-ca
mountPath: /etc/mysql-tls
readOnly: true
env:
- name: APP_ENV
value: production
- name: MYSQL_SSL_CA
value: /etc/mysql-tls/ca.crt
envFrom:
- secretRef:
name: billing-api-env
Parameters
Image
| Parameter | Description | Default |
|---|---|---|
image.repository |
Container image repository | nginx |
image.tag |
Container image tag | 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.type |
Service type | ClusterIP |
service.port |
Service port | 80 |
Ingress
| Parameter | Description | Default |
|---|---|---|
ingress.enabled |
Enable Ingress resource | true |
ingress.forceHttps |
Force HTTPS via Kong 301 redirect | true |
ingress.className |
Ingress class name | "kong" |
ingress.annotations |
Additional ingress annotations | {} |
ingress.hosts |
List of {host, paths} entries |
See values.yaml |
ingress.hosts[].paths[].path |
URL path | / |
ingress.hosts[].paths[].pathType |
K8s path type | ImplementationSpecific |
ingress.hosts[].paths[].redirect.www.enabled |
Route this host to a www-redirect Service | false |
ingress.hosts[].paths[].redirect.www.serviceName |
Name of the redirect Service to back this host (must match an entry in redirect.www[].name) |
— |
ingress.tls |
List of {secretName, hosts} TLS bindings |
See values.yaml |
Certificates
| Parameter | Description | Default |
|---|---|---|
certificates |
List of cert-manager Certificate resources | [] |
certificates[].secretName |
Name of the TLS Secret to create | — |
certificates[].issuer |
ClusterIssuer name (see issuer table above) | — |
certificates[].dnsNames |
List of DNS names for the certificate | — |
Health probes
| Parameter | Description | Default |
|---|---|---|
startupProbe.httpGet.path |
Startup probe HTTP path | / |
startupProbe.httpGet.port |
Startup probe port | http |
startupProbe.periodSeconds |
Startup probe interval | 10 |
startupProbe.failureThreshold |
Startup probe max failures | 6 |
livenessProbe.httpGet.path |
Liveness probe HTTP path | / |
livenessProbe.httpGet.port |
Liveness probe port | http |
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 | true |
autoscaling.minReplicas |
Minimum replicas | 1 |
autoscaling.maxReplicas |
Maximum replicas | 3 |
autoscaling.targetCPUUtilizationPercentage |
CPU target for scaling | 100 |
autoscaling.targetMemoryUtilizationPercentage |
Memory target for scaling (optional) | — |
WWW redirect
redirect.www is a list — one entry per apex hostname that opts in. Each entry renders a Service + KongPlugin pair, addressed by name. The matching ingress.hosts[].paths[].redirect.www.serviceName must equal redirect.www[].name.
| Parameter | Description | Default |
|---|---|---|
redirect.www |
List of {name, url} redirect entries |
[] |
redirect.www[].name |
Service / KongPlugin name. Conventionally redirect-www-<container-handle>-<apex-with-dashes>. |
— |
redirect.www[].url |
Target URL the www. host redirects to (must include scheme). The Kong plugin appends the incoming path and query. |
— |
Kong plugins
| Parameter | Description | Default |
|---|---|---|
kong.basicAuth.enabled |
Enable basic authentication | false |
kong.basicAuth.hideCredentials |
Strip credentials from upstream request | true |
kong.basicAuth.credentials |
List of {username, password} entries |
[] |
kong.rateLimiting.enabled |
Enable rate limiting | false |
kong.rateLimiting.requestsPerMinute |
Requests per minute limit | null |
kong.rateLimiting.requestsPerHour |
Requests per hour limit | null |
kong.ipRestriction.whitelist.enabled |
Enable IP whitelist | false |
kong.ipRestriction.whitelist.ips |
Allowed IP addresses/CIDRs | [] |
kong.ipRestriction.blacklist.enabled |
Enable IP blacklist | false |
kong.ipRestriction.blacklist.ips |
Denied IP addresses/CIDRs | [] |
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 | {} |