From SQLite to MariaDB on Proxmox — a complete migration guide for serious setups
Category: Installation & configuration · HA 2026.4.2 · Proxmox 9.1 (Trixie)
Tags:mariadbproxmoxrecorderperformancesqlitemigration
If you run Home Assistant with a significant number of integrations and devices, SQLite will eventually let you down. This post documents a complete, production-tested migration to MariaDB — including every pitfall encountered along the way — so you can do it cleanly in one shot.
Before that I was running HA instance on a Win 11 miniPC / Virtual Box, certainly not the best setup. Proxmox is definitely the way to go.
I decided to move forward after reading this post.
My setup
| Official integrations | 35 |
| HACS integrations | 13 |
| Devices | 524 |
| Entities | 3,082 |
Infrastructure: HAOS 15.2 as a VM (UEFI, VirtIO SCSI, 4 cores, 6 GB RAM) on Proxmox 9.1 running on an Intel NUC i3 8109U. SQLite database: 1.9 GB, accumulated over several years.
Why leave SQLite?
SQLite is embedded, single-writer, and relies on WAL for concurrency. Under the continuous write pressure of a busy HA instance (states, events, statistics), it is prone to corruption — especially when the host crashes or the VM is killed without a clean shutdown.
I experienced recently several corruptions: after a SIGSEGV during a HAOS upgrade, after orphaned statistics cleanup. Recovery involved .recover, manually deleting .wal/.shm files, and hoping for the best. MariaDB’s InnoDB engine has a redo log that automatically replays incomplete transactions after a crash — this class of failure simply does not exist.
The performance improvement after migration is dramatic: UI responsiveness, history loading, and dashboard rendering are all noticeably faster.
Architecture choice: official addon vs. dedicated LXC
After evaluating both options, the official MariaDB addon is the right choice for most setups:
- Two-click install, automatic updates
- Included in HAOS snapshots
- Fully configurable via
mariadb_server_args - No extra infrastructure to maintain
A dedicated LXC only makes sense if you plan to set up MariaDB replication to a secondary node.
Pitfalls — read this before starting
innodb_flush_method=O_DIRECT
Commonly recommended for dedicated servers, but incompatible with the overlay filesystem of HAOS Docker containers. Causes:
ERROR 1180 (HY000): Got error 1 "Operation not permitted" during COMMIT
Do not use it. It is intentionally absent from the configuration below.
mysqldump for import
A SQL dump wraps large tables (statistics, statistics_short_term) in a single LOCK TABLES / INSERT transaction. InnoDB cannot absorb it inside the HAOS container. Same ERROR 1180. Use sqlite3mysql directly instead.
Running sqlite3mysql from a remote machine
If you run the migration tool from a PC connecting over the LAN to the addon’s exposed port 3306, throughput is ~165 rows/sec. With 8M rows, that’s 13+ hours. Run it from a temporary Debian LXC on the same Proxmox host instead — throughput jumps to ~2300 rows/sec via the virtual bridge.
statistics table behavior
sqlite3mysql loads each chunk into RAM before inserting. The progress bar exists but appears frozen for the first few minutes — this is normal. The first chunks are slow while InnoDB warms up its buffer pool. Use --chunk 50000 for the statistics pass — it is the right balance between memory usage and speed on a 4 GB LXC.
What you need
- HAOS running on Proxmox (any recent version)
- A temporary Debian 12 LXC on the same Proxmox host — 4 GB RAM, 1 vCPU, 10 GB disk
- MariaDB addon installed (not started yet) with the configuration below
LXC post-install
apt update && apt install -y python3-pip python3-dev \
default-libmysqlclient-dev default-mysql-client \
build-essential sqlite3
pip3 install sqlite3-to-mysql --break-system-packages
Addon configuration
databases:
- homeassistant
logins:
- username: homeassistant
password: YOUR_PASSWORD
rights:
- database: homeassistant
username: homeassistant
mariadb_server_args:
- "--innodb_buffer_pool_size=512M"
- "--innodb_log_file_size=512M"
- "--innodb_log_buffer_size=32M"
- "--innodb_flush_log_at_trx_commit=2"
- "--innodb_io_capacity=2000"
- "--innodb_io_capacity_max=4000"
- "--innodb_read_io_threads=4"
- "--innodb_write_io_threads=4"
- "--innodb_file_per_table=1"
- "--innodb_stats_persistent=1"
- "--innodb_adaptive_hash_index=1"
- "--innodb_use_native_aio=0"
- "--sort_buffer_size=2M"
- "--read_buffer_size=1M"
- "--join_buffer_size=1M"
- "--tmp_table_size=64M"
- "--max_heap_table_size=64M"
- "--max_connections=50"
- "--table_open_cache=1000"
- "--skip-log-bin"
- "--slow_query_log=1"
- "--long_query_time=2"
- "--skip-name-resolve"
- "--performance_schema=ON"
![]()
innodb_use_native_aio=0is mandatory — native AIO is incompatible with the HAOS container filesystem.
Migration procedure
Total downtime (HA core stopped): approximately 1h30.
| Phase | Description | Duration | HA state |
|---|---|---|---|
| 0 | Create temporary Debian 12 LXC and install dependencies | 15 min | Running |
| 1 | Configure MariaDB addon (do not start yet) | 5 min | Running |
| 2 | Take a Proxmox snapshot of the HAOS VM | 2 min | Running |
| 3 | Start MariaDB addon, then stop HA core | 2 min | Transitioning |
| 4 | Copy SQLite from HAOS to the LXC | 2 min | Stopped |
| 5A | sqlite3mysql — all tables except statistics | ~20 min | Stopped |
| 5B | sqlite3mysql — statistics only, with --chunk 50000 | ~55 min | Stopped |
| 6 | Verify row counts match between SQLite and MariaDB | 5 min | Stopped |
| 7 | Update configuration.yaml and restart HA core | 5 min | Restarting |
| 8 | Validate logs, enable Start on boot, delete LXC | 10 min | Running |
Phase 3 — critical order
Start the MariaDB addon before stopping HA core. Once the core is stopped, the HAOS web UI is gone — you can only interact via the SSH addon.
Phase 4 — copy SQLite from the LXC
scp root@YOUR_HAOS_IP:/config/home-assistant_v2.db /root/
Phase 5A — all tables except statistics
sqlite3mysql \
--sqlite-file /root/home-assistant_v2.db \
--mysql-user homeassistant \
--mysql-password YOUR_PASSWORD \
--mysql-database homeassistant \
--mysql-host YOUR_HAOS_IP \
--sqlite-tables event_data event_types events migration_changes \
recorder_runs schema_changes state_attributes states \
states_meta statistics_meta statistics_runs statistics_short_term
Phase 5B — statistics table only
sqlite3mysql \
--sqlite-file /root/home-assistant_v2.db \
--mysql-user homeassistant \
--mysql-password YOUR_PASSWORD \
--mysql-database homeassistant \
--mysql-host YOUR_HAOS_IP \
--sqlite-tables statistics \
--chunk 50000
The progress bar exists but appears frozen for the first few minutes — this is normal. Throughput stabilizes around 2300 rows/sec after InnoDB warms up. Monitor LXC RAM in Proxmox — usage stays well within limits on a 4 GB LXC with
--chunk 50000.
Phase 6 — verify counts
mysql -h YOUR_HAOS_IP -u homeassistant -pYOUR_PASSWORD homeassistant \
-e "SELECT COUNT(*) FROM states; SELECT COUNT(*) FROM statistics; SELECT COUNT(*) FROM events;"
sqlite3 /root/home-assistant_v2.db \
"SELECT COUNT(*) FROM states; SELECT COUNT(*) FROM statistics; SELECT COUNT(*) FROM events;"
Production results (1.9 GB database):
| Table | SQLite | MariaDB |
|---|---|---|
| events | 1,325,288 | 1,325,288 ✓ |
| states | 98,012 | 98,012 ✓ |
| statistics | 6,784,081 | 6,784,081 ✓ |
Phase 7 — switch recorder to MariaDB
recorder:
db_url: mysql://homeassistant:YOUR_PASSWORD@core-mariadb/homeassistant?charset=utf8mb4
purge_keep_days: 10 (corrected, was 30)
commit_interval: 30
exclude:
entity_globs:
- sensor.*_rssi
- sensor.*_lqi
- sensor.*_signal_strength
Then from the SSH addon terminal:
ha core start
Expected log output on first start
You will see auto_repairs.schema errors about double precision and utf8mb4_bin columns — this is normal. HA detects the schema differences left by sqlite3mysql and automatically corrects them. Wait a few minutes and they will stop.
Results
The performance improvement is immediately noticeable: UI responsiveness, history queries, and dashboard rendering are all significantly faster. The fundamental reason is that InnoDB’s buffer pool keeps frequently accessed data in RAM (512 MB configured here), while SQLite was reading and writing a 1.9 GB flat file on every transaction.
More importantly: no more corruption. InnoDB’s redo log handles crashes cleanly. The days of manually running .recover on a malformed SQLite file are over.
Questions welcome. If you run into the ERROR 1180 or the frozen progress bar on statistics, check the pitfalls section above — those two issues account for most failed migrations.
Edit : A more straightforward approch is proposed by @Impact [here]. No need to add LXC. (# From SQLite to MariaDB on Proxmox — a complete migration guide for serious setups - #8 by Impact - Installation - Home Assistant Community)