Home Assistant Docker Deploy — Full Guide
Prerequisites
- A Linux box with 4 GB+ RAM and 20 GB+ free disk (RPi 4/5, J4125, or a mini PC)
- Docker Engine 24+ and docker compose v2
- A domain (optional but recommended for remote access)
- ~2 hours of your time (30 min to deploy, 90 min to configure)
Step 1: Install Docker
Option A: Official Install Script (Recommended)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Verify
docker --version
docker compose version
# Optional: allow your user to run Docker without sudo
sudo usermod -aG docker $USER
# Log out and back in to apply
Option B: Yum on RHEL / Rocky Linux
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now docker
Option C: apt on Debian / Ubuntu
# Remove old versions
sudo apt remove -y docker docker.io containerd runc
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now docker
Step 2: Configure Docker Daemon
Edit /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"dns": ["8.8.8.8", "1.1.1.1"]
}
Step 3: Install Nginx (Reverse Proxy)
Nginx Server Block
Create /etc/nginx/sites-available/homeassistant:
upstream homeassistant {
server 127.0.0.1:8123;
keepalive 16;
}
server {
listen 80;
server_name homeassistant.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name homeassistant.example.com;
# Certbot-managed certs
ssl_certificate /etc/letsencrypt/live/homeassistant.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/homeassistant.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
access_log /var/log/nginx/ha.access.log;
error_log /var/log/nginx/ha.error.log warn;
# Large uploads (add-on downloads, large camera streams)
client_max_body_size 100m;
location / {
proxy_pass http://homeassistant;
proxy_http_version 1.1;
# Required HA headers — do not remove
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# WebSockets (required for real-time updates)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Keepalive
proxy_set_header Connection "";
}
}
sudo ln -s /etc/nginx/sites-available/homeassistant /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 4: Configure SSL with Certbot
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d homeassistant.example.com
# Auto-renew
sudo systemctl enable --now certbot.timer
sudo certbot renew --dry-run
Step 5: Install Home Assistant
Create Data Directory
sudo useradd --system --uid 1000 homeassistant 2>/dev/null || true
sudo mkdir -p /opt/hass
sudo chown -R 1000:1000 /opt/hass
Run the Container
docker run -d \
--name homeassistant \
--restart=unless-stopped \
--network=host \
-e TZ=Asia/Shanghai \
-e ALLOW_LIST=0.0.0.0/0 \
-v /opt/hass:/config \
homeassistant/amd64:latest
Verify
Once HA boots, open http://your-host-ip:8123 (or via your domain) and complete the onboarding wizard.
Step 6: Install Common Add-ons
Option A: Add-on Store in UI
- Settings → Add-ons & Integrations (if you installed with Supervisor)
- Settings → Add-ons → Add-on Store (Docker-only setup)
⚠️ Docker-only (HA Container) doesn't include the Supervisor. Add-ons like Mosquitto, Zigbee2MQTT, File Editor are not directly available. See manual add-on setup below.
Option B: Manual Setup via SSH
If you installed with Docker only (no Supervisor), install add-ons manually:
Mosquitto (MQTT broker):
mkdir -p /opt/addons/mosquitto
cd /opt/addons/mosquitto
wget https://github.com/MoEttaz/Mosquitto-Add-on-for-HA/releases/latest/download/docker-compose.yml
sed -i 's|./mosquitto/config:/mosquitto/config:/mosquitto/data|../../../addons/mosquitto/config:/mosquitto/config:ro ../../../addons/mosquitto/data:/mosquitto/data|g' docker-compose.yml
docker compose up -d
docker compose ps
Zigbee2MQTT:
mkdir -p /opt/addons/zigbee2mqtt
cd /opt/addons/zigbee2mqtt
wget https://raw.githubusercontent.com/koenkk/zigbee2mqtt-addon/master/docker-compose.yml
docker compose up -d
Home Assistant Backup (in case Supervisor is missing):
mkdir -p /opt/addons/backup
cd /opt/addons/backup
wget https://raw.githubusercontent.com/home-assistant/addons/master/backup/docker-compose.yml
docker compose up -d
Step 7: Configure Backup
backup.sh
Save as /usr/local/bin/backup-ha.sh:
#!/usr/bin/env bash
# Home Assistant automatic backup script
# Usage: backup-ha.sh [retention_days]
set -euo pipefail
RECORDS=200
BACKUP_DIR="/opt/hass/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
SNAPSHOT_FILE="${BACKUP_DIR}/ha_backup_${TIMESTAMP}.tar.gz"
# Ensure backup dir exists
mkdir -p "$BACKUP_DIR"
# 1. Create HA snapshot via API
echo "Creating HA snapshot..."
TOKEN="$(cat /opt/hass/.ha_token 2>/dev/null || true)"
curl -s -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"auto-backup-'$TIMESTAMP'"}' \
http://127.0.0.1:8123/api/snapshot/create
# 2. Wait for snapshot to complete
sleep 10
# 3. Snapshot data is at /opt/hass/snapshot/
SNAPSHOT_ID=$(ls -t /opt/hass/snapshot/*.tar.gz 2>/dev/null | head -1 | xargs -I{} basename {} | xargs -I{} dirname {})
# 4. Copy latest snapshot to timestamped file
LATEST=$(ls -t /opt/hass/snapshot/*.tar.gz 2>/dev/null | head -1)
if [ -f "$LATEST" ]; then
cp "$LATEST" "$SNAPSHOT_FILE"
echo "Backup saved: $SNAPSHOT_FILE"
fi
# 5. Retention: keep last N snapshots
echo "Pruning old backups..."
find "$BACKUP_DIR" -name "ha_backup_*.tar.gz" -mtime +"${RECORDS}" -delete
# 6. Optional: sync to remote (uncomment to enable)
# rsync -avz "$SNAPSHOT_FILE" user@remote:/backups/ha/
Cron Schedule
Step 8: Verify Everything Works
# HA is up
curl -I http://127.0.0.1:8123/
# Domain resolves
nslookup homeassistant.example.com
# HTTPS works
curl -I https://homeassistant.example.com/
# Cert is valid
openssl s_client -connect homeassistant.example.com:443 -servername homeassistant.example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates
# Webhook works (test with a simple HA webhook)
# POST to /api/webhook/YOUR_WEBHOOK_ID
# Docker container is healthy
docker ps
docker stats homeassistant
Docker vs Container vs Supervised Install — Comparison
| Aspect | HA OS | HA Container | HA Supervised |
|---|---|---|---|
| Base OS | Raspberry Pi OS / J4125 image | Any Linux | Ubuntu / Debian |
| Add-on store | ✅ Built-in | ❌ Manual setup | ✅ Built-in |
| OTA updates | Automatic | Manual docker pull |
Automatic |
| Resource usage | Moderate | Low | High |
| Best for | Home users | Advanced users | Advanced users who want add-ons |
| Backup options | Snapshot + USB export | DIY | Snapshot + remote |
5 Real Failure Modes
Failure 1: Port 8123 Already in Use
Symptom: Error: Bind for 0.0.0.0:8123 failed: port is already allocated.
Root cause: Another HA instance (HAOS leftover, snap, or previous Docker run) is still holding the port.
Fix:
sudo ss -lnt | grep 8123
# Kill the offending process
sudo lsof -i :8123
sudo systemctl stop homeassistant-os # or snap stop homeassistant
Failure 2: Webhooks Not Working Behind Proxy
Symptom: webhook not found in HA logs, camera snapshots broken.
Root cause: Nginx isn't forwarding X-Forwarded-Proto / X-Forwarded-Host correctly.
Fix:
# Add these to your Nginx config
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Failure 3: WebSocket Keeps Dropping
Symptom: UI shows "Connection lost" every few minutes.
Root cause: Nginx's default proxy_read_timeout is 60s; WebSocket connections get killed.
Fix:
location / {
proxy_pass http://homeassistant;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
Failure 4: Permissions Denied on Config Files
Symptom: Permission denied when HA writes to /opt/hass/.
Root cause: Container runs as 1000:1000 inside, but host directory owned by root or wrong UID.
Fix:
Failure 5: SSL Certificate Expiry
Symptom: NET::ERR_CERT_DATE_INVALID on the HA site.
Root cause: Certbot auto-renewal is off or the renewal failed silently.
Fix:
Related Articles
- Home Assistant OS Deploy — the alternative to Docker
- MQTT Setup for HA — Mosquitto + MQTT Discovery
- Zigbee Setup for HA — Zigbee2MQTT pairing
- HA Backup & Restore — the guide to actually using backups