Skip to content

v0.18.21.

Complete release notes · 2026-11-05. Read upgrade instructions and runtime limits before deploying.

Added

  • Restore verification for MySQL, MariaDB, MongoDB, ClickHouse, Meilisearch, RabbitMQ, Redis, Valkey, KeyDB and Dragonfly backups (R3). Verify restore and backups drill --restore-query … --expect … now also restore a backup of these engines into an isolated container of the database's own engine image and run your query there.
    • Isolation: the same as PostgreSQL. No network, all capabilities dropped, a read-only root, bounded tmpfs, and the container is removed afterwards.
    • MySQL and MariaDB: the whole --all-databases dump is restored, and the query runs in the app database. The expected output is the client's batch output without headers. Columns are separated by a tab, and NULL prints as NULL.
    • MongoDB: the archive is streamed into mongorestore --stopOnError, so no copy of it lands in the container. The query is a mongosh script, and the expected value is what mongosh prints for its last expression.
    • ClickHouse: the panel's BACKUP archive is restored with RESTORE ALL. The query runs in the app database, and the expected value is clickhouse-client's TabSeparated output.
    • Meilisearch: the dump is imported as the container starts. The query is an API path, and the expected value is the JSON body a GET on it returns.
    • RabbitMQ: the definitions export is imported, and the restore counts as done only once every vhost, queue, exchange and binding it names exists. The query is one rabbitmqctl list_… command; its rows compare in any order. A definitions backup holds no messages.
    • Redis, Valkey, KeyDB and Dragonfly: the server loads the RDB backup as it starts, and an RDB the engine rejects is failed. The query is one command per line, and the expected value is what the command-line client prints.
  • Recovery duration of a restore verification. The Backups tab shows how long the real restore took next to the verdict, for example "restored in 1m 05s". backups drill --restore-query prints it on a pass.
  • Point-in-time recovery for PostgreSQL (R3). It is off by default; an upgrade changes nothing until an admin turns it on for a database (Backups tab, ninedeploy databases pitr enable, PUT /v1/databases/:id/pitr).
    • Archiving: the database restarts once with WAL archiving into a second volume on the panel host. At most a minute of committed work waits in an unarchived segment.
    • Base backups and retention: the panel takes a base backup a day (and on demand) and keeps them for the retention, 1–35 days, default 7.
    • Recovering: recover to any second inside the window, or to the latest archived point, into a new database. The source is never touched. The history records what PostgreSQL replayed to and how long it took.
    • Refusals: a missing or damaged segment, or a target the archive cannot reach yet, is refused with a reason instead of recovering silently to an earlier point.
    • Limits: panel-host databases only. Disabling deletes the archive. Restoring the volumes of a database with it on is refused.

Fixed

  • MongoDB databases start on Linux 6.19 through 7.0.13.
    • Problem: on these kernels MongoDB 8.0 refuses to start ("Linux kernel versions 6.19 and newer has a known incompatibility", SERVER-121912). The cause is the mongo:8 image's own GLIBC_TUNABLES=glibc.pthread.rseq=0, which turns on TCMalloc's per-CPU caches; they crash on these kernels.
    • Fix: MongoDB containers now set glibc.pthread.rseq=1, which starts on every kernel. TCMalloc falls back to per-thread caches, as in MongoDB 7. This applies to databases on the panel host and on nodes.
    • Existing databases: an existing MongoDB database picks this up the next time it is started, with Stop and then Start. Restart keeps the container's old settings.
  • A restore verification whose database engine stopped is unverifiable, not failed. This applies to every engine. Examples are a lost connection to the server, or a helper that is no longer running.
    • MariaDB aborts outright when its scratch space fills up. The helper's own log is now checked for that, so the verdict names the capacity limit.
    • MySQL's "The table … is full" is also recognized as the capacity limit.

Source CHANGELOG.md at 6046cfe8