Docker & DevOps

Self-host Home Assistant in Docker on a Linux VPS

Run the official Home Assistant container on a Linux VPS with host networking and a persistent config volume, then reach the dashboard safely from outside your network.

IntermediateLinux server6 min read

Home Assistant in Docker gives you the full smart-home dashboard on a server you control, with your automations and history staying on your own disk. This guide runs the official container with host networking, keeps the configuration in a directory you can back up, and shows how to reach the web UI from outside your network without exposing it carelessly.

The container build is called Home Assistant Container. It runs the same core as Home Assistant OS, but it has no add-on store — add-ons are installed as separate Docker containers instead. That is a fair trade on a VPS, where you already manage containers.

Before you start

You need:

  • A Linux VPS running Ubuntu 22.04/24.04, Debian 12, AlmaLinux 8–9 or Rocky 8–9, with root or sudo access.
  • Docker Engine and the Docker Compose plugin installed. If they are not, install them from Docker's own repository for your distribution.
  • SSH access to the server. Examples below use 203.0.113.10 and the user deploy.
  • Your timezone in Area/City form, for example Europe/Berlin.
  • TCP port 8123 reachable from wherever you will open the dashboard.

Note: Home Assistant uses host networking, so there is no port mapping to configure. The container binds port 8123 directly on the VPS.

Create the config directory

Home Assistant writes everything — configuration.yaml, the database, custom components, TLS certificates — into /config inside the container. Mount that path to a real directory on the host so the data survives container replacement.

  1. Create the directory and take ownership:
Bash
sudo mkdir -p /opt/homeassistant
sudo chown deploy:deploy /opt/homeassistant
  1. Confirm Docker is available:
Bash
docker --version
docker compose version

You should see a Docker version line and a Compose version line. If docker compose version fails, install the Compose plugin before continuing.

Run the Home Assistant container

The official image is published on GitHub Container Registry. The stable tag tracks the current release.

Bash
docker run -d \
  --name homeassistant \
  --restart=always \
  --privileged \
  --network=host \
  -e TZ=Europe/Berlin \
  -v /opt/homeassistant:/config \
  -v /run/dbus:/run/dbus:ro \
  ghcr.io/home-assistant/home-assistant:stable

What the flags do:

  • --network=host puts the container on the host network stack, which Home Assistant needs for device discovery protocols such as mDNS and SSDP.
  • -v /opt/homeassistant:/config persists your configuration and database.
  • -v /run/dbus:/run/dbus:ro lets the Bluetooth integration talk to the host D-Bus.
  • --restart=always brings the container back after a reboot or crash.
  • -e TZ= sets the timezone used in logs and automations.

Warning: --privileged gives the container full access to host devices. It is convenient for USB Zigbee and Z-Wave radios, but if you do not use any, remove the flag and pass only the devices you need with --device=/dev/ttyUSB0.

Use Docker Compose instead

Compose is easier to update and to keep in version control. Create /opt/homeassistant/compose.yaml:

YAML
services:
  homeassistant:
    container_name: homeassistant
    image: ghcr.io/home-assistant/home-assistant:stable
    restart: unless-stopped
    network_mode: host
    privileged: true
    environment:
      TZ: Europe/Berlin
    volumes:
      - /opt/homeassistant:/config
      - /run/dbus:/run/dbus:ro

Then start it:

Bash
cd /opt/homeassistant
docker compose up -d

Tip: With network_mode: host, any ports: block in the Compose file is ignored. Do not add one and expect it to change anything.

Finish onboarding

Open http://203.0.113.10:8123 in a browser. The first run takes a minute or two while Home Assistant builds its database.

  1. Create the owner account with a strong password. This account is local to your instance.
  2. Name your home and set the location, which drives sunrise and sunset automations.
  3. Choose your unit system and currency, then finish.
  4. Open Settings → System → Logs and confirm there are no repeating errors.

Your files now live in /opt/homeassistant. Open configuration.yaml there when you need to add integrations that are not in the UI.

Add remote access

Do not port-forward 8123 straight to the internet. Home Assistant serves plain HTTP by default, so credentials and session cookies would travel unencrypted.

Pick one of these instead:

  • VPN first. A WireGuard or Tailscale tunnel keeps the dashboard private and needs no public port at all. This is the simplest safe option.
  • Reverse proxy with HTTPS. Put Nginx Proxy Manager or Traefik in front, terminate TLS with a real certificate, and forward to 127.0.0.1:8123.

If you use a reverse proxy, tell Home Assistant to trust it. Add this to configuration.yaml and restart the container:

YAML
http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1

Use the proxy's actual source address. If the proxy runs in a bridge network rather than on the host, that address is the Docker bridge subnet instead. Check the proxy documentation for the exact value.

Warning: A wrong trusted_proxies list either blocks logins or lets clients spoof their IP. Verify the address before restarting.

Back up and update

Take a backup before every upgrade. Two layers are worth having:

  1. A Home Assistant snapshot from Settings → System → Backups, which you can restore from the UI.
  2. A file-level copy of /opt/homeassistant, which survives a broken instance.
Bash
sudo tar czf /root/homeassistant-$(date +%F).tar.gz -C /opt homeassistant

To update, pull the new image and recreate the container:

Bash
cd /opt/homeassistant
docker compose pull
docker compose up -d

Warning: Read the release notes before a major version bump. Integrations and YAML keys are sometimes deprecated, and a bad upgrade can leave the UI unreachable until you fix the config.

Check it works

Run these on the VPS:

Bash
docker ps --filter name=homeassistant
curl -I http://127.0.0.1:8123
docker logs --tail 50 homeassistant

You want the container listed as Up, an HTTP/1.1 200 or 302 response from curl, and a log tail without repeating tracebacks. Then load http://203.0.113.10:8123 from your own machine and log in.

Troubleshooting

  • Browser cannot reach port 8123. The host firewall is blocking it. Check with sudo ss -tlnp | grep 8123 to confirm Home Assistant is listening, then allow the port in ufw, firewalld or your provider's firewall. Do not open it publicly if you plan to use a VPN.
  • Container restarts in a loop. Almost always a YAML error in configuration.yaml. Run docker logs homeassistant and look for the line number; indentation and tabs are the usual culprits.
  • Zigbee or Z-Wave stick not detected. The device node was not passed through. Confirm it exists with ls /dev/ttyUSB*, then add --device=/dev/ttyUSB0 or keep --privileged. USB paths can change after a reboot, so consider a udev rule with a stable symlink.
  • Login redirects forever behind a proxy. trusted_proxies is missing or wrong. Set it to the proxy's real source address and restart the container.
  • Timestamps are wrong in automations. The TZ variable does not match your location. Fix it in the run command or Compose file and recreate the container.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.