Home Assistant is an open-source home automation platform that brings devices and online services together in one dashboard and lets you automate them with triggers, conditions and actions. In this tutorial you will run Home Assistant Container with Docker Compose on Ubuntu 24.04, add a Mosquitto MQTT broker for sensors, write a first automation and publish the web interface over HTTPS with Nginx and Let's Encrypt.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS with at least 2 GB of RAM and 20 GB of disk, for example a CubePath VPS.
- A non-root user with
sudoprivileges and UFW enabled with OpenSSH allowed. - Docker Engine and the Docker Compose plugin installed from Docker's official repository.
- A domain name such as
home.your_domainwith an A record pointing toyour_server_ip.
NoteA server in a data center cannot discover devices on your home network (mDNS, Bluetooth, Zigbee or Z-Wave sticks). On a VPS, Home Assistant is useful for cloud integrations, MQTT devices that connect over the internet or a VPN, and remote dashboards. For local radios, run Home Assistant on hardware at home.
Step 1 - Creating the Docker Compose project
Home Assistant Container is the official image without the Supervisor and add-on store; you manage updates and extra services, such as the MQTT broker, with Docker Compose. Create a project directory:
sudo mkdir -p /opt/homeassistant
cd /opt/homeassistant
Create the Compose file:
sudo nano /opt/homeassistant/compose.yaml
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
volumes:
- ./config:/config
- /etc/localtime:/etc/localtime:ro
environment:
TZ: Europe/Madrid
network_mode: host
restart: unless-stopped
Set TZ to your time zone. Host networking is what the Home Assistant documentation recommends: the container listens directly on port 8123 of the server. The privileged flag and the D-Bus mount from the official example are only needed for USB and Bluetooth devices, so they are left out on a VPS.
Because the container uses the host network, UFW rules apply to it. Port 8123 stays closed to the internet, and Nginx will forward traffic to it in Step 4.
Step 2 - Starting Home Assistant
Pull the image and start the container:
sudo docker compose up -d
The first start creates the configuration in /opt/homeassistant/config and takes a minute or two. Follow the log until the web server is up:
sudo docker compose logs -f homeassistant
Press Ctrl+C when the log stops scrolling, then check that port 8123 is listening:
sudo ss -tlnp | grep 8123
LISTEN 0 128 0.0.0.0:8123 0.0.0.0:* users:(("python3",pid=2345,fd=...))
Do not open the onboarding page yet. Anyone who reaches it first can create the owner account, so finish the HTTPS setup in Step 4 and onboard through your domain.
Step 3 - Adding a Mosquitto MQTT broker
MQTT is the lightweight publish and subscribe protocol that most DIY sensors (ESPHome, Tasmota, Zigbee2MQTT) use. Create the broker's directories and configuration:
sudo mkdir -p /opt/homeassistant/mosquitto/config /opt/homeassistant/mosquitto/data
sudo nano /opt/homeassistant/mosquitto/config/mosquitto.conf
listener 1883
allow_anonymous false
password_file /mosquitto/config/passwd
persistence true
persistence_location /mosquitto/data/
log_dest stdout
Create an MQTT user named homeassistant. The command prompts for the password twice; remember it for Step 4:
sudo docker run --rm -it -v /opt/homeassistant/mosquitto/config:/mosquitto/config \
eclipse-mosquitto:2 mosquitto_passwd -c /mosquitto/config/passwd homeassistant
Add the broker to the Compose file, under services: at the same indentation as homeassistant:
sudo nano /opt/homeassistant/compose.yaml
mosquitto:
container_name: mosquitto
image: eclipse-mosquitto:2
volumes:
- ./mosquitto/config:/mosquitto/config
- ./mosquitto/data:/mosquitto/data
ports:
- "127.0.0.1:1883:1883"
restart: unless-stopped
Publishing the port on 127.0.0.1 makes the broker reachable by Home Assistant, which shares the host network, but not from the internet. Docker port mappings bypass UFW, so binding to localhost is what keeps it private. Start the broker:
sudo docker compose up -d mosquitto
sudo docker compose logs mosquitto
mosquitto | ... mosquitto version 2.0.x starting
mosquitto | ... Opening ipv4 listen socket on port 1883.
mosquitto | ... mosquitto version 2.0.x running
Install the command line clients on the host to test it. Replace your_mqtt_password:
sudo apt install -y mosquitto-clients
mosquitto_sub -h 127.0.0.1 -u homeassistant -P 'your_mqtt_password' -t 'test/#' -v -C 1 &
mosquitto_pub -h 127.0.0.1 -u homeassistant -P 'your_mqtt_password' -t test/hello -m 'it works'
test/hello it works
Step 4 - Publishing Home Assistant over HTTPS with Nginx
Install Nginx and Certbot, and allow HTTP and HTTPS through the firewall:
sudo apt install -y nginx certbot python3-certbot-nginx
sudo ufw allow 'Nginx Full'
Create a server block. Home Assistant's frontend uses a WebSocket connection, so the Upgrade and Connection headers are required:
sudo nano /etc/nginx/sites-available/homeassistant
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name home.your_domain;
location / {
proxy_pass http://127.0.0.1:8123;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
}
Enable the site, test the syntax and reload Nginx:
sudo ln -s /etc/nginx/sites-available/homeassistant /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Home Assistant rejects proxied requests with 400 Bad Request until you tell it to trust the proxy. Open its configuration file:
sudo nano /opt/homeassistant/config/configuration.yaml
Add this block at the end, keeping the existing lines such as default_config: and the automation: !include automations.yaml include:
http:
use_x_forwarded_for: true
trusted_proxies:
- 127.0.0.1
- ::1
Validate the configuration, then restart the container:
sudo docker exec homeassistant python -m homeassistant --script check_config --config /config
sudo docker compose restart homeassistant
Finally, request a certificate. Certbot adds the HTTPS server block and the HTTP to HTTPS redirect:
sudo certbot --nginx -d home.your_domain
Open https://home.your_domain in your browser. You should see the Home Assistant onboarding screen.
Step 5 - Completing onboarding and connecting MQTT
Complete the onboarding wizard: create the owner account with a strong password, set your home location (used for sunrise and sunset triggers), time zone and units, and choose which analytics to share.
Then connect Home Assistant to the broker:
- Go to Settings > Devices & services and click Add integration.
- Search for MQTT and select it.
- Enter broker
127.0.0.1, port1883, userhomeassistantand the MQTT password from Step 3.
Since Home Assistant 2022.12, the MQTT connection can only be configured this way, not in YAML. Sensors, however, can still be defined in YAML. Add a temperature sensor fed by MQTT to configuration.yaml:
sudo nano /opt/homeassistant/config/configuration.yaml
mqtt:
sensor:
- name: "Garage temperature"
unique_id: garage_temperature
state_topic: "home/garage/temperature"
unit_of_measurement: "°C"
device_class: temperature
value_template: "{{ value_json.temperature }}"
Restart Home Assistant and publish a test reading:
sudo docker compose restart homeassistant
mosquitto_pub -h 127.0.0.1 -u homeassistant -P 'your_mqtt_password' \
-t home/garage/temperature -m '{"temperature": 21.5}' -r
The -r flag keeps the message as the retained value on the topic, so Home Assistant receives it even if it subscribes later. In Settings > Devices & services > Entities, search for sensor.garage_temperature; its state is 21.5 °C.
Step 6 - Creating an automation
Automations are built from triggers (when), optional conditions (only if) and actions (do). You can create them in Settings > Automations & scenes with the visual editor, which writes them to automations.yaml. You can also write them yourself:
sudo nano /opt/homeassistant/config/automations.yaml
The default file contains [], an empty list. Replace it with:
- id: garage_too_hot
alias: "Warn when the garage is too hot"
triggers:
- trigger: numeric_state
entity_id: sensor.garage_temperature
above: 30
for:
minutes: 5
actions:
- action: persistent_notification.create
data:
title: "Garage temperature"
message: "The garage is at {{ states('sensor.garage_temperature') }} °C."
This fires when the garage stays above 30 °C for 5 minutes and shows a notification in the Home Assistant sidebar. Reload automations without restarting from Developer tools > YAML > Automations, or restart the container.
Test it by publishing a high value:
mosquitto_pub -h 127.0.0.1 -u homeassistant -P 'your_mqtt_password' \
-t home/garage/temperature -m '{"temperature": 34}' -r
After five minutes the notification appears. To see why an automation did or did not run, open it in Settings > Automations & scenes and click Traces.
Step 7 - Updating and backing up
Update Home Assistant and Mosquitto by pulling new images and recreating the containers. Read the release notes first, as breaking changes are listed there:
cd /opt/homeassistant
sudo docker compose pull
sudo docker compose up -d
All state lives in /opt/homeassistant. A simple backup is a compressed archive taken while Home Assistant is stopped, copied off the server afterwards:
sudo docker compose stop homeassistant
sudo tar -czf /root/homeassistant-$(date +%F).tar.gz -C /opt homeassistant
sudo docker compose start homeassistant
Troubleshooting
The page shows 400: Bad Request through the domain. The http: block with use_x_forwarded_for and trusted_proxies is missing or invalid. Check the log with sudo docker compose logs homeassistant | grep -i proxy.
The page loads but stays on "Unable to connect to Home Assistant". The WebSocket upgrade is not reaching the container. Confirm the map block and the Upgrade and Connection headers in the Nginx site, then run sudo nginx -t && sudo systemctl reload nginx.
Home Assistant does not start after a YAML change. Run the configuration check from Step 4; it prints the file and line of the error.
Mosquitto exits with a permissions error on passwd. The file must be readable by the mosquitto user inside the container. Recreate it with the command from Step 3 and restart the broker.
Conclusion
Home Assistant now runs in Docker on Ubuntu 24.04 behind Nginx with HTTPS, with a private Mosquitto broker, an MQTT sensor and an automation that reacts to it. Next, enable two-factor authentication for your account in your user profile, connect remote devices to the broker over a VPN such as WireGuard instead of exposing port 1883, and explore the integrations available in Settings > Devices & services.
