# qBittorrent Settings Migration Between Versions: How the Upgrade System Works

> Learn how qBittorrent automatically migrates settings between versions. Discover the upgrade system and its incremental transformation steps for a seamless experience.

- Repository: [qBittorrent project/qBittorrent](https://github.com/qbittorrent/qBittorrent)
- Tags: internals
- Published: 2026-05-05

---

**qBittorrent automatically migrates settings between versions by checking a hidden `Meta/MigrationVersion` key at startup and executing incremental transformation steps defined in [`src/app/upgrade.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/upgrade.cpp) before the UI loads.**

qBittorrent stores all user-configurable options in native **QSettings** files ([`qBittorrent.ini`](https://github.com/qbittorrent/qBittorrent/blob/main/qBittorrent.ini) on Windows, [`qBittorrent.conf`](https://github.com/qbittorrent/qBittorrent/blob/main/qBittorrent.conf) on Linux/macOS). When upgrading between releases, the application seamlessly transforms legacy configuration keys into the new schema without requiring manual intervention. This article examines the migration mechanism implemented in the `qbittorrent/qBittorrent` repository, covering the startup trigger, atomic persistence, and crash-safe design.

## The Migration Entry Point in application.cpp

The migration sequence fires early during application initialization. In [`src/app/application.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/application.cpp), the `Application::run()` method creates the `SettingsStorage` singleton and immediately invokes the `upgrade()` function at line 343:

```cpp
// src/app/application.cpp
int Application::run()
{
    SettingsStorage::initInstance();
    if (!upgrade()) {
        // Migration failed – log and abort
    }
    // Continue with UI initialization...
}

```

This ensures all settings transformations complete before any UI or network components access configuration values.

## Version Tracking and Incremental Upgrades

The migration logic resides in [`src/app/upgrade.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/upgrade.cpp). The system compares the stored migration version against the current constant `MIGRATION_VERSION` (defined as `10` at line 51).

At line 31, the code reads the stored version using a `CachedSettingValue<int>` wrapper:

```cpp
// src/app/upgrade.cpp (lines 29-53)
CachedSettingValue<int> version(u"Meta/MigrationVersion"_s, 0);

if (version != MIGRATION_VERSION) {
    // Execute missing migration steps
}

```

For each version gap, `upgrade()` invokes dedicated helper functions such as:
- `exportWebUIHttpsFiles()`
- `upgradeTorrentContentLayout()`
- `migrateProxySettings()`

These functions manipulate the in-memory settings map, and the changes persist automatically via the storage layer’s deferred write mechanism.

## Core Components of the Migration System

### SettingsStorage Singleton

The `SettingsStorage` class (defined in [`src/base/settingsstorage.h`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/settingsstorage.h) and implemented in [`src/base/settingsstorage.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/settingsstorage.cpp)) wraps `QSettings` with crash-safe persistence. Key responsibilities include:

- **Atomic writes**: The `writeNativeSettings()` method (lines 73-119) writes to a temporary `"*_new"` file and renames it atomically to prevent corruption during power loss or disk-full conditions
- **Recovery**: `readNativeSettings()` (lines 15-45) detects stray `"*_new"` files left by previous crashes and renames them back to the final filename (lines 39-56)
- **Type-safe API**: Templated `loadValue<T>()` and `storeValue<T>()` methods handle conversion between C++ types and `QVariant`

### CachedSettingValue Wrapper

The `CachedSettingValue<int>` template class caches the migration version in memory to avoid repeated disk reads during the upgrade check.

## The Migration Workflow Step-by-Step

The upgrade process follows a strict sequence to ensure data integrity:

1. **Load native settings**: `SettingsStorage::readNativeSettings()` populates an internal `QVariantHash` named `m_data` from the INI/CONF file

2. **Determine version gap**: The `upgrade()` function compares the stored `Meta/MigrationVersion` against `MIGRATION_VERSION`. If the stored value is lower, it runs the missing steps in ascending order

3. **Execute transformation**: Each migration helper reads legacy keys, computes new values, and stores them. For example, converting integer-based proxy types to enum-based values:

```cpp
// src/app/upgrade.cpp – migrateProxySettingsEnum() (lines 64-84)
void migrateProxySettingsEnum()
{
    auto *settingsStorage = SettingsStorage::instance();
    const auto key = u"Network/Proxy/Type"_s;
    const auto value = settingsStorage->loadValue<QString>(key);
    bool ok = false;
    const auto number = value.toInt(&ok);
    if (ok) {
        switch (number) {
            case 0: settingsStorage->storeValue(key, Net::ProxyType::None); break;
            case 1: settingsStorage->storeValue(key, Net::ProxyType::HTTP); break;
            case 2: settingsStorage->storeValue(key, Net::ProxyType::SOCKS5); break;
            default:
                LogMsg(QCoreApplication::translate("Upgrade",
                    "Invalid value ..."), Log::WARNING);
                settingsStorage->removeValue(key);
        }
    }
}

```

4. **Defer persistence**: Each `storeValue()` call sets a dirty flag. A single-shot `QTimer` (5-second delay) triggers `SettingsStorage::save()`, batching all modifications into one atomic write

5. **Update version marker**: After successful migration, line 80 writes the new version back to `Meta/MigrationVersion`, skipping future migrations:

```cpp
// src/app/upgrade.cpp (line 80)
version = MIGRATION_VERSION;

```

## Crash Safety and Atomic Persistence

The migration system employs defensive programming to protect configuration integrity:

- **Temporary file pattern**: When writing settings, the code creates a `"qBittorrent_new.ini"` (or equivalent) file and performs an atomic rename. If the application crashes during write, the original file remains untouched
- **Automatic recovery**: On the next startup, `readNativeSettings()` detects the orphaned `"*_new"` file and completes the replacement (lines 39-56 of [`settingsstorage.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/settingsstorage.cpp))
- **Batch updates**: The 5-second deferred write timer minimizes I/O operations and ensures partial updates cannot occur between migration steps

## Summary

- qBittorrent settings migration runs automatically at startup via `upgrade()` in [`src/app/application.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/application.cpp) before UI initialization
- The system tracks migration progress using the `Meta/MigrationVersion` key and the constant `MIGRATION_VERSION` (currently `10`)
- `SettingsStorage` provides crash-safe persistence through atomic file replacement and temporary `"*_new"` files
- Migration helpers in [`src/app/upgrade.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/upgrade.cpp) transform legacy values (such as converting integer proxy types to enums) using type-safe `loadValue<T>()` and `storeValue<T>()` APIs
- Changes persist via a deferred write mechanism using `QTimer` to batch disk operations

## Frequently Asked Questions

### Where does qBittorrent store the migration version?

qBittorrent stores the migration version in the `Meta/MigrationVersion` key within the native QSettings file. This value is read using a `CachedSettingValue<int>` wrapper at startup in [`src/app/upgrade.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/upgrade.cpp) to determine which incremental upgrades to apply.

### What happens if qBittorrent crashes during settings migration?

The migration is designed to be crash-safe. `SettingsStorage` writes to a temporary `"*_new"` file and renames it atomically only after the write completes. If a crash occurs, the next startup detects the orphaned temporary file in `readNativeSettings()` (lines 39-56 of [`src/base/settingsstorage.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/settingsstorage.cpp)) and recovers by renaming it to the final configuration file.

### How do I manually trigger a settings migration in qBittorrent?

You cannot manually trigger the migration process. The `upgrade()` function runs automatically when `Application::run()` detects that the stored `Meta/MigrationVersion` is lower than the current `MIGRATION_VERSION` constant. If you need to force a re-migration, you would need to manually edit the configuration file to lower the `Meta/MigrationVersion` value, which is not recommended for production use.

### Are old settings keys deleted after migration?

Some migration functions call `removeValue(oldKey)` to clean up legacy entries, while others leave them in place for backward compatibility. The specific behavior depends on the individual migration step implemented in [`src/app/upgrade.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/app/upgrade.cpp). The system prioritizes safety over cleanup, ensuring that unrecognized keys do not cause application errors.