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 sudo privileges 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_domain with an A record pointing to your_server_ip.

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:

  1. Go to Settings > Devices & services and click Add integration.
  2. Search for MQTT and select it.
  3. Enter broker 127.0.0.1, port 1883, user homeassistant and 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.