Database migration

Your question

I am currently running Stalwart v0.15 with a postgresql database.
I would like to change the database to mysql before upgrading stalwart to v0.16
Is there a script available to manage this database migration ?
Thank you

There is an important nuance here: the built-in Database Import/Export utility described in the current docs is a feature of the current version of Stalwart, not v0.15. Before planning your migration, you should be aware that the upgrade from v0.15 to v0.16 is a major breaking change, and the recommended approach matters significantly.

The key concern with your plan: The Database Import/Export tool (using --export / --import flags on the Stalwart binary) is a feature of the current Stalwart codebase, not v0.15. Migrating the database backend before upgrading (i.e., while still on v0.15) would require using v0.15’s own tools/schema, which may not be compatible with the newer binary’s import format.

The recommended path is likely:

  1. Upgrade from v0.15 to the current version first (following the v0.16 upgrade guide very carefully, as it has multiple breaking changes).
  2. Once on the current version, use the built-in export/import utility to migrate between backends (PostgreSQL to MySQL):
    • Stop Stalwart.
    • Export: stalwart --config /path/to/config.json --export /path/to/export-dir
    • Reconfigure Stalwart to point to the new MySQL backend.
    • Import: stalwart --config /path/to/new-config.json --import /path/to/export-dir
    • Start Stalwart.

There is no external SQL migration script; the migration is handled entirely through the binary’s built-in tool, which operates on a binary dump (not SQL). The v0.16 upgrade notes specifically warn: “do not upgrade a production deployment without first reading the upgrading documentation in full.”

You can read the full migration procedure here: Database Migration docs

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

when trying to migrate to v0.16, I get the following error at the convert stage:
error: storage.blob points at store ‘rocksdb’ of type ‘rocksdb’, but v0.16 does not support a separate rocksdb blob store. Consolidate blob data into the data store, …"
but I do not find how to do it

Can you help me here ?

Thanks

The error is clear from the migration script source code. Your v0.15 setup has storage.blob pointing at a separate RocksDB store that is different from your storage.data store. v0.16 does not support a RocksDB store used exclusively as a blob store – it only works if both storage.data and storage.blob point at the same RocksDB instance (same path).

You have two options to resolve this before the convert step:

Option 1 – Consolidate blobs into the data store (on v0.15, before converting)

In your v0.15 TOML configuration, change storage.blob to point at the same store ID as storage.data. You may need to physically migrate any blob data into the data store first, depending on how your v0.15 is configured. Once storage.blob and storage.data reference the same store, the migration script will emit {"@type": "Default"} for the blob store, which is correct for v0.16.

Option 2 – Switch to a supported blob backend first (on v0.15)

Configure a supported blob backend on your v0.15 instance – filesystem (fs), S3, Azure, PostgreSQL, MySQL, or FoundationDB – migrate the blobs to it, and update storage.blob to point at that new store. Then rerun migrate_v016.py convert. The script supports all of those types for the blob store.

Since you are already on PostgreSQL for your main data store, the simplest path is probably to set storage.blob equal to storage.data (your PostgreSQL store) in v0.15, which means blob data is consolidated into the same PostgreSQL database. Once that is done, the migration script will see storage.blob == storage.data and automatically treat it as Default.

The relevant logic in the script is in the _build_blob_store method: if storage.blob and storage.data point at the same store ID, it returns {"@type": "Default"} with no error. The error only fires when they point at different stores and the blob store is RocksDB or SQLite.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

I am ok for option 2 and colsolidate the storage.blob into the storage.data, but how can this be done? Just by editing the toml configuration file, or is there a migration needed additionaly ?

@helpbot, I am ok for option 2 and consolidate the storage.blob into the storage.data, but I don’t find how this can be done. Can you please provide me detailed instructions on how to do it ?

Thank you

The key question is whether your blob data actually needs to be physically moved, or whether it already lives in PostgreSQL.

First, check your v0.15 config.toml: look at the store definitions for both storage.data and storage.blob.

  • If storage.data is already PostgreSQL, blobs stored in a separate RocksDB store need to be physically migrated before you can just point storage.blob at your PostgreSQL store. Simply editing the TOML to point storage.blob at PostgreSQL would make the server look for blobs in PostgreSQL, but they are still sitting in RocksDB – resulting in data loss (missing email bodies).

The safe procedure for your situation:

Since you are running PostgreSQL as your data store, the cleanest approach is:

  1. Stop Stalwart (required before any export/import operation).

  2. Export your blobs from the RocksDB blob store using the built-in export tool, selecting only the blob family:

    EXPORT_TYPES="blob" /opt/stalwart/bin/stalwart \
      --config /path/to/config.toml \
      --export ~/stalwart-blob-export
    
  3. Edit your v0.15 config.toml to point storage.blob at your PostgreSQL store (the same store ID as storage.data).

  4. Import the blob dump into the PostgreSQL-backed instance:

    /opt/stalwart/bin/stalwart \
      --config /path/to/config.toml \
      --import ~/stalwart-blob-export
    

    Note: the import tool imports all subspaces present in the dump, so since you exported only blob, only blob data will be imported.

  5. Restart Stalwart briefly to verify it works correctly (blobs/emails are accessible).

  6. Stop Stalwart again, then proceed with the migrate_v016.py dump and convert steps. At this point storage.blob and storage.data both reference the same PostgreSQL store, so the script will emit {"@type": "Default"} without error.

The export/import utility docs are here: Database Migration | Stalwart

Important caveat: The export/import tool described in the current docs is a feature of the current codebase. If your v0.15 binary does not have the --export/--import flags, this approach may not be available directly on v0.15. In that case, a community member with direct v0.15 experience should advise on the correct blob migration path for that version.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

0.16 dropped the separate-RocksDB blob store; for KV-based blobs, blob and data must share the same store. The script’s error is protecting you from a 0.16 config that would be invalid.
You can export and then import the blobs into v0.15 using the database utility.

@helpbot, when trying the command
EXPORT_TYPES=blob “blob” /usr/local/bin/stalwart --config /opt/stalwart/etc/config.toml --export /opt/stalwart/export/export-20260521
I get the error
Could not retrieve database schema version.: Store not configured (store.not-configured): causedBy = crates/store/src/dispatch/store.rs:47

The error means the Stalwart binary cannot find the store configuration it needs at startup – most likely because the --config path is wrong, the TOML file has a syntax error, or the store referenced in the config is not reachable.

Two issues with the command you ran:

1. The EXPORT_TYPES syntax is wrong for v0.15

In v0.15, EXPORT_TYPES takes subspace letters, not human-readable names like "blob". The blob-related subspaces in v0.15 are j (Blob Reserved), k (Blob Links), and t (Blobs). The command should be:

EXPORT_TYPES="j,k,t" /usr/local/bin/stalwart \
  --config /opt/stalwart/etc/config.toml \
  --export /opt/stalwart/export/export-20260521

(Also note: EXPORT_TYPES=blob "blob" in your command is malformed – there are two separate blob tokens. The correct form is just EXPORT_TYPES="j,k,t" before the binary.)

2. The “Store not configured” error

The error Store not configured (store.not-configured) means the export tool is trying to initialise a store that isn’t defined in your config. The export reads the config to know which store to open. Make sure:

  • The config path /opt/stalwart/etc/config.toml is correct and readable.
  • Stalwart is fully stopped before running the export (especially important for RocksDB, which holds an exclusive lock while running).

The correct full procedure, per the v0.15 migration docs:

# 1. Stop Stalwart first
systemctl stop stalwart

# 2. Export only blob subspaces from the RocksDB blob store
EXPORT_TYPES="j,k,t" /usr/local/bin/stalwart \
  --config /opt/stalwart/etc/config.toml \
  --export /opt/stalwart/export/export-20260521

# 3. Edit config.toml: point storage.blob at the same store as storage.data (PostgreSQL)

# 4. Import the blob dump into PostgreSQL
/usr/local/bin/stalwart \
  --config /opt/stalwart/etc/config.toml \
  --import /opt/stalwart/export/export-20260521

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

Thank you

I have been able to export and reimport with the 2 commands you provided.

But as I am running stalwart in a docker image, I had first to start a new temporary debian docker image, mount the stalwart data folder into it, download the v0.15.5 binary version of stalwart and run the export command.