# How to run Home Assistant Container with Docker on a server

> Run Home Assistant Container with Docker Compose on a server, reach it over WireGuard or Caddy HTTPS with trusted proxies, and back up and update it safely.

Difficulty: Intermediate\
Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12, Debian 13

Home Assistant is an open-source home automation platform with thousands of integrations, a visual automation editor and customisable dashboards. The **Home Assistant Container** installation runs the core application from the official image `ghcr.io/home-assistant/home-assistant` with Docker.

Running it on a rented server instead of a box at home changes what it can do: there are no USB radios and no local network to discover devices on. In return you get a hub that is always online, reachable from anywhere, and that can control your home over a VPN. This guide sets up Home Assistant Container with Docker Compose as documented by the project, onboards it through an SSH tunnel, publishes it over HTTPS with Caddy and the required trusted proxy settings, connects it to your home with WireGuard, and covers backups and updates.

## Prerequisites

- A server running **Ubuntu 24.04 or 26.04 LTS** or **Debian 12 or 13**.
- A non-root user with `sudo` rights: see [Secure a new Linux server](/guides/secure-a-new-linux-server) and [Set up SSH keys](/guides/ssh-keys).
- Docker Engine **23.0.0 or newer** with the Compose plugin, from Docker's repository: [Ubuntu](/guides/install-docker-ubuntu) or [Debian](/guides/install-docker-debian). Home Assistant states that Docker Desktop does not work.
- For HTTPS: Caddy from [How to set up Caddy as a reverse proxy](/guides/caddy-reverse-proxy) and a subdomain such as `ha.example.com` pointing at the server. For access to devices at home: [a WireGuard VPN server](/guides/wireguard-vpn-server).

Home Assistant publishes hardware minimums only for the Home Assistant OS virtual machine (2 GB of RAM and 2 vCPUs), not for Container. The suggested values below are a conservative starting point:

| Resource | Minimum (official) | Suggested starting point |
|---|---|---|
| RAM | Not published for Container (2 GB for the OS virtual machine) | 2 GB |
| CPU | Not published for Container (2 vCPUs for the OS virtual machine) | 2 vCPUs |
| Disk | Not published for Container | 20 GB SSD; the history database and backups grow over time |

### What works on a remote server

| Feature | Home Assistant OS at home | Container on a remote server |
|---|---|---|
| Automations, dashboards, integrations | Yes | Yes |
| Apps (formerly add-ons) and the Supervisor | Yes | No; run extra services as separate containers |
| Backups from the interface | Yes | Yes |
| USB radios (Zigbee, Z-Wave, Thread) and Bluetooth | Yes, with local hardware | No; there is no local hardware |
| Automatic discovery of home devices | Yes, on the home network | No; add devices by IP address over the VPN |

Home Assistant also notes that Thread and Z-Wave are controlled by apps, so Container has no out-of-the-box support for them.

## Step 1 — Create the Compose file

Create a project folder with a `config` subfolder, then the Compose file:

```bash
sudo mkdir -p /opt/homeassistant/config
sudo chown -R $USER:$USER /opt/homeassistant
cd /opt/homeassistant
nano compose.yaml
```

Paste the official example with the config path filled in, and set `TZ` to your time zone:

```yaml
services:
  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /opt/homeassistant/config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    restart: unless-stopped
    stop_grace_period: 60s
    privileged: true
    network_mode: host
    environment:
      TZ: Europe/Amsterdam
```

`network_mode: host` lets Home Assistant use the server's network directly, which many integrations expect. It also means port 8123 is a normal host port, so **ufw does filter it**, unlike ports published by Docker.

> **Note**
>
> The official example runs the container privileged and mounts the D-Bus socket so Home Assistant can use local hardware such as Bluetooth adapters. A rented server has no such hardware. You may remove `privileged: true` and the `/run/dbus` line to reduce the container's privileges; keep them if you want to follow the official example exactly.

## Step 2 — Close the firewall and start Home Assistant

Allow only SSH so that port 8123 stays closed to the internet, then start the container:

```bash
sudo ufw allow OpenSSH
sudo ufw enable
docker compose up -d
docker compose ps
docker compose logs -f homeassistant
```

Wait until the log stops showing setup messages, then press Ctrl+C. Check that Home Assistant answers locally:

```bash
curl -I http://127.0.0.1:8123
sudo ss -tlnp | grep 8123
```

`curl` prints HTTP headers, and `ss` shows Home Assistant listening on all addresses, which ufw now blocks from outside.

## Step 3 — Onboard through an SSH tunnel

The first person to open Home Assistant creates the owner account. Do this through a tunnel. On **your own computer**, run:

```bash
ssh -L 8123:127.0.0.1:8123 admin@203.0.113.10
```

Open `http://localhost:8123`, select **Create my smart home**, and enter a name, a lowercase username and a strong password. Home Assistant warns that the owner credentials cannot be recovered, so store them in a password manager. Then set your home location, which also sets the time zone, unit system and currency, choose what anonymous data to share (sharing is off by default), and select **Finish**.

Turn on multi-factor authentication right away: open your user profile, go to the **Security** tab and enable the authenticator app module.

## Step 4 — Trust Caddy as a reverse proxy

Home Assistant blocks requests from reverse proxies unless you allow them. With host networking, Caddy on the same server connects from `127.0.0.1` (or `::1`). Since version 2026.8 these settings live in the interface, not in `configuration.yaml`:

1. Go to **Settings → System → Network** and find the **HTTP server** section.
2. Turn on **Trust X-Forwarded-For**.
3. Add `127.0.0.1` and `::1` to **Trusted proxies**.
4. Turn on **Enable IP banning** and set **Login attempts before ban** to a low number, such as 5.
5. Save. Home Assistant restarts, and you must **confirm the new settings** afterwards; otherwise it reverts to the previous settings after 5 minutes.

If you upgrade an older installation that still has an `http:` block in `configuration.yaml`, Home Assistant imports it into these settings once and then shows a repair asking you to remove the block.

## Step 5 — Publish Home Assistant over HTTPS with Caddy

Open the web ports and add a site block to `/etc/caddy/Caddyfile`. Caddy proxies WebSocket connections, which the Home Assistant frontend uses, without extra settings:

```caddyfile
ha.example.com {
    reverse_proxy 127.0.0.1:8123
}
```

```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo systemctl reload caddy
curl -I https://ha.example.com
```

You should get an HTTP response with a valid certificate. Under **Settings → System → Network**, set the Home Assistant URL for the internet to `https://ha.example.com`, so links and the companion apps use it.

> **Warning**
>
> A Home Assistant instance on the internet controls real devices in your home. Keep multi-factor authentication on for every account, keep IP banning enabled, and consider using only WireGuard (Step 6) without a public domain at all.

## Step 6 — Reach your home devices over WireGuard

Home Assistant on a server can only control devices it can reach over the network. The usual pattern is a site-to-site VPN: the server runs WireGuard, and a router or small device at home joins as a peer that routes your home subnet, for example `192.168.1.0/24`. Set this up with [How to set up a WireGuard VPN server](/guides/wireguard-vpn-server), adding the home subnet to that peer's allowed IPs.

From the server, check that a home device answers, then add its integration in Home Assistant by IP address:

```bash
ping -c 3 192.168.1.10
```

Automatic discovery (mDNS, SSDP) does not cross a routed VPN, so devices are not found on their own; most integrations let you enter the address manually. You can also use the VPN to open Home Assistant from your phone instead of publishing it with Caddy. Allow the port only on the VPN interface:

```bash
sudo ufw allow in on wg0 to any port 8123 proto tcp
```

## Back up and restore

Everything Home Assistant knows lives in the config folder, `/opt/homeassistant/config`: `configuration.yaml`, the hidden `.storage` folder with dashboards and integrations, and the history database.

**Backups from the interface.** Go to **Settings → System → Backups**, select **Backup now** and then **Manual backup**, or set up automatic backups, which also delete old ones. Download the **emergency kit** and keep it safe; it holds the key you need to restore encrypted backups. On Container, local backups are written to `/opt/homeassistant/config/backups`, inside the folder they protect, so copy them elsewhere or add a network or cloud backup location in the same screen.

**Folder backup.** Stop the container, archive the whole config folder, and start it again:

```bash
sudo mkdir -p /opt/backups
cd /opt/homeassistant
docker compose stop
sudo tar czf /opt/backups/homeassistant-config-$(date +%F).tar.gz -C /opt/homeassistant config
docker compose start
```

Copy `/opt/backups` to another machine with rsync or scp.

**Restore.** In the interface, open **Settings → System → Backups**, select a backup and choose **Restore**. On a fresh installation, the welcome screen lets you upload a backup instead of creating a new home. To restore the folder archive, stop the container, move the current folder aside and unpack the archive:

```bash
cd /opt/homeassistant
docker compose down
sudo mv config config.old
sudo tar xzf /opt/backups/homeassistant-config-2026-10-09.tar.gz -C /opt/homeassistant
docker compose up -d
```

## Update Home Assistant

Read the release notes first, especially the **Backward-incompatible changes** section, and make a backup. Then pull the new image and recreate the container, as the Container documentation describes:

```bash
cd /opt/homeassistant
docker compose pull homeassistant
docker compose up -d
docker image prune
```

Afterwards, check **Settings → System → Repairs** and the logs. The `stable` tag always points to the latest stable release. To control updates, replace it with a specific version tag, for example `2026.10.0`, which also lets you go back to a previous release.

Before restarting after manual YAML changes, validate the configuration:

```bash
docker exec homeassistant python -m homeassistant --script check_config --config /config
```

## Troubleshooting

### 400: Bad Request when you open the domain

Home Assistant does not trust the proxy. Repeat Step 4 and make sure both `127.0.0.1` and `::1` are in the trusted proxies, then check the log with `docker compose logs homeassistant` for a message about a request from a reverse proxy.

### Network settings reverted after a few minutes

After saving the HTTP server settings, Home Assistant restarts and waits for an administrator to confirm them. If nobody confirms within 5 minutes, it restores the previous settings. Save again and confirm.

### Port 8123 cannot be reached from your browser

That is intended: ufw blocks it. Use the SSH tunnel, the VPN, or the Caddy domain. If you expected access over WireGuard, check that the `wg0` rule exists with `sudo ufw status`.

### Devices at home are not discovered

Discovery protocols do not cross the VPN. Check that the server can reach the device with `ping`, then add the integration manually with the device's IP address. If `ping` fails, the home subnet is missing from the WireGuard peer's allowed IPs or the home router does not route it.

### Home Assistant does not start after editing configuration.yaml

A YAML error stops the configuration from loading. Run the `check_config` command from the update section, fix the reported lines, and start the container again with `docker compose up -d`.

## Next steps

- Connect the server to your home network with [a WireGuard VPN server](/guides/wireguard-vpn-server).
- Learn more about HTTPS and site blocks in [How to set up Caddy as a reverse proxy](/guides/caddy-reverse-proxy).
- Add more services as containers with [Docker Compose basics](/guides/docker-compose-basics).
- Compare servers for home automation on the [Home Assistant hosting](/home-assistant-hosting) page.
- Explore integrations in the [Home Assistant documentation](https://www.home-assistant.io/docs/).

## Frequently asked questions

### What is the difference between Home Assistant OS and Home Assistant Container?

Home Assistant OS is a complete operating system with the Supervisor, which manages apps (formerly add-ons) and one-click updates. Home Assistant Container is the same core application in a Docker image: it has no apps, and you update it by pulling a new image.

### Can I use Zigbee or Z-Wave sticks with Home Assistant on a remote server?

Not directly. USB radios and Bluetooth must be plugged into the machine that runs Home Assistant, and a data-centre server has no access to your home. Use network-based devices and integrations, reached over a VPN to your home network.

### Why does Home Assistant return 400 Bad Request behind Caddy?

Home Assistant blocks requests from reverse proxies it does not trust. Open Settings, System, Network, turn on Trust X-Forwarded-For in the HTTP server section and add 127.0.0.1 and ::1 as trusted proxies, then confirm the new settings after the restart.

### Does Home Assistant Container support backups?

Yes. The backup integration creates and restores backups on all installation types. On Container, local backups are stored in the backups folder inside the config directory, so copy them off the server or add a remote backup location.

### Which HyperDC servers can run Home Assistant?

Home Assistant Container runs on a HyperDC Linux VPS, VDS or dedicated server with root access and Docker Engine 23 or newer. It is most useful there as a central hub for network and cloud integrations reached over a VPN.

---

Source: <https://hyperdc.com/guides/tutorials/home-assistant-docker>\
Updated: 2026-10-09
