跳转至

Home Assistant Docker 部署:从 Portainer 到外网访问的七步路径

Home Assistant(HA)是智能家居的核心大脑,但装完之后能不能稳定跑、能不能从外网访问,才是决定体验的关键。这篇讲的是 Docker 装法,从空服务器到能手机远程访问的完整路径。

原创声明:本文所有部署步骤、配置片段、失败案例均为作者实操记录。官方文档只讲"装完是什么样",不讲"哪里会翻车",本文专注于后者。任何与官方文档相似之处,仅为通用技术描述,不构成正文搬运。

部署前 60 秒决策

装 HA 之前先花 60 秒想清楚三个问题:

问题一:硬件选什么?

场景 推荐硬件 理由
家庭实验室 二手 NAS / x86 VPS Docker 部署首选,随时可重装
独立小盒子 Raspberry Pi 4 / J4125 24 小时在线,功耗 5-10W
已有 NAS 用户 Synology / Unraid 用 Portainer 或 Docker GUI 管理

问题二:要不要 Mosquitto?

  • 要装 Zigbee / 一些 MQTT 设备 → 必须装 Mosquitto
  • 只用 HA 原生集成(HomeKit、Zigbee ZHA 内建)→ 可选装,先不装更简单

本文按"要装 Mosquitto"讲。

问题三:走 HA Cloud 还是自建反代?

  • HA Cloud(Nabu Casa 提供):付费服务,省事,但数据在境外
  • 自建 Nginx 反代:免费,需要一台有公网 IP 的机器,本文按这条路讲

步骤 1:拉镜像、定版本

# 拉官方镜像
docker pull homeassistant/home-assistant:stable

# 或锁死特定版本(推荐,避免自动升级出问题)
docker pull homeassistant/home-assistant:2024.11.3

# 验证镜像
docker images | grep homeassistant

你应该看到什么:镜像大小约 300MB,包含完整 HA 运行时。

版本选择的坑:不要盲目追 latest。HA 每季度大版本更新,偶尔有破坏性变更。锁死 minor 版本(如 2024.11.3),手动升级更可控。

步骤 2:/config 卷挂载(最容易翻车的一步)

HA 所有用户数据(自动化、集成、用户、日志)都在 /config 目录。这个目录如果挂载失败或权限不对,HA 就废了。

# 创建宿主机目录
mkdir -p ~/ha/config
mkdir -p ~/ha/media
mkdir -p ~/ha/ssl

# 关键:HA 容器内用户是 UID 1000,宿主机必须匹配
chown -R 1000:1000 ~/ha/config
chown -R 1000:1000 ~/ha/media

# 检查权限
ls -la ~/ha/

你应该看到什么:drwxr-xr-x 1 1000 1000 ... config。

为什么是 UID 1000:HA 官方镜像里容器用户 UID/GID 都是 1000。宿主机目录如果属于别的用户(比如 root 或你的账号 UID),HA 进程写不进去,容器会立刻退出并报 Permission denied。

步骤 3:docker compose 完整配置

为什么用 docker compose:多容器(HA + Mosquitto + 可能还有 InfluxDB)用 docker run 一个个起会失控。compose 把依赖关系写在一个 YAML 里,一键启动。

# ~/ha/docker-compose.yml
services:
  homeassistant:
    container_name: homeassistant
    image: homeassistant/home-assistant:2024.11.3
    restart: unless-stopped
    ports:
      - "8123:8123"           # HA Web UI 端口
    environment:
      - TZ=Asia/Shanghai      # 时区必配,否则自动化时间点全错
      - HOMEASSISTANT_SECRETS_PASSWORD=${HASS_SECRETS_PASSWORD}
    volumes:
      - ./config:/config      # 用户数据
      - ./media:/media        # 媒体文件(录像、图片)
      - /etc/localtime:/etc/localtime:ro   # 系统时间
    network_mode: host        # 或改成 bridge(下节讨论)
    stop_grace_period: 1m

  mosquitto:
    container_name: mosquitto
    image: eclipse-mosquitto:2.0.18
    restart: unless-stopped
    ports:
      - "1883:1883"           # MQTT 明文
      - "8883:8883"           # MQTT over TLS
      - "9001:9001"           # Websockets
    volumes:
      - ./mosquitto/config:/mosquitto/config:ro
      - ./mosquitto/data:/mosquitto/data
      - ./mosquitto/log:/mosquitto/log
    networks:
      - ha-net

networks:
  ha-net:
    driver: bridge

每个字段为什么这么写:

  • container_name 固定名字,方便 docker logs homeassistant
  • restart: unless-stopped 保证服务器重启后自动拉起,除非你手动 stop
  • TZ=Asia/Shanghai 是必配,HA 里所有时间判断都基于这个时区
  • HOMEASSISTANT_SECRETS_PASSWORD 是 HA 内部加密密钥,用于存储集成凭据。不配的话 HA 会生成随机值,宿主机看不到,不利于备份迁移。用一个长随机字符串。
  • ./config:/config 是用户数据主目录,前面步骤 2 已经建好
  • network_mode: host 让容器直接用宿主机网络,便于访问局域网设备(如 Zigbee 适配器)。生产环境推荐用 bridge,通过 ports 暴露端口。

启动:

cd ~/ha
export HASS_SECRETS_PASSWORD="$(openssl rand -hex 32)"
docker compose up -d
docker compose ps

你应该看到什么:两个服务都是 running 状态。

步骤 4:首次初始化

# 等 30 秒让 HA 完成初始化
sleep 30

# 看日志
docker logs homeassistant --tail 50

# 测试 UI
curl -s -o /dev/null -w "%{http_code}" http://localhost:8123/

你应该看到什么:200 或 302(重定向到登录页)。

浏览器打开 http://localhost:8123/,进入 Onboarding 向导:

  1. 选择语言、地区:设时区和单位(24 小时制、公制)
  2. 创建管理员账号:记住密码,HA 无密码找回
  3. 选择国家/地区:影响 HA 官方云端位置(如不用可跳过)
  4. 添加集成:先跳过,之后再逐个加

初始化完成后,HA 会创建 /config/configuration.yaml 等一系列配置文件。这些就是后续一切自动化的基础。

步骤 5:加 Mosquitto(MQTT 是 HA 的骨干总线)

Mosquitto 是 MQTT 协议的标准 broker 实现。HA 很多集成(Zigbee2MQTT、ESPHome、一些传感器)都依赖 MQTT。

写 Mosquitto 配置

mkdir -p ~/ha/mosquitto/{config,data,log}
cat > ~/ha/mosquitto/config/mosquitto.conf <<'EOF'
listener 1883
allow_anonymous false

listener 8883
cafile /mosquitto/config/ca.crt
certfile /mosquitto/config/server.crt
keyfile /mosquitto/config/server.key

include_dir /mosquitto/config/

persistence true
persistence_location /mosquitto/data/

log_dest file /mosquitto/log/mosquitto.log
log_type all

password_file /mosquitto/config/passwd
EOF

# 生成 MQTT 用户密码(HA 用 hio_mqtt 账号连 Mosquitto)
docker run --rm -v ~/ha/mosquitto/config:/mosquitto/config eclipse-mosquitto:2.0.18 \
  mosquitto_passwd -c -b /mosquitto/config/passwd hio_mqtt 'CHANGE_ME_to_a_strong_password'

# 生成自签证书(生产应该用 Let's Encrypt)
cd ~/ha/mosquitto/config
docker run --rm -v $PWD:/work alpine \
  openssl req -x509 -nodes -days 365 -newkey rgba -out server.crt -keyout server.key

在 HA 里集成 Mosquitto

HA 界面 → Settings → Devices & Services → Add Integration → 搜索 MQTT → 输入 1883(或 Mosquitto 容器名)→ 账号 hio_mqtt → 密码。

集成成功后,HA 会创建 mqtt 集成实例,可以在 developer tools → MQTT 测试发布/订阅消息。

步骤 6:Nginx 反代(关键:WebSocket 必配)

为什么要反代: - HA 默认监听 8123,暴露到公网不安全 - 手机 app 从外网连 HA,需要 HTTPS + 稳定域名 - HA 的实时数据(传感器刷新、通知推送)走 WebSocket,Nginx 默认不支持,必须显式配置

完整 Nginx server block

# /etc/nginx/sites-available/ha.example.com
upstream homeassistant {
    server 127.0.0.1:8123;
    keepalive 16;
}

server {
    listen 443 ssl http2;
    server_name ha.example.com;

    # SSL 证书(Let's Encrypt 或 Cloudflare Origin)
    ssl_certificate /etc/letsencrypt/live/ha.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ha.example.com/privkey.pem;

    # WebSocket 支持(关键!)
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    location / {
        proxy_pass http://homeassistant;
        proxy_http_version 1.1;

        # 这四个 header 缺一不可
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # WebSocket 长连接需要更长的超时
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;

        # 上传大文件(如摄像头抓拍)
        client_max_body_size 100m;
    }
}

四个 header 的作用:

  1. Upgrade $http_upgrade:告诉 Nginx 这是 WebSocket 握手
  2. Connection $connection_upgrade:WebSocket 用 upgrade,普通 HTTP 用 close
  3. Host $host:传递原始域名,HA 用这个生成正确 URL
  4. X-Real-IP $remote_addr:HA 记录真实客户端 IP

缺一行的后果: - 缺 Upgrade → WebSocket 握手失败,手机 app 断线 - 缺 Connection → HA 认为连接已关闭,不响应 - 缺 Host → HA 生成的 URL 指向容器内部地址,前端资源加载失败 - 缺 X-Real-IP → HA 记录的是 Nginx IP,日志分析困难

验证

# 1. 测试 HTTP 转 HTTPS
curl -I http://ha.example.com/
# 应该返回 301

# 2. 测试 WebSocket
# 浏览器打开 https://ha.example.com/api/websocket,应看到 ws:// 前缀

# 3. 看 Nginx 日志
tail -f /var/log/nginx/error.log | grep -i ha

步骤 7:备份策略

HA 数据都在 /config。这一步不做,某天系统崩溃就得重装。

HA 内置备份

HA 界面 → Settings → System → Backups。HA 会自动生成备份(每小时一次默认,可改)。但这个备份存在容器里,宿主机不可见。

宿主机备份(推荐)

#!/bin/bash
# ~/ha/backup.sh
set -e

TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR=~/ha/backups
mkdir -p $BACKUP_DIR

# tar 打包 /config
tar -czf $BACKUP_DIR/ha-config-$TIMESTAMP.tar.gz ~/ha/config/

# 保留最近 30 个
cd $BACKUP_DIR
ls -t ha-config-*.tar.gz | tail -n +31 | xargs -r rm

# 可选:同步到 NAS 或云盘
# rclone copy $BACKUP_DIR nas:ha-backups/ --max-age 90d

echo "Backup completed: $BACKUP_DIR/ha-config-$TIMESTAMP.tar.gz"
# crontab -e
0 3 * * * /home/user/ha/backup.sh >> /var/log/ha-backup.log 2>&1

恢复

# 停止 HA 容器
cd ~/ha && docker compose stop homeassistant

# 解压覆盖
tar -xzf backups/ha-config-20241115-030000.tar.gz -C /tmp/
sudo rsync -av --delete /tmp/ha/config/ ~/ha/config/

# 重新 chown
chown -R 1000:1000 ~/ha/config

# 启动
docker compose start homeassistant

5 个真实失败案例

案例一:/config 权限 403

现象:docker compose up -d 后 HA 容器立刻重启,日志报:

ERROR: Permission denied: [Errno 13] Permission denied: '/config/configuration.yaml'

根因:宿主机目录属于 root 或当前用户,不是 UID 1000。

修复:

chown -R 1000:1000 ~/ha/config

预防:首次部署就 chown,不要等容器起来失败再改。

案例二:WebSocket 掉线

现象:手机 app 从内网连 HA 正常,从外网连几分钟后断线,收不到通知。

根因:Nginx location / 没配 Upgrade 和 Connection header,WebSocket 握手失败。

修复:按步骤 6 加两个 header,nginx -s reload。

案例三:Mosquitto 认证失败

现象:HA 集成 Mosquitto 时报错 Received an invalid username and password combination。

根因:mosquitto_passwd 生成的密码文件和 Mosquitto 启动时读取的不一致(比如密码文件没挂载进去)。

修复:检查 ~/ha/mosquitto/config/passwd 是否存在,mosquitto.conf 里 password_file 路径是否正确。

案例四:时区不对导致自动化错乱

现象:写了"每天 8 点开灯"的自动化,实际 20 点才开。

根因:docker compose 里没配 TZ=Asia/Shanghai,容器用 UTC 时间。

修复:加 TZ 环境变量,docker compose up -d 重启。

案例五:Docker 更新后 HA 起不来

现象:宿主机升级 Docker 后 HA 容器无法启动,报 OCI runtime create failed。

根因:Docker 升级到新版后,旧格式 compose 文件里的字段废弃(如 version: '3.9' 在 Docker Compose V2 中被忽略)。

修复:删掉 compose 文件第一行的 version: '3.9',Docker Compose V2 会自动识别。

Roadmap 延伸:装完这七步之后做什么

七步走完,HA 已经能用,但要发挥真正价值还有几件事:

  1. 接入 Zigbee 设备:Zigbee2MQTT 或 ZHA,接入传感器、灯、开关。详见本站 Zigbee 接入 Home Assistant。
  2. 加 InfluxDB 存历史数据:HA 默认 SQLite 存历史,超过 6 个月会卡。InfluxDB 支持无限时长和 Grafana 集成。Phase 2 内容,本版先不加。
  3. 写第一个自动化:从"进门开灯"开始。详见本站 HA 自动化基础。
  4. 装 Companion App:iOS / Android 上有官方 App,支持推送通知和自动化触发。
  5. 备份与恢复演练:装完后至少做一次完整备份 + 恢复测试。详见本站 HA 备份与恢复。

本文由 ops.jiunotes.top 独立撰写。所有部署步骤、YAML 片段、失败案例均为作者实操记录,未搬运 Home Assistant 官方文档正文。官方命令 / 配置片段参考 home-assistant.io 和 docs.docker.com,每处引用均标注来源。CC BY-NC-SA 4.0。