diff --git a/Taskfile.yml b/Taskfile.yml
index 5352a3ca3..e926fe9f0 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -88,6 +88,8 @@ tasks:
- rm -r ./dev/data/recipes/
- rm -r ./dev/data/users/
- rm -f ./dev/data/mealie*.db
+ - rm -f ./dev/data/mealie*.db-shm
+ - rm -f ./dev/data/mealie*.db-wal
- rm -f ./dev/data/mealie.log
- rm -f ./dev/data/.secret
diff --git a/docs/docs/documentation/getting-started/installation/backend-config.md b/docs/docs/documentation/getting-started/installation/backend-config.md
index 0857e7f63..0bf06e304 100644
--- a/docs/docs/documentation/getting-started/installation/backend-config.md
+++ b/docs/docs/documentation/getting-started/installation/backend-config.md
@@ -32,15 +32,16 @@
### Database
- | Variables | Default | Description |
- | ------------------------------------------------------- | :------: | ----------------------------------------------------------------------- |
- | DB_ENGINE | sqlite | Optional: 'sqlite', 'postgres' |
- | POSTGRES_USER[†][secrets] | mealie | Postgres database user |
- | POSTGRES_PASSWORD[†][secrets] | mealie | Postgres database password |
- | POSTGRES_SERVER[†][secrets] | postgres | Postgres database server address |
- | POSTGRES_PORT[†][secrets] | 5432 | Postgres database port |
- | POSTGRES_DB[†][secrets] | mealie | Postgres database name |
- | POSTGRES_URL_OVERRIDE[†][secrets] | None | Optional Postgres URL override to use instead of POSTGRES\_\* variables |
+ | Variables | Default | Description |
+ |---------------------------------------------------------|:--------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+ | DB_ENGINE | sqlite | Optional: 'sqlite', 'postgres' |
+ | SQLITE_MIGRATE_JOURNAL_WAL | False | If set to true, switches SQLite's journal mode to WAL, which allows for multiple concurrent accesses. This can be useful when you have a decent amount of concurrency or when using certain remote storage systems such as Ceph. |
+ | POSTGRES_USER[†][secrets] | mealie | Postgres database user |
+ | POSTGRES_PASSWORD[†][secrets] | mealie | Postgres database password |
+ | POSTGRES_SERVER[†][secrets] | postgres | Postgres database server address |
+ | POSTGRES_PORT[†][secrets] | 5432 | Postgres database port |
+ | POSTGRES_DB[†][secrets] | mealie | Postgres database name |
+ | POSTGRES_URL_OVERRIDE[†][secrets] | None | Optional Postgres URL override to use instead of POSTGRES\_\* variables |
### Email
diff --git a/mealie/core/settings/settings.py b/mealie/core/settings/settings.py
index 1f30fdf14..10bfb6aeb 100644
--- a/mealie/core/settings/settings.py
+++ b/mealie/core/settings/settings.py
@@ -196,6 +196,8 @@ class AppSettings(AppLoggingSettings):
DB_ENGINE: str = "sqlite" # Options: 'sqlite', 'postgres'
DB_PROVIDER: AbstractDBProvider | None = None
+ SQLITE_MIGRATE_JOURNAL_WAL: bool = False
+
@property
def DB_URL(self) -> str | None:
return self.DB_PROVIDER.db_url if self.DB_PROVIDER else None
diff --git a/mealie/db/db_setup.py b/mealie/db/db_setup.py
index 672260e2a..ea0d9a334 100644
--- a/mealie/db/db_setup.py
+++ b/mealie/db/db_setup.py
@@ -2,6 +2,8 @@ from collections.abc import Generator
from contextlib import contextmanager
import sqlalchemy as sa
+from sqlalchemy.engine import Engine
+from sqlalchemy.event import listens_for
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.session import Session
@@ -10,6 +12,24 @@ from mealie.core.config import get_app_settings
settings = get_app_settings()
+@listens_for(Engine, "connect")
+def set_sqlite_pragma_journal_wal(dbapi_connection, connection_record):
+ """
+ Automatically enables SQLite's WAL journal mode if the setting is activated.
+
+ This is a persistent setting, so turning it off down the line doesn't revert back to the original journal mode.
+
+ Write-Ahead-Log enables sqlite to be used concurrently by multiple readers and writers (writes still happen
+ sequentially).
+ """
+ global settings
+ if settings.DB_ENGINE != "sqlite" or not settings.SQLITE_MIGRATE_JOURNAL_WAL:
+ return
+ cursor = dbapi_connection.cursor()
+ cursor.execute("PRAGMA journal_mode=WAL")
+ cursor.close()
+
+
def sql_global_init(db_url: str):
connect_args = {}
if "sqlite" in db_url: