Home Assistant in Docker with Nginx and Let's Encrypt on Raspberry Pi
This guide will help to you to setup home assistant as a docker container and make it publicly available behind a ngix proxy, all running in containers. Additionally, we'll use certbot to keep your ssl certificates in sync and Duck DNS so that you access it even with a dynamic ip address.
![]() |
There we go... Dockerized Home Assistant behind Nginx with Let's Encrypt and DuckDNS :) |
This guide is partially based on the generic guide on how to install Ngix and Let's Encrypt by Philipp (Nginx and Let’s Encrypt with Docker in Less Than 5 Minutes | by Philipp | Medium)
In order to start, please make sure you have the following setups done already
- Home Assistant installed as docker container and available under port 8123
- Create a DuckDNS account and register your subdomain
- You know how to redirect a port from your router to the computer running docker
Let's get SSL started!
version: '3'
services:
nginx:
image: arm64v8/nginx
ports:
- "80:80"
volumes:
- ./data/nginx:/etc/nginx/conf.d:ro
- ./data/wwwroot:/var/www/root:ro
- docker-compose.yaml: The file above
- ./data/nginx: The configuration for nginx, basically the file app.conf
- ./data/wwwroot: content of the default bindings (port 80 and 443), like an index.html
Make Nginx available from internet
server {
listen 80;
server_name yourdomain.duckdns.org; #replace this
location / {
root /var/www/root;
}
}
<html>
<body>
<h1>Welcome</h1>
It works!
</html>T
ubuntu@rpiuntu:/home/docker/proxy$ tree
.
├── data
│ ├── nginx
│ │ └── app.conf
│ └── wwwroot
│ └── index.html
└── docker-compose.ymlT
Use docker-compose up to start and see the log in the command line. Exit with Ctrl-C.
![]() |
Using visualping.io to check accessibility from remote |
Switch to SSL
version: '3'
services:
nginx:
image: arm64v8/nginx
ports:
- "80:80"
- "443:443" # added
volumes:
- ./data/nginx:/etc/nginx/conf.d:ro
- ./data/wwwroot:/var/www/root:ro
- ./data/certbot/conf:/etc/letsencrypt:ro # added
- ./data/certbot/www:/var/www/certbot:ro # added
certbot: # added
image: certbot/certbot:arm64v8-latest # added
volumes: # added
- ./data/certbot/conf:/etc/letsencrypt # added
- ./data/certbot/www:/var/www/certbot # added
- ./data/certbot/conf: All the configuration for the certbot
- ./data/certbot/www: Public accessible challenges folder
- Serves the challenges under the challenges folder when asked
- Serves the index.html under port 443 with ssl and the given certificates
server {
listen 80;
server_name yourdomain.duckdns.org; # replace this
location /.well-known/acme-challenge/ { # added
root /var/www/certbot; # added
} # added
location / {
root /var/www/root;
}
}
server {
listen 443 ssl;
server_name yourdomain.duckdns.org;
location / {
root /var/www/root;
}
ssl_certificate /etc/letsencrypt/live/##REPLACE##/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/##REPLACE##/privkey.pem;
#Optional: Only works with Philipp's script (see below)
include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
- Comment out the second server and wait until certificates are downloaded via certibot
- Use the script from Philipp's Article which generates a temporary certificate for the first start
- Download the script
- Edit the script to include your domain and e-mail address
- Run the script
Setup Certificate Renewal
entrypoint: "/bin/sh -c 'trap exit TERM; while :;
do certbot renew; sleep 12h & wait $${!}; done;'"
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!};
nginx -s reload; done & nginx -g \"daemon off;\
version: '3'
services:
nginx:
image: arm64v8/nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./data/nginx:/etc/nginx/conf.d:ro
- ./data/wwwroot:/var/www/root:ro
- ./data/certbot/conf:/etc/letsencrypt:ro
- ./data/certbot/www:/var/www/certbot:ro
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
certbot:
image: certbot/certbot:arm64v8-latest
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
Update DuckDNS Entries automatically
version: '3'
services:
duckdns:
image: lscr.io/linuxserver/duckdns:latest
environment:
- TZ=Asia/Singapore
- SUBDOMAINS=##REPLACE##
- TOKEN=##REPLACE##
- LOG_FILE=true
volumes:
- ./duckdns:/config
restart: unless-stopped
Make Home Assistant Available via Reverse Proxy
nginx and docker
server {
listen 443 ssl;
server_name yourdomain.duckdns.org;
location / {
proxy_pass http://127.0.0.1:8123; #YOUR Address and Port of HA
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
ssl_certificate /etc/letsencrypt/live/##REPLACE##/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/##REPLACE##/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
version: '3'
services:
nginx:
image: arm64v8/nginx
ports:
- "80:80"
- "443:443"
- "8124:8124"
Home Assistant
- Make sure Home Assistant can identify the IP of the client
- Allows the revers proxy to connect to the endpoint
# Loads default set of integrations. Do not remove.
default_config:
# Text to speech
tts:
- platform: google_translate
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
http:
use_x_forwarded_for: true # How to extract client IP address
trusted_proxies: # Permit connections from reverse proxy
- 172.16.0.0/12
Thanks a lot for this great tutorial, it works as a charm!
ReplyDeleteThansk for you feedback, great to hear!
DeleteUnfortunately this didn't work for me... I'm able to create the https test website but when I try the home assistant integration, I get "This site can’t provide a secure connection" in my browser and in the HA logs: from: Logger: aiohttp.server... Any thoughts?
ReplyDeleteNevermind, got it working. I replaced the IP in proxy_pass http://127.0.0.1:8123 with my LAN IP, 192.168.xxx.xxx I also restarted the NGINX container
Delete