Using Plugins
EasyHAProxy supports a plugin system that extends HAProxy configuration with custom functionality. This guide explains how to use and configure plugins.
What are Plugins?
Plugins automatically run during the discovery cycle and can:
- Add HAProxy configuration directives
- Perform maintenance tasks
- Modify discovery data
- Integrate with external services
Plugin Types
Global Plugins
Execute once per discovery cycle regardless of how many domains are discovered.
Use cases:
- Cleanup tasks
- Global monitoring
- DNS updates
- Log management
Example: cleanup plugin
Domain Plugins
Execute once for each discovered domain/host.
Use cases:
- Domain-specific configuration
- IP restoration (Cloudflare)
- Path blocking
- Custom headers per domain
Examples: cloudflare, deny_pages
Built-in Plugins
EasyHAProxy includes several built-in plugins ready to use:
- Cloudflare - Restore visitor IP from Cloudflare CDN
- Cleanup - Cleanup temporary files
- Deny Pages - Block specific paths
- IP Whitelist - Restrict access to IPs/CIDR ranges
- JWT Validator - Validate JWT tokens
- FastCGI - Configure PHP-FPM and FastCGI applications
Configuration Methods
Plugins can be configured using different methods depending on your deployment environment:
1. Kubernetes Annotations (Ingress Resources)
Enable and configure domain plugins for specific Kubernetes ingresses:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: easyhaproxy-ingress
# Enable plugins
easyhaproxy.plugins: "jwt_validator,deny_pages"
# Configure jwt_validator plugin (protect specific paths only)
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.pubkey_path: "/etc/haproxy/jwt_keys/api_pubkey.pem"
easyhaproxy.plugin.jwt_validator.paths: "/api/admin,/api/users"
easyhaproxy.plugin.jwt_validator.only_paths: "false"
# Configure deny_pages plugin
easyhaproxy.plugin.deny_pages.paths: "/admin,/private"
easyhaproxy.plugin.deny_pages.status_code: "403"
name: api-ingress
namespace: production
spec:
rules:
- host: api.example.com
http:
paths:
- backend:
service:
name: api-service
port:
number: 8080
pathType: ImplementationSpecific
Annotation format:
- Enable plugins:
easyhaproxy.plugins: plugin1,plugin2 - Configure plugin:
easyhaproxy.plugin.<plugin_name>.<config_key>: value
See the Kubernetes guide for more examples.
2. Container Labels (Docker/Docker Compose)
Enable and configure domain plugins for specific Docker containers:
services:
webapp:
image: myapp:latest
labels:
easyhaproxy.http.host: example.com
easyhaproxy.http.port: 80
# Enable multiple plugins
easyhaproxy.http.plugins: cloudflare,deny_pages
# Configure deny_pages plugin
easyhaproxy.http.plugin.deny_pages.paths: /admin,/api/internal
easyhaproxy.http.plugin.deny_pages.status_code: 403
Label format:
- Enable plugins:
easyhaproxy.<definition>.plugins: plugin1,plugin2 - Configure plugin:
easyhaproxy.<definition>.plugin.<plugin_name>.<config_key>: value
Where <definition> is: http, https, tcp, etc.
3. Static YAML Configuration
Configure plugins in /etc/haproxy/static/config.yaml:
plugins:
# Global settings
abort_on_error: false # Log and continue on errors (recommended)
# Enable GLOBAL plugins (run once per discovery cycle)
enabled: [cleanup]
# Configure plugins (both global and domain plugins)
config:
# Global plugin configuration (cleanup runs once per cycle)
cleanup:
enabled: true
max_idle_time: 600
# Domain plugin configuration (applies to ALL domains by default)
cloudflare:
enabled: true # Apply to all domains
use_builtin_ips: true # Use built-in Cloudflare IPs
# Domain plugin disabled by default (enable per-domain via labels/annotations)
deny_pages:
enabled: false
Important distinctions:
- Global plugins (like
cleanup): Run once per discovery cycle, configured here only - Domain plugins (like
cloudflare,deny_pages,jwt_validator):- Configuration here sets defaults for ALL domains
- Can be enabled/disabled per-domain via container labels or Kubernetes annotations
- Per-domain configuration overrides these defaults
4. Environment Variables
Configure plugins via environment variables. Note: Environment variables set system-wide defaults and cannot configure plugins per-domain.
# Enable GLOBAL plugins (run once per discovery cycle)
EASYHAPROXY_PLUGINS_ENABLED=cleanup
EASYHAPROXY_PLUGINS_ABORT_ON_ERROR=false
# Configure GLOBAL plugins
EASYHAPROXY_PLUGIN_CLEANUP_ENABLED=true
EASYHAPROXY_PLUGIN_CLEANUP_MAX_IDLE_TIME=600
# Configure DOMAIN plugins (sets defaults for ALL domains)
EASYHAPROXY_PLUGIN_CLOUDFLARE_ENABLED=true
EASYHAPROXY_PLUGIN_CLOUDFLARE_USE_BUILTIN_IPS=true
Variable format:
- Enable global plugins:
EASYHAPROXY_PLUGINS_ENABLED=plugin1,plugin2 - Configure plugin:
EASYHAPROXY_PLUGIN_<PLUGIN_NAME>_<CONFIG_KEY>=value
Scope limitations:
- Global plugins: Environment variables configure the single instance
- Domain plugins: Environment variables set defaults for ALL domains
- Per-domain configuration: Use container labels (Docker) or annotations (Kubernetes) instead
Common Use Cases
Protect API with JWT Authentication
Secure entire API domain:
services:
api:
labels:
easyhaproxy.http.host: api.example.com
easyhaproxy.http.plugins: jwt_validator
easyhaproxy.http.plugin.jwt_validator.issuer: https://auth0.myapp.com/
easyhaproxy.http.plugin.jwt_validator.audience: https://api.example.com
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
volumes:
- ./auth_pubkey.pem:/etc/haproxy/jwt_keys/api_pubkey.pem:ro
Protect only admin/sensitive endpoints:
services:
api:
labels:
easyhaproxy.http.host: api.example.com
easyhaproxy.http.plugins: jwt_validator
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
easyhaproxy.http.plugin.jwt_validator.paths: /api/admin,/api/users,/api/billing
easyhaproxy.http.plugin.jwt_validator.only_paths: false
volumes:
- ./auth_pubkey.pem:/etc/haproxy/jwt_keys/api_pubkey.pem:ro
# /api/health, /api/docs, etc. remain publicly accessible
Restrict API to only allow specific endpoints:
services:
api:
labels:
easyhaproxy.http.host: api.example.com
easyhaproxy.http.plugins: jwt_validator
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api_pubkey.pem
easyhaproxy.http.plugin.jwt_validator.paths: /api/v1,/api/v2
easyhaproxy.http.plugin.jwt_validator.only_paths: true
volumes:
- ./auth_pubkey.pem:/etc/haproxy/jwt_keys/api_pubkey.pem:ro
# All paths except /api/v1 and /api/v2 are denied
Restrict Admin Panel to Office IPs
Protect admin panel by only allowing access from office network:
labels:
easyhaproxy.http.host: admin.example.com
easyhaproxy.http.plugins: ip_whitelist
easyhaproxy.http.plugin.ip_whitelist.allowed_ips: 203.0.113.0/24,198.51.100.42
Protect Admin Paths
Block access to WordPress admin and other sensitive paths:
labels:
easyhaproxy.http.host: wordpress.example.com
easyhaproxy.http.plugins: deny_pages
easyhaproxy.http.plugin.deny_pages.paths: /wp-admin,/wp-login.php,/.env
easyhaproxy.http.plugin.deny_pages.status_code: 404
Cloudflare IP Restoration
Restore original visitor IPs for applications behind Cloudflare:
labels:
easyhaproxy.http.host: myapp.com
easyhaproxy.http.plugins: cloudflare
Multiple Plugins Together
Combine multiple plugins for one domain:
labels:
easyhaproxy.http.host: secure-app.com
easyhaproxy.http.plugins: cloudflare,deny_pages
easyhaproxy.http.plugin.deny_pages.paths: /admin,/config
easyhaproxy.http.plugin.deny_pages.status_code: 403
Automatic Cleanup
Keep your system clean with automatic temp file removal:
# /etc/haproxy/static/config.yaml
plugins:
enabled: [cleanup]
config:
cleanup:
enabled: true
max_idle_time: 3600 # 1 hour
Error Handling
Log and Continue (Recommended)
By default, plugin errors are logged as warnings and discovery continues:
plugins:
abort_on_error: false # Default
When to use: Most situations. Ensures a failing plugin doesn't prevent HAProxy updates.
Behavior:
- Plugin errors logged as warnings
- Discovery cycle continues
- Other plugins still execute
- HAProxy config is generated without the failed plugin
Abort on Error
Stop discovery cycle if any plugin fails:
plugins:
abort_on_error: true
When to use: Critical plugins where failure should halt deployment.
Behavior:
- Plugin error stops discovery
- Previous HAProxy config remains active
- No configuration changes until issue is resolved
Troubleshooting
Enable Debug Logging
See detailed plugin execution information:
EASYHAPROXY_LOG_LEVEL=DEBUG
Look for:
INFO: Loaded builtin plugin: cloudflare (domain)
INFO: Loaded builtin plugin: cleanup (global)
DEBUG: Executing domain plugin: cloudflare for domain: example.com
DEBUG: Plugin cloudflare metadata: {'domain': 'example.com', 'ip_list_path': '/etc/haproxy/cloudflare_ips.lst'}
Plugin Not Loading
Check:
- Plugin file exists in
/etc/haproxy/plugins/or builtin directory - Python syntax is valid
- Plugin class inherits from
PluginInterface - Check logs for load errors
Plugin Not Executing
For domain plugins:
- Check container has label:
easyhaproxy.http.plugins: plugin_name - Verify plugin name is correct (case-sensitive)
- Enable debug logging
For global plugins:
- Check YAML config:
plugins.enabled: [plugin_name] - Or env var:
EASYHAPROXY_PLUGINS_ENABLED=plugin_name - Enable debug logging
Configuration Not Applied
Check precedence order:
For Kubernetes deployments:
- Ingress annotations (highest)
- YAML configuration
- Environment variables (lowest)
For Docker deployments:
- Container labels (highest)
- YAML configuration
- Environment variables (lowest)
Per-ingress/per-container settings override global configuration.
Plugin Output Missing
Verify:
- Plugin is enabled (
enabled: true) - Plugin configuration is correct
- Plugin's
process()method returns validPluginResult - Check debug logs for plugin execution
Best Practices
- Start with log-and-continue mode - Use
abort_on_error: falseuntil you're confident plugins are stable - Use container labels for domain-specific config - Easier to manage per-service
- Use YAML/env for global config - Better for global plugins and defaults
- Enable debug logging during testing - Helps identify configuration issues
- Test plugin changes in staging first - Avoid production surprises
- Keep plugin configurations simple - Use defaults when possible
Limitations
- Plugins must be written in Python
- Domain plugins execute for each domain, so keep them lightweight
- Plugins cannot modify the Jinja2 template structure directly
- Plugin errors in abort mode prevent all configuration updates
Creating Custom Plugins
Want to create your own plugins? See the Plugin Developer Guide for detailed instructions on building custom plugins that extend EasyHAProxy functionality.
Further Reading
- Plugin Developer Guide - Create custom plugins
- Container Labels - Label configuration reference
- Environment Variables - Environment variable reference
- Static Configuration - YAML configuration reference
- Kubernetes Guide - Using plugins with Kubernetes