Home Assistant with Docker Compose: The Complete 2026 Installation Guide
Step-by-step guide to deploy Home Assistant 2026.9.0 with Docker Compose, including security hardening, backups, reverse proxy setup, and troubleshooting.
Introduction
Home Assistant has evolved from a niche hobbyist project into the de facto standard for local-first home automation. As of September 2026, the latest official release is 2026.9.0, which brings significant improvements to the automation engine and device integrations. Running Home Assistant via Docker Compose remains the most flexible deployment method for homelab operators who want full control over their stack without the overhead of a dedicated virtual machine or the Home Assistant Operating System.
This guide walks you through a production-grade deployment of Home Assistant using Docker Compose. You will learn how to structure your project directories, configure persistent storage, harden the container against common attack vectors, and integrate it with an external reverse proxy for secure remote access. We will also cover backup strategies that actually work and a troubleshooting table for the most frequent pitfalls encountered by self-hosters.
The approach taken here prioritizes reproducibility. Every command is provided in full, and every configuration file is complete and ready to use. You will not find placeholders like "and so on" — if a parameter is required, it is explicitly stated. By the end of this guide, you will have a running instance of Home Assistant 2026.9.0 that survives reboots, container updates, and even partial disk failures.
A note on versions: this guide pins Home Assistant to 2026.9.0 as verified from the official release feed. However, container image tags can become outdated quickly. Always check the official Home Assistant Docker Hub page or GitHub releases before pinning a specific version in your own deployment.
Prerequisites
Before you begin, ensure your host system meets the following minimum requirements. These are typical values; actual resource usage depends on the number of integrations, devices, and add-ons you run.
| Resource | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU | 1 core | 2 cores | ARM64 or AMD64. Raspberry Pi 4 or newer works, but x86_64 is preferred for faster database operations. |
| RAM | 1 GB | 2 GB | Home Assistant Core itself is lightweight, but the SQLite database and Python runtime benefit from extra memory. |
| Storage | 10 GB | 32 GB SSD | Use an SSD. Avoid SD cards for long-term deployments due to write wear. |
| OS | Debian 12, Ubuntu 24.04, or any Linux with kernel 5.15+ | Same | Docker Engine 24.0+ and Docker Compose v2 are mandatory. |
| Network | Static IP or DHCP reservation | Same | Required for stable integration with IoT devices. |
| Software | Docker Engine, Docker Compose plugin | Same | Install via official Docker repositories, not distro packages. |
Verify your user ID and group ID before proceeding. Home Assistant runs as user root inside the container by default, but you may want to map volumes to a non-root host user. Run id -u && id -g on your host and check the official image documentation for the default user (in this image, the default is UID 0). Adjust the user: directive in the Compose file only if you have verified the correct UID/GID.
Step-by-Step Installation
Step 1: Create Project Directory and Environment File
Create a dedicated directory for your Home Assistant deployment. This keeps all configuration, logs, and backups in one place.
mkdir -p ~/home-assistant && cd ~/home-assistant
Now create a .env file in this directory. This file stores all secrets and version pins. Never commit this file to Git. Add it to your .gitignore if you use version control.
cat > .env << 'EOF'
HOME_ASSISTANT_VERSION=2026.9.0
TZ=UTC
PUID=1000
PGID=1000
EOF
Replace TZ with your actual timezone (e.g., Europe/Berlin). Replace PUID and PGID with the output of id -u and id -g from your host. Set the file permissions to prevent other users from reading your configuration:
chmod 600 .env && chmod 700 ~/home-assistant
Step 2: Create Configuration and Media Directories
Home Assistant stores its configuration in /config inside the container. Create the corresponding host directory and one for media files (used by the media browser or local voice assistants).
mkdir -p ~/home-assistant/config ~/home-assistant/media
Step 3: Write the docker-compose.yml File
Create the Compose file with the following content. This is a complete, production-ready configuration.
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:${HOME_ASSISTANT_VERSION:-latest}
container_name: homeassistant
restart: unless-stopped
environment:
- TZ=${TZ}
- PUID=${PUID}
- PGID=${PGID}
volumes:
- ./config:/config
- ./media:/media
- /etc/localtime:/etc/localtime:ro
ports:
- "8123:8123"
devices:
- /dev/ttyUSB0:/dev/ttyUSB0 # Only if you have a Zigbee or Z-Wave USB stick
# For network discovery (mDNS/uPnP) you may need host networking instead of bridge.
# Uncomment the next line if you have devices that are not discovered.
# network_mode: host
# The following lines are commented out because they require specific host setup.
# If you use a custom network for the reverse proxy, uncomment and adjust.
# networks:
# - proxy
# Uncomment if you use a custom network for reverse proxy
# networks:
# proxy:
# external: true
Step 4: Pull the Image and Start the Container
Pull the exact version specified in your .env file and start the container in detached mode.
docker compose up -d
Check the container logs to verify that Home Assistant started correctly:
docker compose logs -f homeassistant
Wait until you see a line similar to Starting Home Assistant and then check that the web interface is reachable at http://localhost:8123.
Step 5: Initial Web Configuration
Open your browser and navigate to http://<your-host-ip>:8123. Follow the onboarding wizard. Create a user account with a strong password. Do not use the same password as your host system. After completing onboarding, Home Assistant will scan your network for devices. This can take 5–10 minutes. You can skip this step and do it later from Settings > Devices & Services.
Step 6: Verify Persistence and Restart
Stop the container and restart it to ensure your configuration is persistent.
docker compose stop && docker compose start
Make a change in the configuration (e.g., change the unit system in Settings > System > General) and restart again. Your change should persist.
Step 7: Create a Backup Script
Home Assistant has a built-in backup feature, but it stores backups inside the container. For offsite backups, create a simple script that copies the entire config directory to a backup location.
cat > backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="$HOME/home-assistant/backups/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -r "$HOME/home-assistant/config" "$BACKUP_DIR/"
cp "$HOME/home-assistant/.env" "$BACKUP_DIR/"
find "$HOME/home-assistant/backups" -type d -mtime +7 -exec rm -rf {} \;
echo "Backup completed to $BACKUP_DIR"
EOF
chmod +x backup.sh
Run this script manually or via cron. For a more robust solution, use the built-in Home Assistant backup feature and upload the resulting .tar files to a network share.
Step 8: Update the Container
When a new version is released, update the .env file with the new version number and run:
docker compose pull && docker compose up -d
Always read the release notes before updating. Home Assistant occasionally introduces breaking changes. The official upgrade guide is available on the Home Assistant website.
Advanced Configuration and Optimization
Reverse Proxy with SSL (Traefik or Caddy)
Expose Home Assistant securely using a reverse proxy. Below is a minimal Traefik configuration. Create a separate docker-compose.yml in ~/traefik for the proxy, and connect Home Assistant to the same external network.
First, create the external network:
docker network create proxy
Then uncomment the networks section in your Home Assistant Compose file and run docker compose up -d to reconnect.
Create ~/traefik/docker-compose.yml:
services:
traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
networks:
- proxy
networks:
proxy:
external: true
Now add labels to your Home Assistant Compose service to enable routing:
labels:
- "traefik.enable=true"
- "traefik.http.routers.ha.rule=Host(`ha.example.com`)"
- "traefik.http.routers.ha.entrypoints=websecure"
- "traefik.http.routers.ha.tls.certresolver=letsencrypt"
- "traefik.http.services.ha.loadbalancer.server.port=8123"
Replace ha.example.com with your domain. Ensure your DNS A record points to your host. Restart both stacks and access Home Assistant via https://ha.example.com.
Optional Hardening
The following directives can improve security but must be adapted to your specific setup. Copying them blindly may break container functionality. Test in a staging environment first.
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
cap_add:
- CHOWN
- SETUID
- SETGID
- NET_BIND_SERVICE
read_only: truemakes the container filesystem read-only. Home Assistant writes to/configwhich is a mounted volume, so this is generally safe. Some integrations may try to write to other locations; usetmpfsfor/tmpand monitor logs for permission errors.cap_drop: ALLremoves all Linux capabilities. You must add back the capabilities Home Assistant needs. The list above is a starting point; you may need to addDAC_OVERRIDEorSYS_ADMINif you use certain hardware integrations.- Do not use
privileged: trueunless absolutely necessary (e.g., for specific USB devices). Instead, pass individual devices via thedevicessection.
Database Optimization
By default, Home Assistant uses SQLite. For large installations (more than 50 devices), consider using PostgreSQL or MariaDB. Add a database service to your Compose file:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: homeassistant
POSTGRES_USER: homeassistant
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "homeassistant"]
interval: 10s
retries: 5
Add POSTGRES_PASSWORD=your_strong_password to your .env file. Then in Home Assistant, go to Settings > System > Storage and select PostgreSQL. Enter the hostname db, port 5432, database homeassistant, and your credentials. Home Assistant will migrate the database automatically.
Troubleshooting Common Issues
| Error | Cause | Solution |
|---|---|---|
Permission denied when writing to /config |
Host directory ownership does not match container user | Run id -u && id -g on your host. Change directory ownership: sudo chown -R $(id -u):$(id -g) ~/home-assistant/config. Verify against the image documentation for the default user. |
| Container restarts in a loop | Corrupted configuration or incompatible version | Check logs: docker compose logs homeassistant. Move the configuration.yaml file aside temporarily: mv config/configuration.yaml config/configuration.yaml.bak and restart. |
| Devices not discovered on the network | mDNS/uPnP blocked by bridge networking | Set network_mode: host in the Compose service. Note that you cannot use port mapping when using host networking. |
Error response from daemon: driver failed programming external connectivity |
Port 8123 already in use | Check with `sudo netstat -tulpn |
| Zigbee USB stick not detected | Device path changed after reboot | Use /dev/serial/by-id/ instead of /dev/ttyUSB0. Run ls /dev/serial/by-id/ to find the correct ID. Update the devices section in Compose. |
Database disk image is malformed |
SQLite database corruption | Stop the container, back up the config directory, and delete the home-assistant_v2.db file. Restart and let Home Assistant recreate it. You will lose automation history. |
| Container starts but web UI is unreachable | Firewall blocking port 8123 | Allow incoming traffic: sudo ufw allow 8123/tcp (if using UFW). For other firewalls, adjust accordingly. |
Conclusion
Deploying Home Assistant 2026.9.0 with Docker Compose gives you a maintainable, portable, and secure home automation platform. The key takeaways are to always pin your image version, keep secrets in a .env file, and test recovery procedures before you need them. The configuration provided in this guide is a solid foundation — extend it with additional services like Node-RED, Zigbee2MQTT, or Frigate as your needs grow.
Remember that the Home Assistant ecosystem evolves rapidly. Regularly check the official release notes and the breaking changes log before updating. Join the community forums and the Discord server for real-world advice from other self-hosters.
FAQ
Q1: Can I run Home Assistant on a Raspberry Pi 4 with Docker?
Yes, the Raspberry Pi 4 (4GB or 8GB model) is sufficient for most setups. Use a 64-bit OS like Raspberry Pi OS Lite (64-bit) or Ubuntu Server for ARM. Ensure you use an SSD via USB 3.0 for storage, not an SD card. The image ghcr.io/home-assistant/home-assistant:2026.9.0 supports linux/arm64.
Q2: How do I migrate from a Home Assistant Core installation (venv) to Docker?
Stop the core service, copy the entire configuration directory (usually /home/homeassistant/.homeassistant) to ~/home-assistant/config, and start the Docker container. The database files are compatible. Do not run both simultaneously, as this will cause database corruption.
Q3: Is it safe to use the latest tag instead of pinning a version?
No. Using latest can introduce breaking changes when you run docker compose pull. Always pin a specific version in your .env file. This makes rollbacks trivial — just change the version number and run docker compose up -d.
Q4: How do I back up Home Assistant automatically?
The built-in backup feature creates .tar files that you can download. For automation, add a cron job that runs curl -X POST http://localhost:8123/api/backups with a long-lived access token. Store the resulting file on a remote NFS share or S3 bucket. Test your backups monthly by restoring to a temporary container.
Q5: Can I expose Home Assistant directly to the internet without a reverse proxy?
Technically yes, but it is not recommended. Home Assistant has built-in authentication, but a reverse proxy adds TLS termination, rate limiting, and security headers. If you bypass the proxy, at minimum enable SSL in Home Assistant using a Let's Encrypt certificate and configure trusted_proxies in http: section of configuration.yaml.