Skip to content
Stand with Ukraine flag

HAProxy Setup for ThingsBoard

This guide walks you through installing HAProxy with Let’s Encrypt as HTTPS termination for ThingsBoard. Applicable when ThingsBoard is hosted in the cloud with a valid DNS name assigned to the instance.

A server with a valid DNS name. Ports 80 (HTTP) and 443 (HTTPS) must be reachable from the internet.

Ubuntu 20.04, 22.04, or 24.04. No additional firewall configuration is required for most cloud environments.

Terminal window
ssh -i <PRIVATE-KEY> ubuntu@<PUBLIC_DNS_NAME>

Consult your cloud vendor for other SSH connection options.

Terminal window
sudo apt install --no-install-recommends software-properties-common
sudo add-apt-repository ppa:vbernat/haproxy-2.9 -y
sudo apt-get update
sudo apt-get install haproxy=2.9.\* openssl
sudo systemctl enable haproxy
Terminal window
sudo apt-get install ca-certificates certbot

Step 4. Install default self-signed certificate

Section titled “Step 4. Install default self-signed certificate”

Create the certificate generation script:

cat <<EOT | sudo tee /usr/bin/haproxy-default-cert
#!/bin/sh
set -e
HA_PROXY_DIR=/usr/share/tb-haproxy
CERTS_D_DIR=\${HA_PROXY_DIR}/certs.d
TEMP_DIR=/tmp
PASSWORD=\$(openssl rand -base64 32)
SUBJ="/C=US/ST=somewhere/L=someplace/O=haproxy/OU=haproxy/CN=haproxy.selfsigned.invalid"
KEY=\${TEMP_DIR}/haproxy_key.pem
CERT=\${TEMP_DIR}/haproxy_cert.pem
CSR=\${TEMP_DIR}/haproxy.csr
DEFAULT_PEM=\${HA_PROXY_DIR}/default.pem
if [ ! -e \${HA_PROXY_DIR} ]; then
mkdir -p \${HA_PROXY_DIR}
fi
if [ ! -e \${CERTS_D_DIR} ]; then
mkdir -p \${CERTS_D_DIR}
fi
# Check if default.pem has been created
if [ ! -e \${DEFAULT_PEM} ]; then
openssl genrsa -des3 -passout pass:\${PASSWORD} -out \${KEY} 2048 &> /dev/null
sleep 1
openssl req -new -key \${KEY} -passin pass:\${PASSWORD} -out \${CSR} -subj \${SUBJ} &> /dev/null
sleep 1
cp \${KEY} \${KEY}.org &> /dev/null
openssl rsa -in \${KEY}.org -passin pass:\${PASSWORD} -out \${KEY} &> /dev/null
sleep 1
openssl x509 -req -days 3650 -in \${CSR} -signkey \${KEY} -out \${CERT} &> /dev/null
sleep 1
cat \${CERT} \${KEY} > \${DEFAULT_PEM}
echo \${PASSWORD} > \${HA_PROXY_DIR}/password.txt
fi
EOT

Make it executable and run it:

Terminal window
sudo chmod +x /usr/bin/haproxy-default-cert
touch ~/.rnd
sudo haproxy-default-cert

Create the HAProxy configuration file:

Terminal window
cat <<EOT | sudo tee /etc/haproxy/haproxy.cfg
#HA Proxy Config
global
ulimit-n 500000
maxconn 99999
maxpipes 99999
tune.maxaccept 500
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
defaults
log global
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
timeout tunnel 1h # timeout to use with WebSocket and CONNECT
default-server init-addr none
listen stats
bind *:9999
stats enable
stats hide-version
stats uri /stats
stats auth admin:admin@123
frontend http-in
bind *:80 alpn h2,http/1.1
option forwardfor
http-request add-header "X-Forwarded-Proto" "http"
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
redirect scheme https if !letsencrypt_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
use_backend letsencrypt_http if letsencrypt_http_acl
default_backend tb-backend
frontend https_in
bind *:443 ssl crt /usr/share/tb-haproxy/default.pem crt /usr/share/tb-haproxy/certs.d/ ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1
option forwardfor
http-request add-header "X-Forwarded-Proto" "https"
default_backend tb-backend
backend letsencrypt_http
server letsencrypt_http_srv 127.0.0.1:8090
backend tb-backend
balance leastconn
option tcp-check
option log-health-checks
server tb1 127.0.0.1:8080 check inter 5s
http-request set-header X-Forwarded-Port %[dst_port]
EOT

Step 6. Configure Edge TLS communication (Optional)

Section titled “Step 6. Configure Edge TLS communication (Optional)”

ThingsBoard supports securing the gRPC connection between the platform and Edge instances using TLS/SSL, with HAProxy as the TLS termination point.

Redirect the Edge RPC port to 7071 so HAProxy can listen on the default 7070:

Terminal window
sudo sh -c 'cat <<EOL >> /etc/thingsboard/conf/thingsboard.conf
export EDGES_RPC_PORT=7071
EOL'

Restart ThingsBoard to apply:

Terminal window
sudo systemctl restart thingsboard

Append the TLS frontend and backend to the HAProxy configuration:

Terminal window
sudo sh -c 'cat <<EOL >> /etc/haproxy/haproxy.cfg
# Edge gRPC TLS (optional)
listen grpc_front
bind *:7070 ssl crt /usr/share/tb-haproxy/default.pem crt /usr/share/tb-haproxy/certs.d/ ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1
mode tcp
option clitcpka
option tcplog
default_backend grpc_backend
backend grpc_backend
mode tcp
server grpc 127.0.0.1:7071
EOL'

Step 7. Configure Certbot with Let’s Encrypt

Section titled “Step 7. Configure Certbot with Let’s Encrypt”

Set up the Let’s Encrypt directory structure:

Terminal window
sudo mkdir -p /usr/local/etc/letsencrypt \
&& sudo mkdir -p /usr/share/tb-haproxy/letsencrypt \
&& sudo rm -rf /etc/letsencrypt \
&& sudo ln -s /usr/share/tb-haproxy/letsencrypt /etc/letsencrypt

Create the Certbot configuration:

Terminal window
cat <<EOT | sudo tee /usr/local/etc/letsencrypt/cli.ini
authenticator = standalone
agree-tos = True
http-01-port = 8090
non-interactive = True
preferred-challenges = http-01
EOT

Create the HAProxy cert refresh script:

cat <<EOT | sudo tee /usr/bin/haproxy-refresh
#!/bin/sh
HA_PROXY_DIR=/usr/share/tb-haproxy
LE_DIR=/usr/share/tb-haproxy/letsencrypt/live
DOMAINS=\$(ls -I README \${LE_DIR})
# update certs for HA Proxy
for DOMAIN in \${DOMAINS}
do
cat \${LE_DIR}/\${DOMAIN}/fullchain.pem \${LE_DIR}/\${DOMAIN}/privkey.pem > \${HA_PROXY_DIR}/certs.d/\${DOMAIN}.pem
done
# restart haproxy
exec service haproxy restart
EOT

Create Certbot wrapper scripts:

cat <<EOT | sudo tee /usr/bin/certbot-certonly
#!/bin/sh
/usr/bin/certbot certonly -c /usr/local/etc/letsencrypt/cli.ini "\$@"
EOT
cat <<EOT | sudo tee /usr/bin/certbot-renew
#!/bin/sh
/usr/bin/certbot -c /usr/local/etc/letsencrypt/cli.ini renew "\$@"
EOT

Make the scripts executable:

Terminal window
sudo chmod +x /usr/bin/haproxy-refresh /usr/bin/certbot-certonly /usr/bin/certbot-renew

Step 8. Install certificate auto-renewal cron job

Section titled “Step 8. Install certificate auto-renewal cron job”
Terminal window
cat <<EOT | sudo tee /etc/cron.d/certbot
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc. Renewal will only occur if expiration
# is within 30 days.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root test -x /usr/bin/certbot && perl -e 'sleep int(rand(3600))' && certbot -c /usr/local/etc/letsencrypt/cli.ini -q renew && haproxy-refresh
EOT
Terminal window
sudo service haproxy restart

Step 10. Generate certificate using Let’s Encrypt

Section titled “Step 10. Generate certificate using Let’s Encrypt”

Replace your_domain and your_email before running:

Terminal window
sudo certbot-certonly --domain your_domain --email your_email
Terminal window
sudo haproxy-refresh