Apache
Apache HTTP Server chart for Kubernetes using the official docker.io/library/httpd image. The chart is designed for static sites, reverse-proxy front doors, and internal web endpoints that need a small, hardened web server with clear Kubernetes integration points.
Key Features
- Official
httpdimage pinned to Apache HTTP Server2.4.68 - Non-root runtime on port
8080with a read-only root filesystem - Generated Apache configuration for security headers, health checks, logging, and static content
- Inline content, existing ConfigMap content, and extra virtual host snippets
- Ingress and Gateway API routing with separate value contracts
- Optional Basic Auth from an existing Secret or External Secrets Operator
- Apache exporter sidecar, ServiceMonitor, HPA, PDB, NetworkPolicy, and dual-stack Services
- Helm tests that verify the
/healthzendpoint through the Service
Installation
Install from the HelmForge HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install apache helmforge/apache --namespace apache --create-namespace
Install from OCI:
helm install apache oci://ghcr.io/helmforgedev/helm/apache \
--namespace apache \
--create-namespace
Architecture
The chart runs Apache HTTP Server as a Deployment. Apache listens on a non-root container port, serves content from a ConfigMap-backed document root, writes logs to stdout and stderr, and exposes /healthz for probes and Helm tests.
Typical traffic flow:
- Client traffic enters through Ingress, Gateway API, or the cluster Service.
- The Service forwards traffic to Apache pods on port
8080. - Apache serves chart-managed static content, a user-managed ConfigMap, or custom virtual host snippets.
- Optional Basic Auth reads htpasswd material from a Secret.
- Optional metrics scrape the exporter sidecar, which reads Apache
mod_status.
The chart intentionally does not install a certificate manager, ingress controller, Gateway implementation, WAF, content build system, PHP runtime, or external synchronization agent.
Differentiators
The Apache chart is useful when you need a secure web server primitive rather than a full application platform:
- It uses the official Apache image without wrapping it in a custom runtime.
- It defaults to a non-root, read-only filesystem posture while keeping the required Apache write paths explicit.
- It treats static content as a deployable artifact through ConfigMap names, which fits GitOps rollouts.
- It can expose the same workload through Ingress or Gateway API without changing application pods.
- It keeps Basic Auth as a Kubernetes Secret concern and integrates with External Secrets Operator for provider-backed secrets.
- It includes NetworkPolicy and Service dual-stack fields for platform teams that enforce network boundaries centrally.
Quick Start
Deploy a small static page from inline values:
content:
files:
index.html: |
<!doctype html>
<html lang="en">
<body>
<h1>Hello from Apache</h1>
</body>
</html>
Validate the deployment:
helm test apache -n apache
kubectl port-forward svc/apache -n apache 8080:80
curl -fsS http://localhost:8080/healthz
Production Pattern
For production, keep web content in an immutable ConfigMap that is created by your release pipeline. Roll the Helm release by changing the ConfigMap name.
replicaCount: 3
content:
existingConfigMap: apache-site-content-20260613
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1
memory: 512Mi
pdb:
enabled: true
minAvailable: 1
networkPolicy:
enabled: true
ingress:
enabled: false
egress:
enabled: true
allowDns: true
allowInternet: false
extraEgress:
- to:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 443
Use immutable content names instead of mutating a live ConfigMap. That keeps rollbacks deterministic and avoids confusing pod checksums.
Routing
Ingress
Ingress uses the HelmForge-standard ingress.ingressClassName key:
ingress:
enabled: true
ingressClassName: nginx
hosts:
- host: apache.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: apache-tls
hosts:
- apache.example.com
Gateway API
Gateway API renders an HTTPRoute. The chart points route backends to the Apache Service automatically.
gateway:
enabled: true
parentRefs:
- name: public-gateway
namespace: gateway-system
hostnames:
- apache.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
Gateway API requires Gateway API CRDs and a compatible controller in the cluster. See the official Gateway API documentation.
Dual-Stack Services
Dual-stack fields are optional and map directly to Kubernetes Service fields:
service:
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv4
- IPv6
Use SingleStack with one family when validating on IPv4-only local clusters.
Authentication And External Secrets
Basic Auth expects an htpasswd file from a Secret:
basicAuth:
enabled: true
existingSecret: apache-basicauth
htpasswdKey: htpasswd
For GitOps installations, let External Secrets Operator reconcile that Secret:
basicAuth:
enabled: true
existingSecret: apache-basicauth
externalSecrets:
enabled: true
secretStoreRef:
name: cluster-secrets
kind: ClusterSecretStore
data:
- secretKey: htpasswd
remoteRef:
key: apache/basicauth
property: htpasswd
The chart fails template rendering when ExternalSecret is enabled without data or dataFrom. See the official External Secrets Operator documentation.
Observability
Enable the exporter only when Apache mod_status is explicitly allowed for the scrape path:
metrics:
enabled: true
serviceMonitor:
enabled: true
labels:
release: prometheus
serverStatus:
enabled: true
require: 'local'
In production, keep mod_status local to the pod and let the exporter scrape it from the sidecar path. Do not expose the status endpoint to general client traffic.
Network Policy
NetworkPolicy is opt-in because enforcement depends on the cluster CNI:
networkPolicy:
enabled: true
ingress:
enabled: true
namespaceSelector:
kubernetes.io/metadata.name: ingress-nginx
egress:
enabled: true
allowDns: true
allowInternet: false
Set networkPolicy.egress.allowInternet=true only when Apache needs outbound access for reverse proxy targets or remote content integrations.
Use networkPolicy.egress.extraEgress for complete additional egress rules with to and ports.
Configuration Reference
Core
| Parameter | Default | Description |
|---|---|---|
replicaCount |
2 |
Apache pod count when HPA is disabled. |
nameOverride |
"" |
Chart name override. |
fullnameOverride |
"" |
Full release name override. |
commonLabels |
{} |
Labels added to chart resources. |
serverName |
localhost |
Apache ServerName value. |
image.repository |
docker.io/library/httpd |
Official Apache HTTP Server image. |
image.tag |
2.4.68 |
Apache image tag. |
image.pullPolicy |
IfNotPresent |
Image pull policy. |
imagePullSecrets |
[] |
Image pull secrets. |
containerPorts.http |
8080 |
Non-root Apache listener port. |
containerPorts.metrics |
9117 |
Exporter sidecar port. |
Apache Configuration
| Parameter | Default | Description |
|---|---|---|
httpd.serverTokens |
Prod |
Apache ServerTokens value. |
httpd.serverSignature |
Off |
Apache ServerSignature value. |
httpd.traceEnable |
Off |
Apache TraceEnable value. |
httpd.logLevel |
warn |
Apache log level. |
httpd.allowOverride |
None |
Directory AllowOverride setting. |
httpd.options |
-Indexes +FollowSymLinks |
Directory Options setting. |
httpd.extraConfig |
"" |
Extra snippets appended to httpd.conf. |
httpd.extraVhosts |
{} |
Additional virtual host snippets. |
Content
| Parameter | Default | Description |
|---|---|---|
content.existingConfigMap |
"" |
Existing ConfigMap containing web root files. |
content.files |
sample index.html |
Files rendered into the chart-managed content ConfigMap. |
Authentication And Secrets
| Parameter | Default | Description |
|---|---|---|
basicAuth.enabled |
false |
Enable Basic Auth using htpasswd data. |
basicAuth.existingSecret |
"" |
Secret containing htpasswd data. |
basicAuth.htpasswdKey |
htpasswd |
Secret key for the htpasswd file. |
externalSecrets.enabled |
false |
Render an ExternalSecret for Basic Auth material. |
externalSecrets.refreshInterval |
0 |
ExternalSecret refresh interval. |
externalSecrets.secretStoreRef.name |
"" |
SecretStore or ClusterSecretStore name. |
externalSecrets.secretStoreRef.kind |
SecretStore |
Secret store kind. |
externalSecrets.target.creationPolicy |
Owner |
ExternalSecret target creation policy. |
externalSecrets.data |
[] |
Individual provider key mappings. |
externalSecrets.dataFrom |
[] |
Provider extraction blocks. |
Service And Routing
| Parameter | Default | Description |
|---|---|---|
service.type |
ClusterIP |
Kubernetes Service type. |
service.annotations |
{} |
Service annotations. |
service.port |
80 |
Public HTTP Service port. |
service.metricsPort |
9117 |
Metrics Service port. |
service.ipFamilyPolicy |
null |
Optional Service IP family policy. |
service.ipFamilies |
[] |
Optional Service IP families. |
ingress.enabled |
false |
Render an Ingress. |
ingress.ingressClassName |
"" |
IngressClass name. |
ingress.annotations |
{} |
Ingress annotations. |
ingress.hosts |
apache.local |
Ingress host and path rules. |
ingress.tls |
[] |
Ingress TLS entries. |
gateway.enabled |
false |
Render a Gateway API HTTPRoute. |
gateway.apiVersion |
gateway.networking.k8s.io/v1 |
HTTPRoute API version. |
gateway.parentRefs |
[] |
Gateway parent references. |
gateway.hostnames |
[] |
HTTPRoute hostnames. |
gateway.rules |
path prefix / |
HTTPRoute match rules. |
Network Policy
| Parameter | Default | Description |
|---|---|---|
networkPolicy.enabled |
false |
Render NetworkPolicy resources. |
networkPolicy.ingress.enabled |
true |
Restrict inbound traffic when policy is enabled. |
networkPolicy.ingress.namespaceSelector |
{} |
Allowed namespace selector. |
networkPolicy.ingress.podSelector |
{} |
Allowed pod selector. |
networkPolicy.egress.enabled |
true |
Restrict outbound traffic when policy is enabled. |
networkPolicy.egress.allowDns |
true |
Allow DNS egress. |
networkPolicy.egress.allowInternet |
true |
Allow IPv4 and IPv6 internet egress. |
networkPolicy.egress.extraRules |
[] |
Legacy additional egress rules. |
networkPolicy.egress.extraEgress |
[] |
Additional complete egress rule objects. |
Probes, Resources, And Scheduling
| Parameter | Default | Description |
|---|---|---|
livenessProbe.* |
see values | Apache liveness probe timings. |
readinessProbe.* |
see values | Apache readiness probe timings. |
startupProbe.* |
see values | Apache startup probe timings. |
resources.requests.cpu |
50m |
Apache CPU request. |
resources.requests.memory |
64Mi |
Apache memory request. |
resources.limits.cpu |
500m |
Apache CPU limit. |
resources.limits.memory |
256Mi |
Apache memory limit. |
podSecurityContext |
non-root fsGroup + seccomp | Pod-level security context. |
securityContext |
non-root, no privilege escalation | Container-level security context. |
nodeSelector |
{} |
Node selector. |
tolerations |
[] |
Pod tolerations. |
affinity |
{} |
Pod affinity. |
topologySpreadConstraints |
[] |
Pod topology spread constraints. |
priorityClassName |
"" |
PriorityClass name. |
terminationGracePeriodSeconds |
30 |
Pod termination grace period. |
Availability And Metrics
| Parameter | Default | Description |
|---|---|---|
pdb.enabled |
false |
Render a PodDisruptionBudget. |
pdb.minAvailable |
1 |
Minimum available pods. |
autoscaling.enabled |
false |
Render an HPA. |
autoscaling.minReplicas |
2 |
HPA minimum replicas. |
autoscaling.maxReplicas |
10 |
HPA maximum replicas. |
autoscaling.targetCPUUtilizationPercentage |
80 |
HPA CPU target. |
metrics.enabled |
false |
Enable Apache exporter sidecar. |
metrics.image.repository |
docker.io/lusotycoon/apache-exporter |
Exporter image repository. |
metrics.image.tag |
v1.0.11 |
Exporter image tag. |
metrics.resources |
small defaults | Exporter resources. |
metrics.serviceMonitor.enabled |
false |
Render a ServiceMonitor. |
metrics.serviceMonitor.interval |
30s |
Scrape interval. |
metrics.serviceMonitor.labels |
{} |
ServiceMonitor labels. |
serverStatus.enabled |
false |
Enable Apache mod_status config. |
serverStatus.path |
/server-status |
Status endpoint path. |
serverStatus.require |
local |
Apache Require expression for status access. |
Extension Points
| Parameter | Default | Description |
|---|---|---|
serviceAccount.create |
true |
Create a dedicated ServiceAccount. |
serviceAccount.name |
"" |
ServiceAccount name override. |
serviceAccount.annotations |
{} |
ServiceAccount annotations. |
podLabels |
{} |
Additional pod labels. |
podAnnotations |
{} |
Additional pod annotations. |
annotations |
{} |
Additional resource annotations. |
extraEnv |
[] |
Additional Apache container environment variables. |
extraVolumes |
[] |
Additional pod volumes. |
extraVolumeMounts |
[] |
Additional Apache volume mounts. |
extraManifests |
[] |
Additional manifests rendered verbatim. |
Examples
Common Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
/healthz returns 404 |
Custom Apache config no longer serves the generated health file | Preserve the generated health alias or update probe paths. |
403 from /server-status |
serverStatus.require blocks the exporter |
Permit the exporter path or disable metrics. |
| Basic Auth is ignored | Secret key does not match basicAuth.htpasswdKey |
Check Secret data keys and ExternalSecret target keys. |
| Gateway route has no traffic | parentRefs or listener hostnames do not match the Gateway |
Check Gateway readiness and HTTPRoute attachment status. |
| NetworkPolicy blocks traffic | Namespace or pod selectors do not match callers | Adjust selectors or disable the policy until the CNI rules are verified. |
| Content does not change | Existing ConfigMap name did not change | Publish a new immutable ConfigMap and update content.existingConfigMap. |
Validation
After deployment:
helm test apache -n apache
kubectl get pods -n apache -l app.kubernetes.io/name=apache
kubectl rollout status deployment/apache -n apache
kubectl logs -n apache deploy/apache --all-containers --since=10m
kubectl get events -n apache --sort-by=.lastTimestamp