Home Assistant Container: Upgrading Thirteen Releases Across a Four-Hop Ladder

· 2 min read · DevOps iotdockerupgradesqlitetraefikreverse-proxy

The thirteen-version maintenance debt

When operating Home Assistant as a dedicated IoT telemetry and environmental control service inside a standalone Docker container (homeassistant/home-assistant)—aggregating rack thermal sensors, UPS power metrics, and automated alerts—system updates do not come from a one-click dashboard button. Every update requires pulling a new container image, recreating the service, and running database migrations on startup.

Over thirteen months, a production installation had remained pinned to version 2025.9.4. Upstream releases had moved to 2026.10.4. Jumping straight across thirteen major versions is the single fastest way to brick an automation stack:

  1. Irreversible Database Migrations: The recorder component upgraded its SQLite schema from version 50 to 53. If an intermediate migration fails mid-stream, the SQLite file cannot be rolled back without cold backups.
  2. Python Runtime Shifts: The base container migrated from Python 3.13 to 3.14, introducing stricter asynchronous task constraints and deprecating legacy helper functions.
  3. The 2026.8 HTTP Gate Rewrite: In 2026.8, Home Assistant restructured its reverse-proxy security subsystem, migrating network parameters into internal storage files (.storage/http). A failed migration turns external access through reverse proxies into immediate HTTP 400: Bad Request errors.

To eliminate risk, the upgrade was partitioned into a verified 4-hop ladder.

Isolating hazards across four hops

Official guidelines recommend stepping through no more than five to six minor releases per increment. The selected upgrade path isolated each architectural breaking change:

HopTarget ImagePrimary Hazard IsolatedVerification Gate
12025.12.5Recorder SQLite schema migration (v50 → v53)Database schema verified, WAL clean
22026.3.4Python 3.13 → 3.14 runtime & zstd container layersLog inspection: 0 setup failures
32026.8.3http: reverse-proxy trusted_proxies validation & .storage/http transitionReverse proxy headers pass; HTTP 200
42026.10.4Current stable target & final auth cleanupAll 259 baseline entities verified

Before pulling images, a cold snapshot of the /config volume was created with the container stopped, ensuring database consistency.

🔍 docker-compose.yml — Home Assistant container definition (14 lines)
services:
  homeassistant:
    container_name: home-assistant
    image: homeassistant/home-assistant:2026.10.4
    volumes:
      - /volume1/docker/home-assistant/config:/config
      - /etc/localtime:/etc/localtime:ro
    environment:
      - TZ=Asia/Kuala_Lumpur
    network_mode: host
    restart: unless-stopped
    stop_grace_period: 60s

Notice stop_grace_period: 60s: under large SQLite databases, Home Assistant requires adequate time to commit SQLite WAL pages and flush event buffers. The default 10-second Docker timeout frequently causes SIGKILL aborts (exit code 137).

The result

The upgrade proceeded through all four stages without rollback:

🛡️ Production Infrastructure & SRE

Dealing with flaky containers, cloud cost bloat, or unexplained outages?

Real production reliability comes from understanding the kernel and the protocol layers, not just restarting containers. I manage 162 containers across 3 hosts running 24/7 client workloads.

Lee Teong Hoe

Lee Teong Hoe · Mr Hoelee

Full-stack developer & DevOps engineer based in Malaysia. I run my own production infrastructure and build web systems for small businesses — this blog is the living record.

Hiring? Profile & stack · Malaysian SME? Website design & development ↗ · All posts