diff --git a/README.md b/README.md
index 8091207a6..67c199960 100644
--- a/README.md
+++ b/README.md
@@ -659,10 +659,10 @@ Here are some of the features Ombi has:
diff --git a/src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs b/src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs
new file mode 100644
index 000000000..2f1933184
--- /dev/null
+++ b/src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Text;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+using Ombi.Core.Models;
+using Polly;
+using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
+
+namespace Ombi.Core.Helpers;
+
+public static class DatabaseConfigurationSetup
+{
+ public static void ConfigurePostgres(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
+ {
+ options.UseNpgsql(config.ConnectionString, b =>
+ {
+ b.EnableRetryOnFailure();
+ }).ReplaceService();
+ }
+
+ public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
+ {
+ if (string.IsNullOrEmpty(config.ConnectionString))
+ {
+ throw new ArgumentNullException("ConnectionString for the MySql/Mariadb database is empty");
+ }
+
+ options.UseMySql(config.ConnectionString, GetServerVersion(config.ConnectionString), b =>
+ {
+ //b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations?
+ b.EnableRetryOnFailure();
+ });
+ }
+
+ private static ServerVersion GetServerVersion(string connectionString)
+ {
+ // Workaround Windows bug, that can lead to the following exception:
+ //
+ // MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
+ // ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
+ // ---> System.ComponentModel.Win32Exception (0x8009030F): The message or signature supplied for verification has been altered
+ //
+ // See https://github.com/dotnet/runtime/issues/17005#issuecomment-305848835
+ //
+ // Also workaround for the fact, that ServerVersion.AutoDetect() does not use any retrying strategy.
+ ServerVersion serverVersion = null;
+#pragma warning disable EF1001
+ var retryPolicy = Policy.Handle(exception => MySqlTransientExceptionDetector.ShouldRetryOn(exception))
+#pragma warning restore EF1001
+ .WaitAndRetry(3, (count, context) => TimeSpan.FromMilliseconds(count * 250));
+
+ serverVersion = retryPolicy.Execute(() => serverVersion = ServerVersion.AutoDetect(connectionString));
+
+ return serverVersion;
+ }
+ public class NpgsqlCaseInsensitiveSqlGenerationHelper : NpgsqlSqlGenerationHelper
+ {
+ const string EFMigrationsHisory = "__EFMigrationsHistory";
+ public NpgsqlCaseInsensitiveSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies)
+ : base(dependencies) { }
+ public override string DelimitIdentifier(string identifier) =>
+ base.DelimitIdentifier(identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
+ public override void DelimitIdentifier(StringBuilder builder, string identifier)
+ => base.DelimitIdentifier(builder, identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Helpers/FileSystem.cs b/src/Ombi.Core/Helpers/FileSystem.cs
new file mode 100644
index 000000000..97b9da0bf
--- /dev/null
+++ b/src/Ombi.Core/Helpers/FileSystem.cs
@@ -0,0 +1,10 @@
+namespace Ombi.Core.Helpers;
+
+public class FileSystem : IFileSystem
+{
+ public bool FileExists(string path)
+ {
+ return System.IO.File.Exists(path);
+ }
+ // Implement other file system operations as needed
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Helpers/IFileSystem.cs b/src/Ombi.Core/Helpers/IFileSystem.cs
new file mode 100644
index 000000000..da2c9bba5
--- /dev/null
+++ b/src/Ombi.Core/Helpers/IFileSystem.cs
@@ -0,0 +1,7 @@
+namespace Ombi.Core.Helpers;
+
+public interface IFileSystem
+{
+ bool FileExists(string path);
+ // Add other file system operations as needed
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Models/DatabaseConfiguration.cs b/src/Ombi.Core/Models/DatabaseConfiguration.cs
new file mode 100644
index 000000000..550800108
--- /dev/null
+++ b/src/Ombi.Core/Models/DatabaseConfiguration.cs
@@ -0,0 +1,40 @@
+using System.IO;
+
+namespace Ombi.Core.Models;
+
+public class DatabaseConfiguration
+{
+ public const string SqliteDatabase = "Sqlite";
+
+ public DatabaseConfiguration()
+ {
+
+ }
+
+ public DatabaseConfiguration(string defaultSqlitePath)
+ {
+ OmbiDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "Ombi.db")}");
+ SettingsDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiSettings.db")}");
+ ExternalDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiExternal.db")}");
+ }
+ public PerDatabaseConfiguration OmbiDatabase { get; set; }
+ public PerDatabaseConfiguration SettingsDatabase { get; set; }
+ public PerDatabaseConfiguration ExternalDatabase { get; set; }
+}
+
+public class PerDatabaseConfiguration
+{
+ public PerDatabaseConfiguration(string type, string connectionString)
+ {
+ Type = type;
+ ConnectionString = connectionString;
+ }
+
+ // Used in Deserialization
+ public PerDatabaseConfiguration()
+ {
+
+ }
+ public string Type { get; set; }
+ public string ConnectionString { get; set; }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Services/DatabaseConfigurationService.cs b/src/Ombi.Core/Services/DatabaseConfigurationService.cs
new file mode 100644
index 000000000..750499b19
--- /dev/null
+++ b/src/Ombi.Core/Services/DatabaseConfigurationService.cs
@@ -0,0 +1,69 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Ombi.Core.Helpers;
+using Ombi.Core.Models;
+using Ombi.Helpers;
+
+namespace Ombi.Core.Services;
+
+public class DatabaseConfigurationService : IDatabaseConfigurationService
+{
+
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+
+ public DatabaseConfigurationService(
+ ILogger logger,
+ IFileSystem fileSystem)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
+ public async Task ConfigureDatabase(string databaseType, string connectionString, CancellationToken token)
+ {
+ var i = StartupSingleton.Instance;
+ if (string.IsNullOrEmpty(i.StoragePath))
+ {
+ i.StoragePath = string.Empty;
+ }
+
+ var databaseFileLocation = Path.Combine(i.StoragePath, "database.json");
+ if (_fileSystem.FileExists(databaseFileLocation))
+ {
+ var error = $"The database file at '{databaseFileLocation}' already exists";
+ _logger.LogError(error);
+ return false;
+ }
+
+ var configuration = new DatabaseConfiguration
+ {
+ ExternalDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
+ OmbiDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
+ SettingsDatabase = new PerDatabaseConfiguration(databaseType, connectionString)
+ };
+
+ var json = JsonConvert.SerializeObject(configuration, Formatting.Indented);
+
+ _logger.LogInformation("Writing database configuration to file");
+
+ try
+ {
+ await File.WriteAllTextAsync(databaseFileLocation, json, token);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Failed to write database configuration to file");
+ return false;
+ }
+
+ _logger.LogInformation("Database configuration written to file");
+
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Services/IDatabaseConfigurationService.cs b/src/Ombi.Core/Services/IDatabaseConfigurationService.cs
new file mode 100644
index 000000000..3530bf913
--- /dev/null
+++ b/src/Ombi.Core/Services/IDatabaseConfigurationService.cs
@@ -0,0 +1,11 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ombi.Core.Services;
+
+public interface IDatabaseConfigurationService
+{
+ const string MySqlDatabase = "MySQL";
+ const string PostgresDatabase = "Postgres";
+ Task ConfigureDatabase(string databaseType, string connectionString, CancellationToken token);
+}
\ No newline at end of file
diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs
index 8a5509963..caceb9b0e 100644
--- a/src/Ombi.DependencyInjection/IocExtensions.cs
+++ b/src/Ombi.DependencyInjection/IocExtensions.cs
@@ -236,6 +236,8 @@ namespace Ombi.DependencyInjection
services.AddScoped();
services.AddTransient();
services.AddTransient();
+ services.AddSingleton();
+ services.AddSingleton();
}
public static void RegisterJobs(this IServiceCollection services)
diff --git a/src/Ombi/ClientApp/src/app/wizard/database/database.component.html b/src/Ombi/ClientApp/src/app/wizard/database/database.component.html
new file mode 100644
index 000000000..40aa353b6
--- /dev/null
+++ b/src/Ombi/ClientApp/src/app/wizard/database/database.component.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
Choose a Database
+
+ SQLite is the default option and the easiest to set up, as it requires no additional configuration.
+ However, it has significant limitations, including potential performance issues and database locking.
+ While many users start with SQLite and later migrate to MySQL or MariaDB, we recommend beginning with MySQL or MariaDB from the start for a more robust and scalable experience.
+
+
+ For more information on using alternate databases, see the documentation.
+