[GH-ISSUE #7062] Misconfigured DATABASE_URL silently falls back to SQLite, risking data loss #35553

Closed
opened 2026-07-13 20:22:43 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @mfw78 on GitHub (Apr 7, 2026).
Original GitHub issue: https://github.com/dani-garcia/vaultwarden/issues/7062

Description

When DATABASE_URL does not start with mysql: or postgresql:, vaultwarden silently treats it as a SQLite file path due to the catch-all else branch in DbConnType::from_url. This means any misconfiguration that does not contain a / (or whose path happens to resolve to an existing directory) results in an empty SQLite database being quietly created rather than an error.

In containerised or ephemeral environments, this causes data loss on restart. The user believes they are connected to their intended database whilst secrets are actually written to a throwaway SQLite file that is destroyed when the container restarts.

Steps to reproduce

  1. Set DATABASE_URL to any value that is not a valid database URI, e.g. DATABASE_URL=foobar
  2. Start vaultwarden in a container
  3. Observe no error. Vaultwarden starts and creates a local SQLite database named foobar
  4. Store some secrets
  5. Restart the container
  6. Secrets are gone

Prior art

This has been reported before in #2835 and #1910. The partial fix in #2873 (checking the parent directory exists) helps when the misconfigured URL would create a new directory, but does not cover cases where the resolved path sits within an existing directory (such as the working directory itself).

The suggestions from contributors in #2873 (checking for colons, checking for quote characters) were never implemented.

Proposed fix

See #7061. Require an explicit sqlite:// prefix for new SQLite deployments. Bare paths without a recognised scheme are still accepted for backwards compatibility, but only when the database file already exists. Otherwise the process panics with a clear error message.

Originally created by @mfw78 on GitHub (Apr 7, 2026). Original GitHub issue: https://github.com/dani-garcia/vaultwarden/issues/7062 ## Description When `DATABASE_URL` does not start with `mysql:` or `postgresql:`, vaultwarden silently treats it as a SQLite file path due to the catch-all `else` branch in `DbConnType::from_url`. This means any misconfiguration that does not contain a `/` (or whose path happens to resolve to an existing directory) results in an empty SQLite database being quietly created rather than an error. In containerised or ephemeral environments, this causes data loss on restart. The user believes they are connected to their intended database whilst secrets are actually written to a throwaway SQLite file that is destroyed when the container restarts. ## Steps to reproduce 1. Set `DATABASE_URL` to any value that is not a valid database URI, e.g. `DATABASE_URL=foobar` 2. Start vaultwarden in a container 3. Observe no error. Vaultwarden starts and creates a local SQLite database named `foobar` 4. Store some secrets 5. Restart the container 6. Secrets are gone ## Prior art This has been reported before in #2835 and #1910. The partial fix in #2873 (checking the parent directory exists) helps when the misconfigured URL would create a new directory, but does not cover cases where the resolved path sits within an existing directory (such as the working directory itself). The suggestions from contributors in #2873 (checking for colons, checking for quote characters) were never implemented. ## Proposed fix See #7061. Require an explicit `sqlite://` prefix for new SQLite deployments. Bare paths without a recognised scheme are still accepted for backwards compatibility, but only when the database file already exists. Otherwise the process panics with a clear error message.
Author
Owner

@enimefl commented on GitHub (Apr 24, 2026):

Confirming this hits real-world Docker Compose self-host deployments. The catch-all branch is still present in current main at src/db/mod.rs L276–282:

//Sqlite
} else {
    #[cfg(sqlite)]
    return Ok(DbConnType::Sqlite);
    ...
}

Anything without a mysql: / postgresql: / postgres: prefix is silently accepted as a SQLite path. The partial guard added in #2873 (at src/config.rs L931–937) only kicks in when the URL contains /, so bare garbage (DATABASE_URL=foobar) or valid-looking-but-unsupported schemes (postgresql+ssl://…, postgres+tcp://… pasted from some managed-DB dashboards) bypass it completely and create an ephemeral SQLite file in the working directory.

Two things that would help self-hosters finding this via search, until #7061 lands:

  1. Detection — after starting the stack, run docker compose exec vaultwarden ls -la /data/ and look for an unexpected db.sqlite3 / *.sqlite3 file. If you configured MySQL/Postgres and see one, your writes are going to the wrong place. Stop, fix DATABASE_URL, restart, and restore from the DB you meant to use.
  2. Explicit paths — if you do want SQLite, set DATABASE_URL to an absolute path like /data/db.sqlite3. Doing so triggers the existing parent-directory check and fails loudly on typos, instead of silently writing into CWD.

PR #7061's "require a recognised scheme, accept bare paths only if the file already exists" is the right shape — it closes the silent-corruption window without breaking existing well-configured deployments.

<!-- gh-comment-id:4316586074 --> @enimefl commented on GitHub (Apr 24, 2026): Confirming this hits real-world Docker Compose self-host deployments. The catch-all branch is still present in current `main` at `src/db/mod.rs` L276–282: ```rust //Sqlite } else { #[cfg(sqlite)] return Ok(DbConnType::Sqlite); ... } ``` Anything without a `mysql:` / `postgresql:` / `postgres:` prefix is silently accepted as a SQLite path. The partial guard added in #2873 (at `src/config.rs` L931–937) only kicks in when the URL contains `/`, so bare garbage (`DATABASE_URL=foobar`) or valid-looking-but-unsupported schemes (`postgresql+ssl://…`, `postgres+tcp://…` pasted from some managed-DB dashboards) bypass it completely and create an ephemeral SQLite file in the working directory. Two things that would help self-hosters finding this via search, until #7061 lands: 1. **Detection** — after starting the stack, run `docker compose exec vaultwarden ls -la /data/` and look for an unexpected `db.sqlite3` / `*.sqlite3` file. If you configured MySQL/Postgres and see one, your writes are going to the wrong place. Stop, fix `DATABASE_URL`, restart, and restore from the DB you meant to use. 2. **Explicit paths** — if you *do* want SQLite, set `DATABASE_URL` to an absolute path like `/data/db.sqlite3`. Doing so triggers the existing parent-directory check and fails loudly on typos, instead of silently writing into CWD. PR #7061's "require a recognised scheme, accept bare paths only if the file already exists" is the right shape — it closes the silent-corruption window without breaking existing well-configured deployments.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/vaultwarden#35553