Home Assistant Container lets you run your smart-home controller alongside other services on a Linux server. Your configuration lives outside the replaceable container, while Home Assistant connects to supported devices through integrations. This guide uses one consistent installation path and explains how to check discovery, protect configuration and recover from an update.

Choose the Right Installation Method

Container is a standalone installation: you manage Linux, Docker, updates and companion services yourself. It does not include the Supervisor or the app/add-on store available in Home Assistant OS. A separate MQTT broker is another deployment to maintain, rather than an add-on you can install from this container's interface. Built-in integrations remain available, subject to their hardware and network requirements.

The official Linux guide requires Docker Engine 23.0.0 or later and explicitly says Docker Desktop will not work for this method. Use a supported Linux host with Docker Engine. For Windows or macOS, follow the appropriate Home Assistant OS virtual-machine guide instead of translating this recipe to Docker Desktop. Our Home Assistant OS directly on a Raspberry Pi guide offers another option; see the Raspberry Pi articles for related hardware.

Prepare the Linux Host

Install Docker Engine and the Compose plugin using the instructions for your distribution. Distribution packages can differ from Docker's packages, so check versions rather than assuming a package name provides the required runtime. The Docker website and our in-depth article on Docker explain the container model.

Choose a host that stays powered on, has a stable LAN address and has free storage for configuration, backups and the recorder database. Put it on a network from which it can reach your devices. A router DHCP reservation is useful because a changing address can break bookmarks or callbacks. Resolve any existing service using port 8123 before starting.

Create a dedicated directory before mounting it. These commands use a fresh installation under /srv/homeassistant; do not point them at an unrelated directory. Commands use sudo consistently rather than assuming your account already has Docker access.

sudo install -d -m 0750 /srv/homeassistant/config
cd /srv/homeassistant
sudo docker version
sudo docker compose version

Create the Container Configuration

Save this as /srv/homeassistant/compose.yaml. The example timezone is Africa/Johannesburg; replace it with the IANA timezone for your home if different. The image uses Home Assistant's official registry. The stable tag follows stable releases, so record the installed version and choose a deliberate release tag when you need reproducible maintenance.

services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    restart: unless-stopped
    privileged: true
    network_mode: host
    environment:
      TZ: Africa/Johannesburg
    volumes:
      - /srv/homeassistant/config:/config

This follows the official privileged-container example. Privileged mode grants broad host access, so use a trusted image and consider a dedicated host or VM when isolating other workloads matters. Do not copy that permission into every companion container. Bluetooth additionally needs the documented D-Bus mount and host setup; USB radios need the correct device and integration configuration. Identify the actual adapter before granting access, rather than copying someone else's serial-device path.

Host networking uses the Linux host's network stack. There is no port mapping or custom bridge network in this recipe: Home Assistant listens on the host's port 8123. This can simplify discovery, but does not bridge VLANs or bypass router rules. Some manually configured integrations work across routed networks; multicast discovery depends on the protocol and network configuration.

cd /srv/homeassistant
sudo docker compose config --quiet
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs --tail=100 homeassistant

Complete Setup and Verify Real Devices

Open http://LINUX_HOST_IP:8123 from your LAN, substituting the actual host address. Complete onboarding and create your own account. Initial startup can take time; logs distinguish initialization from repeated crashes. A working welcome page proves the interface responds, rather than proving every device is reachable.

  1. Add one known device and compare its reported state with the physical device.
  2. Operate it manually and confirm the result before creating an automation.
  3. Trigger a simple automation under controlled conditions and inspect its trace if the result differs from the expected action.
  4. Restart the container during a planned test, then confirm the same account, integration and automation remain available.
  5. Test unavailable-device behavior so a disconnected sensor does not silently produce a misleading routine.

If discovery is empty, check whether the device and controller share the required network, then consult that integration's requirements. For Bluetooth or USB failures, check host visibility and permissions before changing application settings. Avoid deleting configuration as a first troubleshooting step; it can discard logs and working integrations.

For a useful automation test, write down the expected trigger, conditions and action. A motion sensor may report correctly while a time condition prevents a light from turning on. A failed automation does not automatically imply a broken container. Test one component at a time, then repeat the whole routine with the device in its normal location and the phone disconnected from the setup session.

Back Up Before Updating

Use the backup integration, retain its recovery information, and download a copy to another machine. Include companion services in a separate recovery plan. A backup on the controller's disk will not help if that disk fails. Check that the saved file is readable and practise restoring a disposable installation before trusting it.

Follow the official update procedure and read release notes for your integrations. Pull the replacement image before recreating the service, review startup logs and repeat device checks. A newer release may migrate stored data; selecting an older image alone is not a guaranteed rollback. Keep a compatible pre-update backup and previous version information.

The bind mount is the persistence boundary. Recreating the container should retain files under /config because those files live in the host directory. Deleting that directory, mounting a different empty directory or changing its permissions can still make a working installation appear to have lost its configuration. Check the mounted source path before accepting an unexpected onboarding screen after an update.

Keep the Controller Useful Over Time

After maintenance, check recorder growth, free space and failed automations. Keep configuration secrets out of public repositories and limit accounts to people who need them. For remote use, choose a supported access method with authentication and HTTPS; forwarding the plain HTTP port alone is not a complete solution. The Home Assistant project provides integration-specific guidance.

Maintain a small recovery note listing the host address, configuration directory, installed version, radio model and backup location. Keep secret values in protected storage rather than in that note. If an integration relies on a cloud account, record that dependency too. Local hosting does not make every connected device local-only, and an internet outage may affect some integrations while others continue working.

Finally, revisit automations when household routines change. Remove obsolete entities and label devices clearly so a future repair is understandable. A controller that starts reliably, has a tested recovery path and explains its failures is more valuable than an installation with dozens of untested integrations.