- Introduced `FastcgiPlugin` for automatic HAProxy `fcgi-app` configuration generation. - Added example Docker Compose setup for PHP-FPM with FastCGI. - Updated documentation with detailed examples and usage instructions for FastCGI. - Included test cases to validate plugin behavior and generated configurations. - Enhanced `easymapping` to support `fcgi-app` definitions in global configs. |
||
|---|---|---|
| .. | ||
| certs/haproxy | ||
| php-app | ||
| docker-compose-acme.yml | ||
| docker-compose-changed-label.yml | ||
| docker-compose-cloudflare.yml | ||
| docker-compose-ip-whitelist.yml | ||
| docker-compose-jwt-validator.yml | ||
| docker-compose-multi-containers.yml | ||
| docker-compose-php-fpm.yml | ||
| docker-compose-plugins-combined.yml | ||
| docker-compose-portainer-app-example.yml | ||
| docker-compose-portainer.yml | ||
| docker-compose.yml | ||
| host2.local.pem | ||
| README.md | ||
Docker Compose Examples
This directory contains various Docker Compose examples demonstrating different EasyHAProxy configurations.
Examples Overview
1. Basic Configuration (docker-compose.yml)
What it demonstrates:
- Basic SSL setup with two virtual hosts
- SSL redirect (HTTP → HTTPS)
- Custom SSL certificates (embedded and file-based)
- HAProxy stats interface
Features:
host1.local: SSL certificate embedded as base64 in labelshost2.local: SSL certificate loaded from file (host2.local.pem)- Automatic HTTP to HTTPS redirect
- Stats available at port 1936
Usage:
docker compose up -d
Test:
# Test HTTPS
curl -k -H "Host: host1.local" https://127.0.0.1/
curl -k -H "Host: host2.local" https://127.0.0.1/
# Test HTTP redirect
curl -I -H "Host: host1.local" http://127.0.0.1
# Should return: HTTP/1.1 301 Moved Permanently
# View SSL certificate
openssl s_client -showcerts -connect 127.0.0.1:443 -servername host1.local
Access stats:
- URL: http://localhost:1936
- Username:
admin - Password:
password
2. ACME/Let's Encrypt (docker-compose-acme.yml)
What it demonstrates:
- Automatic SSL certificate generation using Let's Encrypt
- HTTP-01 ACME challenge
- Certificate persistence
Requirements:
- Public IP address pointing to your machine
- Open ports 80 and 443 in firewall
- Valid domain name
Configuration:
EASYHAPROXY_CERTBOT_EMAIL: user@example.com # Change this!
easyhaproxy.http.certbot: true # Enable certbot
Usage:
# Edit docker-compose-acme.yml and set:
# - EASYHAPROXY_CERTBOT_EMAIL to your email
# - easyhaproxy.http.host to your domain
docker compose -f docker-compose-acme.yml up -d
Certificate storage:
Certificates are persisted in ./certs/certbot/ to avoid re-challenges on restart.
3. Multiple Containers with Load Balancing (docker-compose-multi-containers.yml)
What it demonstrates:
- Multiple containers behind single domain
- Load balancing with round-robin
- Domain redirect functionality
Features:
- 2 replicas of nginx container
- Load balancing across replicas
- Domain redirect:
google.helloworld.com→www.google.com
Usage:
docker compose -f docker-compose-multi-containers.yml up -d
Test:
# Test load balancing (hostname changes between containers)
curl -H "Host: www.helloworld.com" localhost:19901
# Response: f6d8d45b7411
curl -H "Host: www.helloworld.com" localhost:19901
# Response: 59b213cb8592
# Test redirect
curl -I -H "Host: google.helloworld.com" localhost:19901
# Should redirect to: www.google.com/
4. Changed Label Prefix (docker-compose-changed-label.yml)
What it demonstrates:
- Using custom label prefix instead of default
easyhaproxy - Useful for running multiple EasyHAProxy instances
Configuration:
environment:
EASYHAPROXY_LABEL_PREFIX: myproxy
Container labels:
labels:
myproxy.http.host: example.com
myproxy.http.port: 80
5. Portainer Integration (docker-compose-portainer.yml)
What it demonstrates:
- Running Portainer behind EasyHAProxy
- Real-world application example
Access Portainer:
- URL: http://portainer.local (add to
/etc/hostsor use real DNS) - First time: Create admin user
6. Portainer + App Example (docker-compose-portainer-app-example.yml)
What it demonstrates:
- Multiple applications behind EasyHAProxy
- Portainer + custom app setup
Plugin Examples
FastCGI Plugin with PHP-FPM
Run PHP applications with FastCGI protocol support:
File: docker-compose-php-fpm.yml
What it demonstrates:
- PHP-FPM 8.5 with TCP connection on port 9000
- FastCGI protocol support (
proto: fcgi) - FastCGI plugin for PHP environment configuration
- Custom document root and index file
- PATH_INFO support for RESTful routing
Features:
- HAProxy forwards requests to PHP-FPM via TCP (port 9000)
- FastCGI plugin generates
fcgi-appconfiguration that defines CGI parameters:SCRIPT_FILENAME,DOCUMENT_ROOT,REQUEST_URIQUERY_STRING,REQUEST_METHOD,CONTENT_TYPESERVER_NAME,SERVER_PORT,HTTPSPATH_INFO(for routing support)
- Sample PHP application included in
php-app/directory
Configuration:
version: "3"
services:
haproxy:
image: byjg/easy-haproxy:4.6.0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
EASYHAPROXY_DISCOVER: docker
ports:
- "80:80/tcp"
php-fpm:
image: byjg/php:8.5-fpm
volumes:
- ./php-app:/var/www/html:ro
labels:
easyhaproxy.http.host: phpapp.local
easyhaproxy.http.port: 80
# PHP-FPM listens on port 9000
easyhaproxy.http.localport: 9000
easyhaproxy.http.proto: fcgi
# Enable FastCGI plugin
easyhaproxy.http.plugins: fastcgi
easyhaproxy.http.plugin.fastcgi.document_root: /var/www/html
easyhaproxy.http.plugin.fastcgi.index_file: index.php
easyhaproxy.http.plugin.fastcgi.path_info: "true"
Usage:
# Add to /etc/hosts
echo "127.0.0.1 phpapp.local" | sudo tee -a /etc/hosts
# Start the stack
docker compose -f docker-compose-php-fpm.yml up -d
# Test PHP application
curl http://phpapp.local/
curl http://phpapp.local/info.php
curl http://phpapp.local/test-path-info.php/users/123
Alternative: Unix Socket Connection
For PHP-FPM images that support Unix sockets, you can use socket connection:
services:
haproxy:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- php-fpm-socket:/run/php
php-fpm:
image: php:8.2-fpm # Official PHP image supports sockets
volumes:
- php-fpm-socket:/run/php
- ./php-app:/var/www/html:ro
labels:
easyhaproxy.http.host: phpapp.local
easyhaproxy.http.port: 80
easyhaproxy.http.socket: /run/php/php-fpm.sock
easyhaproxy.http.proto: fcgi
easyhaproxy.http.plugins: fastcgi
# ... plugin configuration
volumes:
php-fpm-socket:
Sample Application:
The php-app/ directory contains:
index.php- Main page showing FastCGI environmentinfo.php- PHP configuration info (phpinfo)test-path-info.php- PATH_INFO routing demonstration
What the FastCGI plugin does:
- Sets
SCRIPT_FILENAMEwith proper document root path - Handles directory requests (appends
index.php) - Sets all standard CGI environment variables
- Enables
PATH_INFOfor RESTful URL routing - Supports custom FastCGI parameters
JWT Validator Plugin
Protect your API with JWT token validation:
version: "3"
services:
haproxy:
image: byjg/easy-haproxy:4.6.0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./jwt_pubkey.pem:/etc/haproxy/jwt_keys/api_pubkey.pem:ro
environment:
EASYHAPROXY_DISCOVER: docker
HAPROXY_USERNAME: admin
HAPROXY_PASSWORD: password
HAPROXY_STATS_PORT: 1936
ports:
- "80:80/tcp"
- "443:443/tcp"
- "1936:1936/tcp"
api:
image: my-api:latest
labels:
easyhaproxy.http.host: api.example.com
easyhaproxy.http.port: 80
easyhaproxy.http.localport: 8080
# Enable JWT validation
easyhaproxy.http.plugins: jwt_validator
easyhaproxy.http.plugin.jwt_validator.algorithm: RS256
easyhaproxy.http.plugin.jwt_validator.issuer: https://auth.example.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
What it validates:
- Authorization header presence
- JWT signing algorithm
- JWT issuer and audience
- JWT signature using public key
- JWT expiration time
Test:
# Without token - should fail
curl http://api.example.com/endpoint
# Response: Missing Authorization HTTP header
# With valid JWT token
curl -H "Authorization: Bearer eyJhbGc..." http://api.example.com/endpoint
# Response: Success
Generate test public key:
# Generate private key
openssl genrsa -out jwt_private.pem 2048
# Extract public key
openssl rsa -in jwt_private.pem -pubout -out jwt_pubkey.pem
Cloudflare IP Restoration Plugin
Restore original visitor IPs when using Cloudflare CDN:
version: "3"
services:
haproxy:
image: byjg/easy-haproxy:4.6.0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./cloudflare_ips.lst:/etc/haproxy/cloudflare_ips.lst:ro
environment:
EASYHAPROXY_DISCOVER: docker
ports:
- "80:80/tcp"
- "443:443/tcp"
webapp:
image: my-webapp:latest
labels:
easyhaproxy.http.host: myapp.com
easyhaproxy.http.port: 80
easyhaproxy.http.localport: 3000
# Enable Cloudflare plugin
easyhaproxy.http.plugins: cloudflare
Setup Cloudflare IP list:
# Download Cloudflare IP ranges
curl https://www.cloudflare.com/ips-v4 > cloudflare_ips.lst
curl https://www.cloudflare.com/ips-v6 >> cloudflare_ips.lst
What it does:
- Detects requests from Cloudflare IPs
- Restores original visitor IP from
CF-Connecting-IPheader - Your application logs show real visitor IPs, not Cloudflare IPs
IP Whitelist Plugin
Restrict access to specific IP addresses:
version: "3"
services:
haproxy:
image: byjg/easy-haproxy:4.6.0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
EASYHAPROXY_DISCOVER: docker
ports:
- "80:80/tcp"
admin_panel:
image: admin-panel:latest
labels:
easyhaproxy.http.host: admin.example.com
easyhaproxy.http.port: 80
easyhaproxy.http.localport: 8080
# Enable IP whitelist
easyhaproxy.http.plugins: ip_whitelist
easyhaproxy.http.plugin.ip_whitelist.allowed_ips: 192.168.1.0/24,10.0.0.5
easyhaproxy.http.plugin.ip_whitelist.status_code: 403
Allowed IP formats:
- Single IP:
10.0.0.5 - CIDR range:
192.168.1.0/24 - Multiple (comma-separated):
192.168.1.0/24,10.0.0.5,172.16.0.100
Test:
# From allowed IP
curl http://admin.example.com
# Response: Success
# From blocked IP
curl http://admin.example.com
# Response: HTTP 403 Forbidden
Multiple Plugins Combined
Combine multiple plugins for enhanced security:
version: "3"
services:
haproxy:
image: byjg/easy-haproxy:4.6.0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./cloudflare_ips.lst:/etc/haproxy/cloudflare_ips.lst:ro
environment:
EASYHAPROXY_DISCOVER: docker
ports:
- "80:80/tcp"
- "443:443/tcp"
webapp:
image: webapp:latest
labels:
easyhaproxy.http.host: myapp.example.com
easyhaproxy.http.port: 80
easyhaproxy.http.localport: 8080
# Enable multiple plugins
easyhaproxy.http.plugins: cloudflare,deny_pages
# Block specific paths
easyhaproxy.http.plugin.deny_pages.paths: /admin,/wp-admin,/wp-login.php,/.env
easyhaproxy.http.plugin.deny_pages.status_code: 404
api:
image: api:latest
labels:
easyhaproxy.http.host: api.example.com
easyhaproxy.http.port: 80
easyhaproxy.http.localport: 3000
# Combine JWT + IP whitelist + path blocking
easyhaproxy.http.plugins: jwt_validator,ip_whitelist,deny_pages
easyhaproxy.http.plugin.jwt_validator.algorithm: RS256
easyhaproxy.http.plugin.jwt_validator.issuer: https://auth.example.com/
easyhaproxy.http.plugin.jwt_validator.pubkey_path: /etc/haproxy/jwt_keys/api.pem
easyhaproxy.http.plugin.ip_whitelist.allowed_ips: 192.168.0.0/16,10.0.0.0/8
easyhaproxy.http.plugin.deny_pages.paths: /internal,/debug
Plugin execution order:
- IP Whitelist (blocks non-whitelisted IPs)
- Deny Pages (blocks specific paths)
- JWT Validator (validates authentication)
Common Configuration Options
Environment Variables (HAProxy Container)
| Variable | Description | Default |
|---|---|---|
EASYHAPROXY_DISCOVER |
Discovery mode | docker |
EASYHAPROXY_SSL_MODE |
SSL mode (loose/strict) | strict |
EASYHAPROXY_CERTBOT_EMAIL |
Email for Let's Encrypt | - |
HAPROXY_CUSTOMERRORS |
Enable custom error pages | false |
HAPROXY_USERNAME |
Stats username | - |
HAPROXY_PASSWORD |
Stats password | - |
HAPROXY_STATS_PORT |
Stats port | 1936 |
Container Labels
| Label | Description | Example |
|---|---|---|
easyhaproxy.http.host |
Virtual host domain | example.com |
easyhaproxy.http.port |
External port | 80 |
easyhaproxy.http.localport |
Container port | 8080 |
easyhaproxy.http.redirect_ssl |
Force HTTPS redirect | true |
easyhaproxy.http.certbot |
Enable Let's Encrypt | true |
easyhaproxy.https.ssl |
Enable SSL | true |
easyhaproxy.https.sslcert |
Base64 SSL cert | LS0t... |
For complete documentation, see Container Labels.
Tips
-
Local Testing with Fake Domains: Add entries to
/etc/hosts:127.0.0.1 host1.local host2.local portainer.local -
Viewing Logs:
docker compose logs -f haproxy -
Reloading Configuration: EasyHAProxy automatically detects changes. Watch logs for reload events.
-
Generating Test SSL Certificates:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout host.key -out host.crt \ -subj "/CN=host1.local" cat host.crt host.key > host.pem -
Base64 Encoding SSL Certificate:
base64 -w 0 host.pem
Troubleshooting
Issue: Container not detected
- Check labels are correct (prefix, syntax)
- Verify Docker socket is mounted
- Check logs:
docker compose logs haproxy
Issue: SSL not working
- Verify certificate format (cert + key in same PEM file)
- Check certificate matches domain
- Verify SSL mode (
loosevsstrict)
Issue: Let's Encrypt fails
- Ensure ports 80/443 are publicly accessible
- Verify domain DNS points to your IP
- Check certbot logs in HAProxy container