diff --git a/pkgs/servers/web-apps/kavita/default.nix b/pkgs/servers/web-apps/kavita/default.nix
index 44c0ad59cda2..7873ee684014 100644
--- a/pkgs/servers/web-apps/kavita/default.nix
+++ b/pkgs/servers/web-apps/kavita/default.nix
@@ -34,6 +34,12 @@ stdenvNoCC.mkDerivation (finalAttrs: {
})
# The webroot is hardcoded as ./wwwroot
./change-webroot.diff
+ # Upstream removes database migrations between versions
+ # Restore them to avoid breaking on updates
+ # Info: Restores migrations for versions between v0.7.1.4 and v0.7.9
+ # On update: check if more migrations need to be restored!
+ # Migrations should at least allow updates from previous NixOS versions
+ ./restore-migrations.diff
];
postPatch = ''
substituteInPlace API/Services/DirectoryService.cs --subst-var out
diff --git a/pkgs/servers/web-apps/kavita/restore-migrations.diff b/pkgs/servers/web-apps/kavita/restore-migrations.diff
new file mode 100644
index 000000000000..d158f503e329
--- /dev/null
+++ b/pkgs/servers/web-apps/kavita/restore-migrations.diff
@@ -0,0 +1,147 @@
+diff --git a/API/Data/ManualMigrations/MigrateDisableScrobblingOnComicLibraries.cs b/API/Data/ManualMigrations/MigrateDisableScrobblingOnComicLibraries.cs
+new file mode 100644
+index 00000000..0de7bf5d
+--- /dev/null
++++ b/API/Data/ManualMigrations/MigrateDisableScrobblingOnComicLibraries.cs
+@@ -0,0 +1,38 @@
++using System.Linq;
++using System.Threading.Tasks;
++using API.Entities.Enums;
++using Microsoft.EntityFrameworkCore;
++using Microsoft.Extensions.Logging;
++
++namespace API.Data.ManualMigrations;
++
++///
++/// v0.7.4 introduced Scrobbling with Kavita+. By default, it is on, but Comic libraries have no scrobble providers, so disable
++///
++public static class MigrateDisableScrobblingOnComicLibraries
++{
++ public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, ILogger logger)
++ {
++ if (!await dataContext.Library.Where(s => s.Type == LibraryType.Comic).Where(l => l.AllowScrobbling).AnyAsync())
++ {
++ return;
++ }
++ logger.LogInformation("Running MigrateDisableScrobblingOnComicLibraries migration. Please be patient, this may take some time");
++
++
++ foreach (var lib in await dataContext.Library.Where(s => s.Type == LibraryType.Comic).Where(l => l.AllowScrobbling).ToListAsync())
++ {
++ lib.AllowScrobbling = false;
++ unitOfWork.LibraryRepository.Update(lib);
++ }
++
++ if (unitOfWork.HasChanges())
++ {
++ await unitOfWork.CommitAsync();
++ }
++
++ logger.LogInformation("MigrateDisableScrobblingOnComicLibraries migration finished");
++
++ }
++
++}
+diff --git a/API/Data/ManualMigrations/MigrateLoginRoles.cs b/API/Data/ManualMigrations/MigrateLoginRoles.cs
+new file mode 100644
+index 00000000..f649908a
+--- /dev/null
++++ b/API/Data/ManualMigrations/MigrateLoginRoles.cs
+@@ -0,0 +1,36 @@
++using System.Threading.Tasks;
++using API.Constants;
++using API.Entities;
++using Microsoft.AspNetCore.Identity;
++using Microsoft.Extensions.Logging;
++
++namespace API.Data.ManualMigrations;
++
++///
++/// Added in v0.7.1.18
++///
++public static class MigrateLoginRoles
++{
++ ///
++ /// Will not run if any users have the role already
++ ///
++ ///
++ ///
++ ///
++ public static async Task Migrate(IUnitOfWork unitOfWork, UserManager userManager, ILogger logger)
++ {
++ var usersWithRole = await userManager.GetUsersInRoleAsync(PolicyConstants.LoginRole);
++ if (usersWithRole.Count != 0) return;
++
++ logger.LogCritical("Running MigrateLoginRoles migration");
++
++ var allUsers = await unitOfWork.UserRepository.GetAllUsersAsync();
++ foreach (var user in allUsers)
++ {
++ await userManager.RemoveFromRoleAsync(user, PolicyConstants.LoginRole);
++ await userManager.AddToRoleAsync(user, PolicyConstants.LoginRole);
++ }
++
++ logger.LogInformation("MigrateLoginRoles migration complete");
++ }
++}
+diff --git a/API/Data/ManualMigrations/MigrateRemoveWebPSettingRows.cs b/API/Data/ManualMigrations/MigrateRemoveWebPSettingRows.cs
+new file mode 100644
+index 00000000..07e98ef6
+--- /dev/null
++++ b/API/Data/ManualMigrations/MigrateRemoveWebPSettingRows.cs
+@@ -0,0 +1,31 @@
++using System.Threading.Tasks;
++using API.Entities.Enums;
++using Microsoft.Extensions.Logging;
++
++namespace API.Data.ManualMigrations;
++
++///
++/// Added in v0.7.2.7/v0.7.3 in which the ConvertXToWebP Setting keys were removed. This migration will remove them.
++///
++public static class MigrateRemoveWebPSettingRows
++{
++ public static async Task Migrate(IUnitOfWork unitOfWork, ILogger logger)
++ {
++ logger.LogCritical("Running MigrateRemoveWebPSettingRows migration - Please be patient, this may take some time. This is not an error");
++
++ var key = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.ConvertBookmarkToWebP);
++ var key2 = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.ConvertCoverToWebP);
++ if (key == null && key2 == null)
++ {
++ logger.LogCritical("Running MigrateRemoveWebPSettingRows migration - complete. Nothing to do");
++ return;
++ }
++
++ unitOfWork.SettingsRepository.Remove(key);
++ unitOfWork.SettingsRepository.Remove(key2);
++
++ await unitOfWork.CommitAsync();
++
++ logger.LogCritical("Running MigrateRemoveWebPSettingRows migration - Completed. This is not an error");
++ }
++}
+diff --git a/API/Startup.cs b/API/Startup.cs
+index 21c4fa45..04f4a077 100644
+--- a/API/Startup.cs
++++ b/API/Startup.cs
+@@ -232,11 +232,19 @@ public class Startup
+ Task.Run(async () =>
+ {
+ // Apply all migrations on startup
++ var userManager = serviceProvider.GetRequiredService>();
+ var dataContext = serviceProvider.GetRequiredService();
+
+
+ logger.LogInformation("Running Migrations");
+
++ // v0.7.2
++ await MigrateLoginRoles.Migrate(unitOfWork, userManager, logger);
++ // v0.7.3
++ await MigrateRemoveWebPSettingRows.Migrate(unitOfWork, logger);
++ // v0.7.4
++ await MigrateDisableScrobblingOnComicLibraries.Migrate(unitOfWork, dataContext, logger);
++
+ // v0.7.9
+ await MigrateUserLibrarySideNavStream.Migrate(unitOfWork, dataContext, logger);
+