diff --git a/services/qbittorrent.nix b/services/qbittorrent.nix index 0591e18..a4274a5 100644 --- a/services/qbittorrent.nix +++ b/services/qbittorrent.nix @@ -164,6 +164,35 @@ in _: path: "d ${path} 0770 ${config.services.qbittorrent.user} ${service_configs.media_group} -" ) service_configs.torrent.categories; + # Periodically checkpoint qBittorrent's SQLite WAL (Write-Ahead Log). + # qBittorrent holds a read transaction open for its entire lifetime, + # preventing SQLite's auto-checkpoint from running. The WAL grows + # unbounded (observed: 405 MB) and must be replayed on next startup, + # causing 10+ minute "internal preparations" hangs. + # A second sqlite3 connection can checkpoint concurrently and safely. + # See: https://github.com/qbittorrent/qBittorrent/issues/20433 + systemd.services.qbittorrent-wal-checkpoint = { + description = "Checkpoint qBittorrent SQLite WAL"; + after = [ "qbittorrent.service" ]; + requires = [ "qbittorrent.service" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.sqlite}/bin/sqlite3 ${config.services.qbittorrent.profileDir}/qBittorrent/data/torrents.db 'PRAGMA wal_checkpoint(TRUNCATE);'"; + User = config.services.qbittorrent.user; + Group = config.services.qbittorrent.group; + }; + }; + + systemd.timers.qbittorrent-wal-checkpoint = { + description = "Periodically checkpoint qBittorrent SQLite WAL"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnUnitActiveSec = "4h"; + OnBootSec = "30min"; + RandomizedDelaySec = "10min"; + }; + }; + users.users.${config.services.qbittorrent.user}.extraGroups = [ service_configs.media_group ];