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.69 - 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 and, optionally, its ServiceMonitor:
metrics:
enabled: true
serviceMonitor:
enabled: true
labels:
release: prometheus
metrics.enabled renders the local mod_status endpoint required by the sidecar. Use serverStatus.enabled only when status is needed without the exporter. In production, keep the status endpoint local to the pod, restrict the exporter port with NetworkPolicy, and ensure a Prometheus Operator selects the ServiceMonitor.
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.69 |
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. |
| Exporter cannot scrape Apache | serverStatus.require blocks the local sidecar |
Keep the default local requirement or permit the pod-local request. |
| 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