Moving InfluxDB 2 without losing the minutes the backup took

influx backup has no incremental mode, and the backup of a 5 GB database takes 18 minutes while Home Assistant keeps writing. Restore a second backup into a scratch bucket and copy only the gap across with Flux to().

Home Assistant writes about nine million points a day into an InfluxDB 2 container on an old host. I was moving it into the cluster, and the part that worried me was the gap: a full backup of the 4.7 GB database took 18 minutes, and Home Assistant doesn’t stop writing for that. Whatever arrived after the backup started would be missing on the new side.

(Before any of this, find the real writer. The integration may not be in configuration.yaml at all; see the note on Home Assistant writing to InfluxDB.)

The bulk move

influx backup runs online. I backed up to a directory, streamed it into the new pod over tar through ssh and kubectl exec, onto the volume and not the pod’s temporary storage, and compared SHA-256 sums on both ends. Then:

influx restore --full --token <token> /path/to/backup

--full brings the old organisation, buckets and tokens back, which is why a pod that was set up with the old token kept working afterwards.

Flags go before the path in both commands. The CLI stops parsing flags at the first positional argument, so a flag after the directory is rejected with “restore path must be specified as a single positional argument”.

Checking, and the gap

For comparing two servers I counted points in fixed windows that ended before the switch, so writes to the new side didn’t skew the numbers:

from(bucket: "home") |> range(start: 2026-09-01T00:00:00Z, stop: 2026-09-20T00:00:00Z) |> group() |> count()

Both sides gave the same number. That left the gap between the start of the bulk backup and the moment I pointed Home Assistant at the new server. The v2 CLI has no --start for an incremental backup, so I took a second backup of just that bucket, which took another 18 minutes, and restored it under a new name:

influx restore --bucket home --new-bucket home_gap --token <token> /path/to/second-backup

Then Flux copies the window across. Identical points just overwrite each other, so an overlap does no harm:

from(bucket: "home_gap")
  |> range(start: 2026-10-09T21:00:00Z, stop: 2026-10-09T22:15:00Z)
  |> to(bucket: "home", org: "docs")
  |> filter(fn: (r) => false)

The last line matters. Appending count() after to() fails with a schema collision between float and string columns, and the query only needs the side effect. Afterwards the gap window and the whole day matched on both servers, and I deleted home_gap.

Small things that cost me time

  • Pass a multi-line Flux query through stdin (influx query -). Inlined into nested shell quoting, the quotes silently vanished and I got “undefined identifier home”.
  • last() or keep() after group() hit the same schema collision. Count across all fields, or filter to one field first.
  • The 101 GB directory next to the database wasn’t the database. The real data was a 4.7 GB Docker volume, which survives docker compose down.