Skip to main content

Kubernetes

EasyHAProxy acts as an Ingress controller for Kubernetes — it watches Ingress resources with ingressClassName: easyhaproxy and configures HAProxy automatically.

How it works

EasyHAProxy queries all ingress definitions with either the spec.ingressClassName: easyhaproxy field (recommended) or the deprecated annotation kubernetes.io/ingress.class: easyhaproxy-ingress (for backward compatibility).

Deployment Modes

Choose the deployment mode that fits your infrastructure:

ModeWorkloadExposed PortsNode LabelRecommended When
NodePortDeployment31080 / 31443 / 31936NoMost setups (recommended)
ClusterIPDeploymentcluster-internal onlyNoBehind a LoadBalancer (recommended)
DaemonSetDaemonSet80 / 443 / 1936 (host)YesBare-metal, special cases

NodePort and ClusterIP run as a Deployment — no node label needed, and they survive node replacements safely.

DaemonSet binds to host ports and requires a node label (easyhaproxy/node=master) via nodeAffinity. The label must be manually reapplied after any node replacement, which can cause outages. Use only for bare-metal or special-case setups.

EasyHAProxy detects its deployment mode automatically (EASYHAPROXY_DEPLOYMENT_MODE=auto). See the environment variable reference for manual override options.

Step 1 — Install EasyHAProxy

kubectl create namespace easyhaproxy

Exposes HAProxy on NodePort 31080 (HTTP), 31443 (HTTPS), and 31936 (stats). Point your DNS or external load balancer to any node IP on these ports.

kubectl apply -f \
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.1/deploy/kubernetes/easyhaproxy-nodeport.yml

ClusterIP (behind a LoadBalancer)

Cluster-internal only. Pair with an external cloud LoadBalancer or kubectl port-forward for local testing.

kubectl apply -f \
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.1/deploy/kubernetes/easyhaproxy-clusterip.yml

DaemonSet (special cases — requires node label)

Requires node label maintenance

The node label must be reapplied after any node replacement. Failing to do so will cause an outage.

# Label the target node first
kubectl label nodes node-01 "easyhaproxy/node=master"

kubectl apply -f \
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/6.0.1/deploy/kubernetes/easyhaproxy-daemonset.yml

If you need to configure environment variables (log levels, stats password, etc.), see the environment variable reference.

Step 2 — Create an Ingress

kind: Ingress
metadata:
name: example-ingress
namespace: example
spec:
ingressClassName: easyhaproxy
rules:
- host: example.org
http:
paths:
- backend:
service:
name: example-service
port:
number: 8080
pathType: ImplementationSpecific

EasyHAProxy routes traffic from example.org:80 to your service at port 8080. No container port exposure needed.

Backward Compatibility

The deprecated annotation kubernetes.io/ingress.class: easyhaproxy-ingress is still supported but spec.ingressClassName is recommended for new deployments.

Step 3 — Verify

curl http://example.org

Kubernetes Annotations

Customize EasyHAProxy behavior per-ingress using annotations. For the full annotations reference, see Container Labels — Kubernetes Ingress Annotations.

Important: Annotations apply to all hosts in the ingress configuration.

Using Plugins

Add the easyhaproxy.plugins annotation with a comma-separated list of plugin names:

metadata:
annotations:
easyhaproxy.plugins: "cloudflare,deny_pages"
easyhaproxy.plugin.deny_pages.paths: "/wp-admin,/wp-login.php"
easyhaproxy.plugin.deny_pages.status_code: "404"

For full plugin documentation, see the Using Plugins guide.

Loading Plugin Configuration from Kubernetes Secrets

EasyHAProxy supports loading sensitive plugin configuration values from Kubernetes Secrets using the k8s_secret pattern:

# Auto-detect key (tries common variations):
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name"

# Explicit key (no variations):
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name/key_name"

How It Works

  1. You create a Kubernetes Secret with your sensitive data
  2. You reference the secret in your ingress annotation using the k8s_secret pattern
  3. EasyHAProxy reads the secret from the same namespace as the ingress
  4. EasyHAProxy transforms the annotation to inject the secret value
  5. The plugin receives the value as if it was provided directly in the annotation

Complete Example

---
apiVersion: v1
kind: Secret
metadata:
name: jwt-pubkey-secret
namespace: production
type: Opaque
stringData:
pubkey: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
easyhaproxy.plugins: "jwt_validator"
easyhaproxy.plugin.jwt_validator.algorithm: "RS256"
easyhaproxy.plugin.jwt_validator.issuer: "https://auth.example.com/"
easyhaproxy.plugin.jwt_validator.audience: "https://api.example.com"
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-pubkey-secret"
spec:
ingressClassName: easyhaproxy
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080

Priority Order

  1. Explicit annotation (e.g., easyhaproxy.plugin.jwt_validator.pubkey: "value")
  2. k8s_secret annotation (e.g., easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "secret")

Security Considerations

  • Secrets are read from the same namespace as the ingress (no cross-namespace access)
  • EasyHAProxy needs RBAC permissions to read secrets (included in default deployment)
  • Use Kubernetes RBAC to control which service accounts can read which secrets

ACME / Let's Encrypt

Add the easyhaproxy.certbot annotation to enable automatic certificate issuing:

kind: Ingress
metadata:
annotations:
easyhaproxy.certbot: 'true'
name: example-ingress
namespace: example
spec:
ingressClassName: easyhaproxy

More info in the ACME guide. Make sure ports 80 and 443 are publicly reachable.

Custom SSL Certificates

Create a secret with your certificate and key and associate it with your ingress:

---
apiVersion: v1
kind: Secret
metadata:
name: host2-tls
namespace: default
data:
tls.crt: base64 of your certificate
tls.key: base64 of your certificate private key
type: kubernetes.io/tls

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example
namespace: default
spec:
ingressClassName: easyhaproxy
tls:
- hosts:
- host2.local
secretName: host2-tls
rules:
...

Important Limitations

  • The implementation doesn't support all ingress properties or wildcard domains.
  • EasyHAProxy reads all spec.rules[].host values but parses only the first path per rule.

Open source ByJG