{"id":117,"date":"2025-04-18T14:41:12","date_gmt":"2025-04-18T13:41:12","guid":{"rendered":"https:\/\/blog.thebowrings.co.uk\/?p=117"},"modified":"2025-04-18T14:47:09","modified_gmt":"2025-04-18T13:47:09","slug":"haproxy-on-docker","status":"publish","type":"post","link":"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/","title":{"rendered":"HAProxy on Docker"},"content":{"rendered":"\n<nav class=\"wp-block-table-of-contents\"><ol><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#introduction\">Introduction<\/a><ol><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#as-is-lab-state\">As-Is Lab State<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#to-be-lab-state\">To-Be Lab state<\/a><\/li><\/ol><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#target-state-setup\">Target State Setup<\/a><ol><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#linux-vm\">Linux VM<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#install-docker\">Install Docker<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#configuration-file-structure\">Configuration File Structure<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#haproxy-configuration\">HAProxy Configuration<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#temporary-certificate\">Temporary Certificate<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#define-docker-compose\">Define Docker Compose and Start HAProxy<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#create-certificate-using-letsencrypt-certbot\">Create certificate using LetsEncrypt Certbot<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#renew-certificate\">Renew Certificate<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#automated-certificate-renewal\">Automated certificate renewal<\/a><\/li><\/ol><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#useful-docker-commands\">Useful Docker Commands<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#haproxy-config-gotcha-when-using-dns\">HAProxy Config Gotcha when using DNS<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/blog.thebowrings.co.uk\/index.php\/2025\/04\/18\/haproxy-on-docker\/#references\">References<\/a><\/li><\/ol><\/nav>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"introduction\">Introduction<\/h4>\n\n\n\n<p>I have used many other peoples work to put this together, a key reference being the following: <a href=\"https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates\/\">https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates\/<\/a><\/p>\n\n\n\n<p>On my home server I have several applications I want to access outside of the home for example; home-assistant and wordpress.<\/p>\n\n\n\n<p>In order to expose these applications to the internet I had forwarded the ports of these on my router. On my server I have a dedicated VM for each application, the diagram below shows the setup.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"as-is-lab-state\">As-Is Lab State<\/h5>\n\n\n\n<figure class=\"wp-block-image aligncenter size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"288\" src=\"https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-2-300x288.png\" alt=\"\" class=\"wp-image-129\" srcset=\"https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-2-300x288.png 300w, https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-2-768x737.png 768w, https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-2.png 830w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/figure>\n\n\n\n<p><strong>Existing Home-Assistant<\/strong> <strong>Setup<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Using https, so the traffic was encrypted and secure<\/li>\n\n\n\n<li>Using the default port for home-assistant which is 8123, which is annoying as you have to put the port number in the url to access it.<\/li>\n\n\n\n<li>LetsEncrypt plugin on home-assistant to manage the certificates needed for https\n<ul class=\"wp-block-list\">\n<li>LetsEncrypt needs to check the webserver when managing certificates so port 80 was also forwarded to home-assistant.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>Existing WordPress<\/strong> <strong>Setup<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Using http, so the traffic was not encrypted, bad news for keeping usernames and passwords and traffic secure from hackers<\/li>\n\n\n\n<li>Using the default port 8080, same problem with home-assistant for me that you have to put the port number on to reach it.<\/li>\n<\/ul>\n\n\n\n<p><strong>Challenges with my implementation<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Due to the way <strong>letsencrypt <\/strong>works it makes a request to the webserver to confirm its there as part of generating and renewing certificates. Letsencrypt makes this request by default over port 80 .With my setup if i wanted to also have a certificate and https for wordpress, I would need to manually switch the port forwarding on my router between home-assistant or wordpress to get the certificates to be issued and updated on both.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>LetsEncrypt on home-assistant<\/strong> does not automatically renew certificates, you have to start the plugin once at least every 90 days and then restart home-assistant to update the certificate, I could have used an automation in home-assistant to manage this, however I wanted something a bit better.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No <strong>secure access for wordpress<\/strong> using https and wanted to secure it.<\/li>\n<\/ul>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"to-be-lab-state\">To-Be Lab state<\/h5>\n\n\n\n<p>By introducing a reverse proxy I&#8217;m able to put my home-assistant and wordpress behind a single port number and depending on the web URL divert traffic to the correct VM.<\/p>\n\n\n\n<p>I can also then manage the certificates for home-assistant and wordpress on the reverse proxy rather than each application needing to have its own set of certificates.<\/p>\n\n\n\n<p>The diagram below shows roughly what I was aiming for:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"266\" height=\"300\" src=\"https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-3-266x300.png\" alt=\"\" class=\"wp-image-130\" srcset=\"https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-3-266x300.png 266w, https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-3-906x1024.png 906w, https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-3-768x868.png 768w, https:\/\/blog.thebowrings.co.uk\/wp-content\/uploads\/2025\/04\/image-3.png 1116w\" sizes=\"auto, (max-width: 266px) 100vw, 266px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"target-state-setup\">Target State Setup<\/h4>\n\n\n\n<p>Proxmox is the hypervisor that I use to host the VMs &#8211; I&#8217;m not going to go into any detail on proxmox here in this post&#8230;<\/p>\n\n\n\n<p>As part of the improvements here and introducing HAProxy, I wanted to move towards using containers and Proxmox doesn&#8217;t have an out of the box way to support Docker images. For Docker I needed a virtual machine. Once I have Docker on the VM I can then run many different containers on the one VM.<\/p>\n\n\n\n<p>Docker also allows me to easily test different services in containers without needing to provision VMs all the time, reducing the overhead and resources on my home server.<\/p>\n\n\n\n<p>So my setup was going to be like this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Proxmox Server\n<ul class=\"wp-block-list\">\n<li>Ubuntu VM\n<ul class=\"wp-block-list\">\n<li>To Store\n<ul class=\"wp-block-list\">\n<li>Config files and scripts for management<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Running Docker\n<ul class=\"wp-block-list\">\n<li>HAProxy Docker Container (Reserse proxy)<\/li>\n\n\n\n<li>Nginx Docker Container (To handle letsencrypt certificate renewals)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"linux-vm\">Linux VM<\/h5>\n\n\n\n<p>First I setup a VM, I used Ubuntu. Setting up a Ubuntu VM on Proxmox is not part of my post.  <a href=\"https:\/\/www.snel.com\/support\/ubuntu-vm-in-proxmox-and-networking-setup\/\">https:\/\/www.snel.com\/support\/ubuntu-vm-in-proxmox-and-networking-setup\/<\/a> &#8211; This link may help you, or otherwise google it!<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"install-docker\">Install Docker<\/h5>\n\n\n\n<p>Second I installed Docker on the VM. Installing docker is also not part of my post, but the docker documentation here might be useful: <a href=\"https:\/\/docs.docker.com\/engine\/install\/ubuntu\/\">https:\/\/docs.docker.com\/engine\/install\/ubuntu\/<\/a><\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"configuration-file-structure\">Configuration File Structure<\/h5>\n\n\n\n<p>This is a key step making sure that there is a place to define and configure everything needed.<\/p>\n\n\n\n<p>I created a folder in \/home\/&lt;user&gt; called dockerconfig, replace user with the name of the user you are logged in as, for me it was robert, so ill put this in the examples:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\" style=\"padding-top:0;padding-right:var(--wp--preset--spacing--30);padding-bottom:0;padding-left:var(--wp--preset--spacing--30)\"><code>mkdir \/home\/robert\/dockerconfig<\/code><\/pre>\n\n\n\n<p>The structure of the dockerconfig folder was as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\" style=\"border-style:none;border-width:0px;padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code><strong>dockerconfig<\/strong>\n|--certs\n|--haproxy\n|--letsencrypt\n|--webroot\n|--dockercompose.yaml\n|--create-cert.sh\n|--renew-certs.sh<\/code><\/pre>\n\n\n\n<p>Use the mkdir command to create the folders; certs, haproxy, letsencrypt, webroot<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>certs: Where the https certificates will be stored<\/li>\n\n\n\n<li>haproxy: Where the config for haproxy is stored<\/li>\n\n\n\n<li>letsencrypt: Where the letsencrypt certificate files are stored and processed<\/li>\n\n\n\n<li>webroot: Where challenge files from letsencrypt get stored for nginx<\/li>\n<\/ul>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"haproxy-configuration\">HAProxy Configuration<\/h5>\n\n\n\n<p>Next we need to define the HAProxy configuration so that haproxy knows how to route the traffic to the wordpress and home-assistant applications.<\/p>\n\n\n\n<p>Create a new config file:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background\"><code>nano \/home\/robert\/dockerconfig\/haproxy\/haproxy.cfg<\/code><\/pre>\n\n\n\n<p>Use the following content, making sure to change the domain to your actual domain (<span style=\"text-decoration: underline;\">blog.domain, ha.domain<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>global\n  log stdout format raw local0\n  daemon\n\n  # Default ciphers to use on SSL-enabled listening sockets.\n  # For more information, see ciphers(1SSL).\nssl-default-bind-ciphers kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-\n\nresolvers docker_resolver\n    nameserver dns 127.0.0.11:53\n\ndefaults\n  log     global\n  mode    http\n  option  httplog\n  option  dontlognull\n\nfrontend http\n    bind *:80\n    mode http\n\n        # if this is an ACME request to proof the domain ownder, then redirect to nginx-certbot server\n    acl is_well_known path_beg -i \/.well-known\/\n\n        # else redirect the traffic to https\n    redirect scheme https code 301 if !is_well_known !{ ssl_fc }\n    use_backend letsencrypt if is_well_known\n\nfrontend stats\n  bind *:8404\n  stats enable\n  stats uri \/\n  stats refresh 10s\n\nbackend letsencrypt\n    server letsencrypt nginx-certbot:80 resolvers docker_resolver check init-addr none\n\nfrontend https\n    bind *:443 ssl crt \/usr\/local\/etc\/certs\/\n    http-response set-header Strict-Transport-Security \"max-age=16000000; includeSubDomains; preload;\"\n    default_backend mybackend\n\n    acl ACL_blog hdr(host) -i blog.domain\n    use_backend blog if ACL_blog\n\n    # ACL for \"ha\"\n    acl ACL_ha hdr(host) -i ha.domain\n    use_backend home-assistant if ACL_ha\n\nbackend home-assistant\n    server ha 10.0.0.212:8123 ssl verify none\n\nbackend blog\n    server blog 10.0.0.213:80\n<\/code><\/pre>\n\n\n\n<p>To save the file:  Press ctrl + x<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"temporary-certificate\">Temporary Certificate<\/h5>\n\n\n\n<p>This is important to get things going<\/p>\n\n\n\n<p>Making sure you are in the &#8216;dockerconfig&#8217; directory:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>sudo apt-get install openssl\nopenssl req -nodes -x509 -newkey rsa:2048 -keyout test.key -out test.crt -days 30\ncat test.key test.crt &gt; .\/certs\/test.pem<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"define-docker-compose\">Define Docker Compose and Start HAProxy<\/h5>\n\n\n\n<p>Next I needed to create the docker compose file to define the containers to provision and run in docker.<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>nano \/home\/robert\/dockerconfig\/dockercompose.yaml<\/code><\/pre>\n\n\n\n<p>Once the editor is up this is my file example:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>services:\n  proxy:\n    image: haproxy:latest\n    restart: always\n    volumes:\n      - .\/haproxy:\/usr\/local\/etc\/haproxy:ro\n      - .\/certs:\/usr\/local\/etc\/certs:ro\n    ports:\n      - 80:80\n      - 443:443\n      - 8404:8404\n\n  nginx-certbot:\n    image: nginx\n    restart: always\n    container_name: nginx-certbot\n    volumes:\n      - .\/webroot:\/usr\/share\/nginx\/html<\/code><\/pre>\n\n\n\n<p>To save the file: Press ctrl + x<\/p>\n\n\n\n<p>Start the docker containers making sure you are in the \/home\/robert\/dockerconfig directory:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background\"><code>docker-compose up -d<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"create-certificate-using-letsencrypt-certbot\">Create certificate using LetsEncrypt Certbot<\/h5>\n\n\n\n<p>The scripts I use are mostly a lift from: <a href=\"https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates\/\">https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates\/<\/a><\/p>\n\n\n\n<p>I have made a few subtle modifications to suit my needs.<\/p>\n\n\n\n<p>There is a single certificate generated which contains alternative sub domains (blog and ha). I&#8217;ve hard coded these sub domains in the script because it was simplest for my needs. The result of the single cert is any of the sub domains defined in the certificate will also be covered by it, this reduces the need to manage separate certificates.<\/p>\n\n\n\n<p>This script will generate the certificate from LetsEncrypt<\/p>\n\n\n\n<p>Create the script using nano<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>nano \/home\/robert\/dockerconfig\/create-certs.sh<\/code><\/pre>\n\n\n\n<p>Contents of the script:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>#!\/bin\/bash\n\nset -e\n\necho \"Starting create new certificate...\"\nif &#91; \"$#\" -lt 2 ]; then\n    echo \"Usage: ...  &lt;domain&gt; &lt;email&gt; &#91;options]\"\n    exit\nfi\n\nDOMAIN=$1\nEMAIL=$2\nOPTIONS=$3\n\ndocker run --rm \\\n  -v $PWD\/letsencrypt:\/etc\/letsencrypt \\\n  -v $PWD\/webroot:\/webroot \\\n  certbot\/certbot \\\n  certonly --webroot -w \/webroot \\\n  -d $DOMAIN \\\n<strong>  -d blog.$DOMAIN \\\n  -d ha.$DOMAIN \\<\/strong>\n  --email $EMAIL \\\n  --non-interactive \\\n  --agree-tos \\\n  $3\n\n# Merge private key and full chain in one file and add them to haproxy certs folder\nfunction cat-cert() {\n  dir=\".\/letsencrypt\/live\/$1\"\n  cat \"$dir\/privkey.pem\" \"$dir\/fullchain.pem\" &gt; \".\/certs\/$1.pem\"\n}\n\n# Run merge certificate for the requested domain name\ncat-cert $DOMAIN<\/code><\/pre>\n\n\n\n<p>Make sure you save and make the script executable:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>chmod +x create-certs.sh<\/code><\/pre>\n\n\n\n<p>Execute for testing by using the staging parameter, remove this for production use:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>.\/create-cert yourdomain.com youremail.com  --staging<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"renew-certificate\">Renew Certificate<\/h5>\n\n\n\n<p>This script renews an existing LetsEncrypt signed certificate<\/p>\n\n\n\n<p>Create the script by using nano<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>nano \/home\/robert\/dockerconfig\/renew-certs.sh<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>#!\/bin\/bash\n\nset -e\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE&#91;0]}\" )\" &gt;\/dev\/null 2&gt;&amp;1 &amp;&amp; pwd )\"\ncd $DIR\n\necho \"$(date) About to renew certificates\" &gt;&gt; letsencrypt-renew.log\ndocker run \\\n       -i \\\n       --rm \\\n       --name certbot \\\n       -v $PWD\/letsencrypt:\/etc\/letsencrypt \\\n       -v $PWD\/webroot:\/webroot \\\n       certbot\/certbot \\\n       renew -w \/webroot\n\necho \"$(date) Cat certificates\" &gt;&gt; letsencrypt-renew.log\n\nfunction cat-cert() {\n  dir=\".\/letsencrypt\/live\/$1\"\n  cat \"$dir\/privkey.pem\" \"$dir\/fullchain.pem\" &gt; \".\/certs\/$1.pem\"\n}\n\nfor dir in .\/letsencrypt\/live\/*; do\n  if &#91;&#91; \"$dir\" != *\"README\" ]]; then\n    cat-cert $(basename \"$dir\")\n  fi\ndone\n\necho \"$(date) Reload haproxy\" &gt;&gt; letsencrypt-renew.log\n<strong>docker restart dockerconfig-proxy-1<\/strong>\n\necho \"$(date) Done\" &gt;&gt; letsencrypt-renew.log<\/code><\/pre>\n\n\n\n<p>Make sure you save and make the script executable <\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background\"><code>chmod +x renew-certs.sh<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"automated-certificate-renewal\">Automated certificate renewal<\/h5>\n\n\n\n<p>Using cron on the ubuntu VM, once a month the renew certificates script runs,<\/p>\n\n\n\n<p>To start you need to elevate your permissions to root:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>sudo su<\/code><\/pre>\n\n\n\n<p>Then executing this command will add a line to the cron config file to execute the script to renew certifcates:<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background has-small-font-size\"><code>echo \"0 0 1 * * \/home\/robert\/dockerconfig\/renew-certs.sh\" &gt;&gt; \/etc\/crontab<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"useful-docker-commands\">Useful Docker Commands<\/h4>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code><strong>#Run haproxy<\/strong>\nsudo docker run -d \\\n   --name haproxy \\\n   --net mynetwork \\\n   -v $(pwd):\/usr\/local\/etc\/haproxy:ro \\\n   -p 80:80 \\\n   -p 8404:8404 \\\n   haproxytech\/haproxy-alpine:2.4\n\n<strong>#Find all running containers<\/strong>\nsudo docker ps\n#Find all running and stopped containers\nsudo docker ps -a\n\n<strong>#Start containers that are stopped<\/strong>\nsudo docker start $(docker ps -a -q -f status=exited)\n\n<strong>#Auto start the haproxy container<\/strong>\nsudo docker run --restart=always -d haproxytech\/haproxy-alpine:2.4\n\n<strong>#Reload haproxy config<\/strong>\nsudo docker kill -s HUP haproxy<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"haproxy-config-gotcha-when-using-dns\">HAProxy Config Gotcha when using DNS<\/h4>\n\n\n\n<p>References:<\/p>\n\n\n\n<p><a href=\"https:\/\/www.haproxy.com\/documentation\/haproxy-configuration-tutorials\/dns-resolution\">https:\/\/www.haproxy.com\/documentation\/haproxy-configuration-tutorials\/dns-resolution<\/a><\/p>\n\n\n\n<p>The init-addr field supports three settings<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>last<\/li>\n\n\n\n<li>libc<\/li>\n\n\n\n<li>none<\/li>\n<\/ul>\n\n\n\n<p>If the backend server is using a dns name, and haproxy cannot reach it when it starts, it will not startup. This is useless for homelabs when you are testing, so use <strong>init-addr none<\/strong>, in order to get a successful startup.<\/p>\n\n\n\n<pre class=\"wp-block-code has-tertiary-background-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>backend webservers\n  server s1 hostname1.example.com:80 check resolvers mynameservers init-addr none<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"references\">References<\/h4>\n\n\n\n<p><a href=\"https:\/\/www.haproxy.com\/blog\/how-to-run-haproxy-with-docker\">https:\/\/www.haproxy.com\/blog\/how-to-run-haproxy-with-docker<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/stackoverflow.com\/questions\/38221463\/command-for-restarting-all-running-docker-containers\">https:\/\/stackoverflow.com\/questions\/38221463\/command-for-restarting-all-running-docker-containers<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/www.haproxy.com\/blog\/how-to-map-domain-names-to-backend-server-pools-with-haproxy\">https:\/\/www.haproxy.com\/blog\/how-to-map-domain-names-to-backend-server-pools-with-haproxy<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates\">https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates<\/a> -Key link for certificate management<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction I have used many other peoples work to put this together, a key reference being the following: https:\/\/omarghader.github.io\/haproxy-letsencrypt-docker-certbot-certificates\/ On my home server I have several applications I want to access outside of the home for example; home-assistant and wordpress. In order to expose these applications to the internet I had forwarded the ports of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[20,6,19,7,33],"class_list":["post-117","post","type-post","status-publish","format-standard","hentry","category-reverse-proxy","tag-ha","tag-haproxy","tag-https","tag-letsencrypt","tag-proxy"],"_links":{"self":[{"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/117","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/comments?post=117"}],"version-history":[{"count":18,"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/117\/revisions"}],"predecessor-version":[{"id":144,"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/117\/revisions\/144"}],"wp:attachment":[{"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/media?parent=117"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/categories?post=117"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.thebowrings.co.uk\/index.php\/wp-json\/wp\/v2\/tags?post=117"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}