A Docker image that combines haproxy and acme.sh.
The combination of haproxy and acme.sh provides a lightweight alternative to Traefik to implement SSL (TLS) termination for public facing Docker services. A main advantage is the decentralized organization of certificates and the implementation of the Zero Trust principle within a container group.
The haproxy-acme-http01 image is a ready-to-run image for local SSL termination and has the following core features:
- HAProxy listening on port
80and443- Port
80is used for theHTTP-01ACME certificate challenge and otherwise redirects tohttpsby default - Port
443redirects traffic to a configurablehost:portand provides SSL termination
- Port
- Issues a SSL certificate on startup
- Configurable ACME provider (
Let's Encrypt,ZeroSSL, ...) - Configurable key length (
2048,4096,ec-256, ...) - Supports SAN certificates
- Configurable ACME provider (
- Automatic certificate renewal
- HAProxy Hitless Reload (zero downtime)
- Configurable certificate renewal notifications (WiP)
docker run -d --name haproxy-acme-http01 \
-e "[email protected]" \
-e "ACME_DOMAIN=domain.com" \
-e "SERVER_ADDRESS=whoami" \
-e "SERVER_PORT=80" \
-v /docker_data/acme:/var/lib/acme:rw \
-p 80:80 \
-p 443:443 \
--sysctl net.ipv4.ip_unprivileged_port_start=0 \
ghcr.io/flobernd/haproxy-acme-http01services:
haproxy-acme:
image: ghcr.io/flobernd/haproxy-acme-http01:latest
container_name: haproxy-acme-http01
restart: unless-stopped
environment:
- [email protected]
- ACME_DOMAIN=domain.com
- SERVER_ADDRESS=whoami
- SERVER_PORT=80
volumes:
- /docker_data/acme:/var/lib/acme:rw
ports:
- 80:80
- 443:443
sysctls:
- net.ipv4.ip_unprivileged_port_start = 0
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stoppedIt is strongly recommended to specify an external volume for the /var/lib/acme directory. Most ACME servers enforce a rate limit for issuing and renewing certificates. If you recreate the container without preserving the internal state of acme.sh, a new certificate will also be created each time.
The container creates a default configuration file haproxy.cfg in the /usr/local/etc/haproxy directory.
By mapping the aforementioned path, the primary haproxy.cfg can be freely customized. Alternatively, additional configurations can be placed in the include directory, which are then loaded after the primary configuration in alphabetical order.
As long as the default config has not been modified or overwritten, the SERVER_ADDRESS (required), SERVER_PORT (default 80), HAPROXY_HTTP_PORT (default 80) and HAPROXY_HTTPS_PORT (default 443) environment variables must be set. Otherwise the container will fail to start.
Important
When overwriting the default configuration, make sure that the stats socket directive is retained. Otherwise, the deployment of certificates will fail.
global
# Allow 'acme.sh' to deploy new certificates without reloading
stats socket /var/lib/haproxy/admin.sock level admin mode 660
Important
When overwriting the default configuration, make sure that the http frontend correctly responds to the http-01 challenge. Otherwise, the issuing of certificates will fail.
frontend http
mode http
[...]
# Respond to ACME HTTP-01 challenge
http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].${ACME_ACCOUNT_THUMBPRINT}\n" if { path_beg '/.well-known/acme-challenge/' }
Set to 1 in order to enable verbose acme.sh debug output.
Set to 1 in order to automatically update acme.sh to the latest version on container startup. This requires an active internet connection (default: 0).
Set to 1 in order to enable the certificate renewal cronjob (default: 1).
The ACME server to use (default: letsencrypt).
Supported values: letsencrypt, letsencrypt_test, buypass, buypass_test, zerossl, sslcom, google, googletest or an explicit ACME server directory URL like e.g. https://acme-v02.api.letsencrypt.org/directory
See also: https://github.com/acmesh-official/acme.sh/wiki/Server
The mail address for the ACME account registration (required).
The domain to issue the certificate for (required). To issue a multi-domain certificate (SAN), enter additional domains separated by a space character after the primary domain.
For example: sub.domain.com (single), domain.com *.domain.com (SAN)
The desired domain key length (default: ec-256).
Supported values (depending on the ACME server capabilities): 2048, 3072, 4096, 8192, ec-256, ec-384, ec-521.
The internal haproxy HTTP listening port. Allows changing the internal port to a non-privileged one (default: 80). Do not forget to adjust the Docker port mapping accordingly (e.g. -p 80:8080).
The internal haproxy HTTPS listening port. Allows changing the internal port to a non-privileged one (default: 443). Do not forget to adjust the Docker port mapping accordingly (e.g. -p 443:8443).
The hostname or IP-address of the internal service for which SSL terminiation should be provided (required).
The port of the internal service for which SSL terminiation should be provided (default: 80).
Optional directives for communicating with the internal service. For example ssl must be specified here, if the internal service uses HTTPS. Check the haproxy documentation for more directives.
The haproxy-acme-dns01 image is a ready-to-run image for local SSL termination and has the following core features:
- Issues a SSL certificate on startup
- Configurable ACME provider (
Let's Encrypt,ZeroSSL, ...) - Configurable key length (
2048,4096,ec-256, ...) - Supports SAN certificates
- Supports wildcard certificates
- Configurable ACME provider (
- Automatic certificate renewal
- HAProxy Hitless Reload (zero downtime)
- Configurable certificate renewal notifications (WiP)
docker run -d --name haproxy-acme-dns01 \
-e "[email protected]" \
-e "ACME_DOMAIN=domain.com *.domain.com" \
-e "ACME_DNS_API=dns_cf" \
-e "CF_Token=<redacted>" \
-e "CF_Zone_ID=<redacted>" \
-v /docker_data/acme:/var/lib/acme:rw \
-p 80:80 \
-p 443:443 \
ghcr.io/flobernd/haproxy-acme-dns01services:
haproxy-acme:
image: ghcr.io/flobernd/haproxy-acme-dns01:latest
container_name: haproxy-acme-dns01
restart: unless-stopped
environment:
- "[email protected]"
- "ACME_DOMAIN=domain.com *.domain.com"
- "ACME_DNS_API=dns_cf"
- "CF_Token=<redacted>"
- "CF_Zone_ID=<redacted>"
volumes:
- /docker_data/acme:/var/lib/acme:rw
ports:
- 80:80
- 43:443It is strongly recommended to specify an external volume for the /var/lib/acme directory. Most ACME servers enforce a rate limit for issuing and renewing certificates. If you recreate the container without preserving the internal state of acme.sh, a new certificate will also be created each time.
The container creates a default configuration file haproxy.cfg in the /usr/local/etc/haproxy directory.
By mapping the aforementioned path, the primary haproxy.cfg can be freely customized. Alternatively, additional configurations can be placed in the include directory, which are then loaded after the primary configuration in alphabetical order.
As long as the default config has not been modified or overwritten, the SERVER_ADDRESS (required), SERVER_PORT (default 80), HAPROXY_HTTP_PORT (default 80) and HAPROXY_HTTPS_PORT (default 443) environment variables must be set. Otherwise the container will fail to start.
Important
When overwriting the default configuration, make sure that the stats socket directive is retained. Otherwise, the deployment of certificates will fail.
global
# Allow 'acme.sh' to deploy new certificates without reloading
stats socket /var/lib/haproxy/admin.sock level admin mode 660
Set to 1 in order to enable verbose acme.sh debug output.
Set to 1 in order to automatically update acme.sh to the latest version on container startup. This requires an active internet connection (default: 0).
Set to 1 in order to enable the certificate renewal cronjob (default: 1).
The ACME server to use (default: letsencrypt).
Supported values: letsencrypt, letsencrypt_test, buypass, buypass_test, zerossl, sslcom, google, googletest or an explicit ACME server directory URL like e.g. https://acme-v02.api.letsencrypt.org/directory
See also: https://github.com/acmesh-official/acme.sh/wiki/Server
The mail address for the ACME account registration (required).
The domain to issue the certificate for (required). To issue a multi-domain certificate (SAN), enter additional domains separated by a space character after the primary domain.
For example: sub.domain.com (single), domain.com *.domain.com (SAN)
The desired domain key length (default: ec-256).
Supported values (depending on the ACME server capabilities): 2048, 3072, 4096, 8192, ec-256, ec-384, ec-521.
The DNS API to use (required).
Supported values (incomplete): dns_cf (Cloudflare), dns_azure (Azure), dns_gcloud (Google Cloud), ...
Depending on the DNS API, additional environment variables must be passed to the container. For example, dns_cf requires the CF_Token and CF_Zone_ID/CF_Account_ID environment variables to be set.
See also: https://github.com/acmesh-official/acme.sh/wiki/dnsapi
The time in seconds to wait for all the TXT records to propagate in DNS API mode. It's not necessary to use this by default, acme.sh polls DNS status by DOH automatically.
The internal haproxy HTTP listening port. Allows changing the internal port to a non-privileged one (default: 80). Do not forget to adjust the Docker port mapping accordingly (e.g. -p 80:8080).
The internal haproxy HTTPS listening port. Allows changing the internal port to a non-privileged one (default: 443). Do not forget to adjust the Docker port mapping accordingly (e.g. -p 443:8443).
The hostname or IP-address of the internal service for which SSL terminiation should be provided (required).
The port of the internal service for which SSL terminiation should be provided (default: 80).
Optional directives for communicating with the internal service. For example ssl must be specified here, if the internal service uses HTTPS. Check the haproxy documentation for more directives.
The haproxy-acme-tlsalpn01 image is a ready-to-run image for local SSL termination and has the following core features:
- Issues a SSL certificate on startup
- Configurable ACME provider (
Let's Encrypt,ZeroSSL, ...) - Configurable key length (
2048,4096,ec-256, ...) - Supports SAN certificates
- Configurable ACME provider (
- Automatic certificate renewal
- HAProxy Hitless Reload (zero downtime)
- Configurable certificate renewal notifications (WiP)
- Support for zero downtime TLS-ALPN authentication
docker run -d --name haproxy-acme-tlsalpn01 \
-e "HAPROXY_HTTP_PORT=80" \
-e "HAPROXY_HTTPS_PORT=443" \
-e "HAPROXY_HTTPS_REASSIGN_PORT=8443" \
-e "[email protected]" \
-e "ACME_DOMAIN=domain.com" \
-e "ACME_TLSALPN_PORT=10443" \
-v /docker_data/acme:/var/lib/acme:rw \
-p 80:80 \
-p 443:443 \
ghcr.io/flobernd/haproxy-acme-tlsalpn01services:
haproxy-acme:
image: ghcr.io/flobernd/haproxy-acme-tlsalpn01:latest
container_name: haproxy-acme-tlsalpn01
restart: unless-stopped
environment:
- "HAPROXY_HTTP_PORT=80"
- "HAPROXY_HTTPS_PORT=443"
- "HAPROXY_HTTPS_REASSIGN_PORT=8443"
- "[email protected]"
- "ACME_DOMAIN=domain.com"
- "ACME_TLSALPN_PORT=10443"
volumes:
- /docker_data/acme:/var/lib/acme:rw
ports:
- 80:80
- 443:443It is strongly recommended to specify an external volume for the /var/lib/acme directory. Most ACME servers enforce a rate limit for issuing and renewing certificates. If you recreate the container without preserving the internal state of acme.sh, a new certificate will also be created each time.
The container creates a default configuration file haproxy.cfg in the /usr/local/etc/haproxy directory.
By mapping the aforementioned path, the primary haproxy.cfg can be freely customized. Alternatively, additional configurations can be placed in the include directory, which are then loaded after the primary configuration in alphabetical order.
As long as the default config has not been modified or overwritten, the SERVER_ADDRESS (required), SERVER_PORT (default 80), HAPROXY_HTTP_PORT (default 80), HAPROXY_HTTPS_PORT (default 443), HAPROXY_HTTPS_REASSIGN_PORT (default 8443) environment variables must be set. Otherwise the container will fail to start.
Important
When overwriting the default configuration, make sure that the stats socket directive is retained. Otherwise, the deployment of certificates will fail.
global
# Allow 'acme.sh' to deploy new certificates without reloading
stats socket /var/lib/haproxy/admin.sock level admin mode 660
Set to 1 in order to enable verbose acme.sh debug output.
Set to 1 in order to automatically update acme.sh to the latest version on container startup. This requires an active internet connection (default: 0).
Set to 1 in order to enable the certificate renewal cronjob (default: 1).
The ACME server to use (default: letsencrypt).
Supported values: letsencrypt, letsencrypt_test, buypass, buypass_test, zerossl, sslcom, google, googletest or an explicit ACME server directory URL like e.g. https://acme-v02.api.letsencrypt.org/directory
See also: https://github.com/acmesh-official/acme.sh/wiki/Server
The mail address for the ACME account registration (required).
The domain to issue the certificate for (required). To issue a multi-domain certificate (SAN), enter additional domains separated by a space character after the primary domain.
For example: sub.domain.com (single), domain1.com domain2.com (SAN)
The desired domain key length (default: ec-256).
Supported values (depending on the ACME server capabilities): 2048, 3072, 4096, 8192, ec-256, ec-384, ec-521.
The port on which the internal stateful acme.sh server should run to handle the acme-tls/1 request (default: 10443).
The internal haproxy HTTP listening port. Allows changing the internal port to a non-privileged one (default: 80). Do not forget to adjust the Docker port mapping accordingly (e.g. -p 80:8080).
The internal haproxy HTTPS listening port. Allows changing the internal port to a non-privileged one (default: 443). Do not forget to adjust the Docker port mapping accordingly (e.g. -p 443:8443).
The internal haproxy port to which the other frontends listening on 443 are reassigned (default: 8443). The frontend listening on 443 instead proxies requests to this frontend or the acme.sh server.
The hostname or IP-address of the internal service for which SSL terminiation should be provided (required).
The port of the internal service for which SSL terminiation should be provided (default: 80).
Optional directives for communicating with the internal service. For example ssl must be specified here, if the internal service uses HTTPS. Check the haproxy documentation for more directives.
The base image haproxy-acme is based on the Docker "Official Image" for haproxy and the acme.sh Bash script. It serves as a generic template, providing some hook points for customization:
This script is executed before haproxy starts. Environment variables exported by this script can be used in the haproxy configuration file.
This script is executed after haproxy started.
This script is executed as root before switching to the haproxy user context.
The default location of the main haproxy configuration file haproxy.cfg and the includes directory.
Docker HAProxy ACME is licensed under the MIT license.