Skip to main content

Setup custom certificates

You can use your own certificates with EasyHAProxy instead of (or in addition to) automatic ACME/Certbot certificates.

How SSL Termination Works

SSL termination happens at the HAProxy level, NOT in your backend containers.

  • Your backend containers should only expose HTTP (port 80), not HTTPS
  • HAProxy handles all SSL/TLS encryption and decryption
  • Backend containers receive plain HTTP traffic from HAProxy
  • Do NOT configure SSL in your backend application when using EasyHAProxy

This is the correct design - it centralizes SSL management at the proxy layer.

Certificate Types

EasyHAProxy supports two certificate sources:

  • ACME/Certbot automatic certificates - Issued automatically via Let's Encrypt or other ACME providers (see ACME documentation)
  • Manual/custom certificates - Your own certificates loaded via volume mount (recommended) or labels (this page)

Both can be used simultaneously. Per domain, ACME certificates (if certbot=true label is set) take precedence over manual certificates.

There are two ways to provide custom certificates:

Map the certificate as a docker volume​

EasyHAProxy stores the certificates inside the container folder /etc/easyhaproxy/certs/haproxy.

  1. Run EasyHAProxy with the volume for the certificates:
Create and mount certificate volume
docker volume create certs_haproxy

docker run \
/* other parameters */
-v certs_haproxy:/etc/easyhaproxy/certs/haproxy \
-d byjg/easy-haproxy
  1. Create a single PEM from the certificate and the key.
Combine certificate and key
cat example.com.crt example.com.key > single.pem

cat single.pem

-----BEGIN CERTIFICATE-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+
U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI=
-----END CERTIFICATE-----

-----BEGIN PRIVATE KEY-----
MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
3j4wz8/I5fdsk090j4s5KA==
-----END PRIVATE KEY-----
  1. Copy this certificate to EasyHAProxy volume:
Copy certificate to container
# IMPORTANT: Filename must match the domain!
docker cp single.pem easyhaproxy:/etc/easyhaproxy/certs/haproxy/example.com.pem

EasyHAProxy watches this folder: a .pem added, replaced or removed there reloads HAProxy within the refresh interval, without restarting the container and without dropping connections. Write a new file to a temporary name first and rename it to <domain>.pem, so HAProxy never reads a half-written one.

Important Notes
  • The filename must match the domain name: example.com.pem for domain example.com
  • When using volume-mounted certificates, do NOT use the easyhaproxy.[definition].sslcert label
  • The volume mount method and the label method are mutually exclusive per domain
  • SSL termination happens at HAProxy - your backend containers should only serve HTTP
  1. Configure your backend container (no sslcert label needed). The HTTPS definition listens on 443 with ssl: true; HAProxy picks the certificate by SNI from the files in /etc/easyhaproxy/certs/haproxy/. The http definition redirects port 80 to it:
services:
easyhaproxy:
image: byjg/easy-haproxy:6.5.3
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- certs_haproxy:/etc/easyhaproxy/certs/haproxy
ports:
- "80:80"
- "443:443"

myapp:
image: nginx
labels:
easyhaproxy.http.host: example.com
easyhaproxy.http.port: 80 # Frontend port (HAProxy listens here)
easyhaproxy.http.localport: 80 # Backend port (your container)
easyhaproxy.http.redirect_ssl: true # Redirect HTTP to HTTPS
easyhaproxy.https.host: example.com
easyhaproxy.https.port: 443
easyhaproxy.https.localport: 80
easyhaproxy.https.ssl: true # TLS on this port, certificate from the volume
# NO sslcert label when using volume method!

volumes:
certs_haproxy:

Setup certificate as a label definition in docker container​

Alternative Method

This method embeds certificates directly in container labels. Use it when you want certificates in version control or don't want to manage external files. Volume method is recommended for most use cases.

  1. Create a single PEM from the certificate and key:
Combine certificate and key
cat example.com.crt example.com.key > single.pem
  1. Convert the single.pem to BASE64 in a single line:
Convert to BASE64
cat single.pem | base64 -w0
  1. Add the Base64 string to your container label. The sslcert label turns the definition's port into a TLS listener, so put it on a definition for port 443; a second definition on port 80 redirects HTTP to it:
services:
myapp:
image: nginx
labels:
easyhaproxy.http.host: example.com
easyhaproxy.http.port: 80
easyhaproxy.http.localport: 80
easyhaproxy.http.redirect_ssl: true
easyhaproxy.https.host: example.com
easyhaproxy.https.port: 443
easyhaproxy.https.localport: 80
easyhaproxy.https.sslcert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t..." # Base64 certificate
warning

Do not put sslcert on a definition with port: 80: that listener would become TLS, so plain HTTP on port 80 stops working and nothing listens on 443.

When Using Label Method
  • There is no necessary to mount the /etc/easyhaproxy/certs/haproxy volume for this domain
  • Using sslcert label means the volume-mounted certificate will be ignored
  • Certificate is visible in docker inspect output (less secure)
  • Updating requires container redeployment

Open source ByJG