HowTo: Use ESPHome in a Docker container with HTTPS

The ability to access ESPHome via HTTPS is an important consideration if you’re going to host ESPHome on your own Docker server. If you don’t have an encrypted connection, you can’t use USB flashing. Unfortunately the current Docker container does not include support for encrypted connections.

This brief post explains how I achieved this aim, and I hope it may help others.

I use docker-compose so will provide these on a per-container basis to simplify the explanation.

Encryption Certificates
I am using the certificates generated via HA’s integrated LetsEncrypt addon. There are plenty of guides on this step, so I won’t duplicate them. But, put simply, you’ll need to set it up for wildcards (like *.mydomain.com), and then create an automation to copy the resulting PEMs to your Docker host. You’ll also need to rename the files, to allow the system to automatically pick them up:

privkey.pem  -> mydomain.com.key
fullchain.pem -> mydomain.com.crt

Reverse Proxy
First you need nginx to act as a reverse proxy. It’ll accept encrypted connections from your browser, and then route the decrypted traffic to your target container. Here is a sample docker-compose.yaml:

version: '2'

services:
  nginx-proxy:
    container_name: nginx
    image: jwilder/nginx-proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - /path/to/copied/certificates:/etc/nginx/certs
      - /storage/path/to/vhosts.d:/etc/nginx/vhost.d
    network_mode: host
    environment:
      - DEFAULT_HOST=<Docker fully-qualified Hostname>

You should be able to start nginx via docker-compose up -d which will create the container, and the nginx network.

ESPHome
The docker-compose.yaml is as follows:

version: '3'
services:
  esphome:
    container_name: esphome
    image: esphome/esphome
    volumes:
      - /path/to/esphome/config:/config
      - /etc/localtime:/etc/localtime:ro
    restart: always
    privileged: true
    networks:
      - nginx_default
      
    environment:
      - VIRTUAL_HOST=esphome.mydomain.com #This needs to be set to a unique subdomain that will be your URL.


networks:
  nginx_default:
    name: nginx_default

Make sure you update your DNS (or hosts file) so that your custom URL is resolvable.

Now fire it up via docker-compose up -d and let it install. I found that nginx only seems to pickup new containers on startup so, just to be sure, stop both, start nginx and then ESPHome.

You now should be able to access esphome via the URL you set above. So point your browser at https://esphome.mydomain.com and you should find yourself connected to ESPHome via HTTPS. Awesome!

The plot twist
But there is an issue. All of your tiles will show that the nodes are offline. This is because ESPHome relies on mdns (which is a broadcast/multicast protocol to do name resolution).

Unfortunately this isn’t easily fixed in either container or Docker itself.

mdns Repeater
The easiest solution, requiring the least amount of tweaking of broader Docker, was to install an mdns repeater on the network ESPHome is connected to. First, we need to find the id of the nginx network. From the command prompt, issue a docker network ls and scan down for the nginx_default network. It should look something like:

abcdefghijk nginx_default bridge local

Copy the ID to your clipboard, and then do an ifconfig | grep abcdefghijk

We’re looking for the interface name, which is probably just br-abcdefghijk, but it’s worth confirming. You then also need to find the interface name of your Ethernet adapter.

The docker-compose.yaml:

version: '3'
services:
  mdns-repeater:
    image: monstrenyatko/mdns-repeater
    container_name: mdns-repeater
    restart: unless-stopped
    command: mdns-repeater-app -f <Ethernet Adapter Name> <nginx-adapter name> #eg mdns-repeater-app -f eth0 br-abcdefghijk
    network_mode: "host"

Issue your final docker-compose up -d and you should find that your ESPHome tiles spring to life.

I hope this helps someone who wants to do the same!

4 Likes

Well you can if you plug the usb into your server.

Which server? ESPHome is running on an external Docker host.

When you say external, do you have no physical access?

It’s a server elsewhere in the house.

Too far away to plug it in?

Hey, each to their own.

My post above will take about 15 mins to do for anyone that needs it and, if repeatedly going to the server each time you add an ESP device is irritating, it might be worth doing.

Those who are happy taking the device to their server are in no way obligated to follow the guide.

Just offering a solution to the community in case someone else finds it helpful.

2 Likes

I am sure other people will find it useful! Some people seem to be getting the idea that plugging into your browser computer is the only way.

Thanks a lot @AnotherMatt, I was missing knowledge of the mDNS-Repeater. Deploying that one helped me to solve the issue :rocket:.

If someone whould have issues with:
ifconfig: command not found
then this command worked for me on Debian 11:
ip link show | grep abcdefghijk

Hello @AnotherMatt ,

I’m completely new to Nginx and I’m struggling to get SSL over ESPHome.
I viewed your tutorial and I have some questions about it.
How do you link the nginx_default network (used in the esphome container) with the nginx container ?
Don’t you have a configuration file for Nginx ?
What is the vhosts file you use in your nginx container ?
What is the default host value in your nginx container environment ?

I tried to set up my configuration through your example and with other examples seen on internet but without any success.

Thank you.

That should be the default container created when nginx first starts. If you get something else, adjust ESPHome accordingly.

That particular container is built specifically for the task at hand and manages its own config files. You can change and override as necessary, but the default should work fine.

It’s blank. As above, this container will manage itself.

It’s defined in the docker-compose.yaml above. I have mine set to the FQDN of the docker host.

This container isn’t just a vanilla nginx install. It’s specifically built to dynamically forward proxy containers with an environment variable set. Make sure you don’t take any configs from other installs.

Full documentation for this nginx container can be found here: GitHub - nginx-proxy/nginx-proxy: Automated nginx proxy for Docker containers using docker-gen

Thanks for the write up and mdns repeater tip. Works perfectly with my Traefik proxy; no more ‘offline’ statuses.