[GH-ISSUE #23854] issue: v0.9.0 (:dev): DATABASE_URL SSL regression — asyncpg and peewee/psycopg2 require incompatible query-string keys #20089

Open
opened 2026-04-20 02:40:31 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @jaco5842 on GitHub (Apr 17, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/23854

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
  • I am using the latest version of Open WebUI.

Installation Method

Docker

Open WebUI Version

v0.9.0 (:dev branch)

Ollama Version (if applicable)

No response

Operating System

Windows 11 Pro, Docker Desktop with WSL2 backend. Container: official open-webui:dev image (Python 3.11).

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

Open WebUI starts cleanly when DATABASE_URL points at an SSL-required Postgres (Azure Database for PostgreSQL, AWS RDS, Supabase, DigitalOcean managed PG, etc.) using ?sslmode=require — the syntax that
worked in every prior release and is documented by libpq / psycopg2.

Actual Behavior

Reporting per the project's guidance that bugs reproduced on the :dev branch should be filed as issues. Reproduced on ghcr.io/open-webui/open-webui:dev (v0.9.0, current as of 2026-04-17).

Startup crashes before serving any request, with a mutually exclusive choice of errors depending on which SSL query-string parameter is used in DATABASE_URL. Neither value satisfies both drivers that v0.9.0
now uses concurrently:

  • ?sslmode=require → asyncpg raises TypeError: connect() got an unexpected keyword argument 'sslmode' (SQLAlchemy/asyncpg runtime path).
  • ?ssl=require → psycopg2 raises psycopg2.ProgrammingError: invalid dsn: invalid connection option "ssl" (peewee migration path in open_webui/internal/db.py:66).

v0.9 appears to have added an async SQLAlchemy + asyncpg runtime path alongside the existing peewee + psycopg2 migration path. The two drivers parse DSN query strings differently:

  • psycopg2 → libpq-style, expects sslmode.
  • asyncpg → expects ssl; rejects sslmode as an unknown kwarg.

No single DATABASE_URL value works for both drivers, so any deployment requiring SSL fails at startup. Earlier versions (v0.8.x) only used psycopg2 and accepted ?sslmode=require without issue, so this is
a regression for every managed-Postgres user.

Workaround (undocumented): remove the SSL query string from DATABASE_URL entirely and set PGSSLMODE=require as a separate environment variable. Both drivers honor libpq's PGSSLMODE env var as a
fallback, so this satisfies both paths.

Steps to Reproduce

  1. Start with a clean Docker Desktop install (tested on Windows 11 Pro + WSL2, but OS is not relevant — same issue on Linux hosts).

  2. Provision any managed Postgres that requires SSL (verified against Azure Database for PostgreSQL Flexible Server; same applies to AWS RDS, Supabase, etc.).

  3. Create an empty database and a user with CREATE privileges on it.

  4. Create a docker-compose.yml:

    services:
      open-webui:
        image: ghcr.io/open-webui/open-webui:dev
        ports:
          - "3080:8080"
        environment:
          - DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/DBNAME?sslmode=require
          - WEBUI_SECRET_KEY=anything
    

Logs & Screenshots

❯ 2026-04-17 23:30:00.675 | Failed to initialize the database connection: invalid dsn: invalid connection option "ssl"
2026-04-17 23:30:00.675 |
2026-04-17 23:30:00.676 | Hint: If your database password contains special characters, you may need to URL-encode it.
2026-04-17 23:30:00.676 | Traceback (most recent call last):
2026-04-17 23:30:00.676 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 3263, in connect
2026-04-17 23:30:00.677 | self._state.set_connection(self._connect())
2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^
2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 4001, in _connect
2026-04-17 23:30:00.677 | conn = psycopg2.connect(**params)
2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/psycopg2/init.py", line 121, in connect
2026-04-17 23:30:00.677 | dsn = _ext.make_dsn(dsn, **kwargs)
2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/psycopg2/extensions.py", line 167, in make_dsn
2026-04-17 23:30:00.677 | parse_dsn(dsn)
2026-04-17 23:30:00.677 | psycopg2.ProgrammingError: invalid dsn: invalid connection option "ssl"
2026-04-17 23:30:00.677 |
2026-04-17 23:30:00.677 |
2026-04-17 23:30:00.677 | During handling of the above exception, another exception occurred:
2026-04-17 23:30:00.677 |
2026-04-17 23:30:00.677 | Traceback (most recent call last):
2026-04-17 23:30:00.677 | File "/app/backend/open_webui/internal/db.py", line 66, in handle_peewee_migration
2026-04-17 23:30:00.677 | db = register_connection(DATABASE_URL.replace('postgresql://', 'postgres://'))
2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-17 23:30:00.677 | File "/app/backend/open_webui/internal/wrappers.py", line 76, in register_connection
2026-04-17 23:30:00.677 | db.connect(reuse_if_open=True)
2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 3262, in connect
2026-04-17 23:30:00.678 | with exception_wrapper:
2026-04-17 23:30:00.678 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 3088, in exit
2026-04-17 23:30:00.678 | reraise(new_type, new_type(exc_value, *exc_args), traceback)
2026-04-17 23:30:00.678 | File "/usr/local/lib/python3.11/site-pac
──── (380 lines hidden) ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
m_string
2026-04-17 23:30:07.962 | module = importlib.import_module(module_str)
2026-04-17 23:30:07.962 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-17 23:30:07.962 | File "/usr/local/lib/python3.11/importlib/init.py", line 126, in import_module
2026-04-17 23:30:07.962 | return _bootstrap._gcd_import(name[level:], package, level)
2026-04-17 23:30:07.962 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-17 23:30:07.962 | File "", line 1204, in _gcd_import
2026-04-17 23:30:07.962 | File "", line 1176, in _find_and_load
2026-04-17 23:30:07.962 | File "", line 1147, in _find_and_load_unlocked
2026-04-17 23:30:07.962 | File "", line 690, in _load_unlocked
2026-04-17 23:30:07.962 | File "", line 940, in exec_module
2026-04-17 23:30:07.962 | File "", line 241, in _call_with_frames_removed
2026-04-17 23:30:07.962 | File "/app/backend/open_webui/main.py", line 60, in
2026-04-17 23:30:07.963 | from open_webui.utils.asgi_middleware import (
2026-04-17 23:30:07.963 | File "/app/backend/open_webui/utils/asgi_middleware.py", line 44, in
2026-04-17 23:30:07.963 | from open_webui.internal.db import ScopedSession
2026-04-17 23:30:07.963 | File "/app/backend/open_webui/internal/db.py", line 86, in
2026-04-17 23:30:07.963 | handle_peewee_migration(DATABASE_URL)
2026-04-17 23:30:07.963 | File "/app/backend/open_webui/internal/db.py", line 78, in handle_peewee_migration
2026-04-17 23:30:07.963 | if db and not db.is_closed():
2026-04-17 23:30:07.963 | ^^

Additional Information

Root cause summary: v0.9 added an async SQLAlchemy + asyncpg code path for runtime queries (open_webui/models/functions.py, etc.) while retaining the peewee + psycopg2 path for migrations
(open_webui/internal/db.py:handle_peewee_migration). Both drivers parse DATABASE_URL independently, and their query-string conventions for SSL are incompatible (sslmode vs ssl). No documentation covers
the new requirement.

Suggested fixes (pick any one, or combine):

  1. Document that users with SSL-required Postgres should set PGSSLMODE=require as an env var and omit the SSL query string from DATABASE_URL.
  2. Normalize the DSN inside both code paths — strip sslmode / ssl from the URL and inject the right kwarg per driver.
  3. Translate sslmode=<value> → asyncpg connect_args={"ssl": <value>} at the SQLAlchemy engine setup site, so the libpq-style URL keeps working.

Secondary cleanup: in open_webui/internal/db.py:handle_peewee_migration, initialize db = None at the top of the function so the outer if db and not db.is_closed() doesn't fail with UnboundLocalError
when the connect itself raises. Right now the real error is masked by a NameError.

Confirmed affected configurations: Azure Database for PostgreSQL Flexible Server (SSL required by default, cannot be disabled). Same failure pattern is expected on AWS RDS, DigitalOcean Managed PG, Supabase,
etc., since all require SSL.

Originally created by @jaco5842 on GitHub (Apr 17, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/23854 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!). - [x] I am using the latest version of Open WebUI. ### Installation Method Docker ### Open WebUI Version v0.9.0 (:dev branch) ### Ollama Version (if applicable) _No response_ ### Operating System Windows 11 Pro, Docker Desktop with WSL2 backend. Container: official open-webui:dev image (Python 3.11). ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior Open WebUI starts cleanly when DATABASE_URL points at an SSL-required Postgres (Azure Database for PostgreSQL, AWS RDS, Supabase, DigitalOcean managed PG, etc.) using `?sslmode=require` — the syntax that worked in every prior release and is documented by libpq / psycopg2. ### Actual Behavior Reporting per the project's guidance that bugs reproduced on the :dev branch should be filed as issues. Reproduced on `ghcr.io/open-webui/open-webui:dev` (v0.9.0, current as of 2026-04-17). Startup crashes before serving any request, with a mutually exclusive choice of errors depending on which SSL query-string parameter is used in DATABASE_URL. Neither value satisfies both drivers that v0.9.0 now uses concurrently: - `?sslmode=require` → asyncpg raises `TypeError: connect() got an unexpected keyword argument 'sslmode'` (SQLAlchemy/asyncpg runtime path). - `?ssl=require` → psycopg2 raises `psycopg2.ProgrammingError: invalid dsn: invalid connection option "ssl"` (peewee migration path in `open_webui/internal/db.py:66`). v0.9 appears to have added an async SQLAlchemy + asyncpg runtime path alongside the existing peewee + psycopg2 migration path. The two drivers parse DSN query strings differently: - psycopg2 → libpq-style, expects `sslmode`. - asyncpg → expects `ssl`; rejects `sslmode` as an unknown kwarg. No single `DATABASE_URL` value works for both drivers, so any deployment requiring SSL fails at startup. Earlier versions (v0.8.x) only used psycopg2 and accepted `?sslmode=require` without issue, so this is a regression for every managed-Postgres user. Workaround (undocumented): remove the SSL query string from `DATABASE_URL` entirely and set `PGSSLMODE=require` as a separate environment variable. Both drivers honor libpq's `PGSSLMODE` env var as a fallback, so this satisfies both paths. ### Steps to Reproduce 1. Start with a clean Docker Desktop install (tested on Windows 11 Pro + WSL2, but OS is not relevant — same issue on Linux hosts). 2. Provision any managed Postgres that requires SSL (verified against Azure Database for PostgreSQL Flexible Server; same applies to AWS RDS, Supabase, etc.). 3. Create an empty database and a user with CREATE privileges on it. 4. Create a `docker-compose.yml`: ```yaml services: open-webui: image: ghcr.io/open-webui/open-webui:dev ports: - "3080:8080" environment: - DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/DBNAME?sslmode=require - WEBUI_SECRET_KEY=anything ``` ### Logs & Screenshots ❯ 2026-04-17 23:30:00.675 | Failed to initialize the database connection: invalid dsn: invalid connection option "ssl" 2026-04-17 23:30:00.675 | 2026-04-17 23:30:00.676 | Hint: If your database password contains special characters, you may need to URL-encode it. 2026-04-17 23:30:00.676 | Traceback (most recent call last): 2026-04-17 23:30:00.676 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 3263, in connect 2026-04-17 23:30:00.677 | self._state.set_connection(self._connect()) 2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^ 2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 4001, in _connect 2026-04-17 23:30:00.677 | conn = psycopg2.connect(**params) 2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/psycopg2/__init__.py", line 121, in connect 2026-04-17 23:30:00.677 | dsn = _ext.make_dsn(dsn, **kwargs) 2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/psycopg2/extensions.py", line 167, in make_dsn 2026-04-17 23:30:00.677 | parse_dsn(dsn) 2026-04-17 23:30:00.677 | psycopg2.ProgrammingError: invalid dsn: invalid connection option "ssl" 2026-04-17 23:30:00.677 | 2026-04-17 23:30:00.677 | 2026-04-17 23:30:00.677 | During handling of the above exception, another exception occurred: 2026-04-17 23:30:00.677 | 2026-04-17 23:30:00.677 | Traceback (most recent call last): 2026-04-17 23:30:00.677 | File "/app/backend/open_webui/internal/db.py", line 66, in handle_peewee_migration 2026-04-17 23:30:00.677 | db = register_connection(DATABASE_URL.replace('postgresql://', 'postgres://')) 2026-04-17 23:30:00.677 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2026-04-17 23:30:00.677 | File "/app/backend/open_webui/internal/wrappers.py", line 76, in register_connection 2026-04-17 23:30:00.677 | db.connect(reuse_if_open=True) 2026-04-17 23:30:00.677 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 3262, in connect 2026-04-17 23:30:00.678 | with __exception_wrapper__: 2026-04-17 23:30:00.678 | File "/usr/local/lib/python3.11/site-packages/peewee.py", line 3088, in __exit__ 2026-04-17 23:30:00.678 | reraise(new_type, new_type(exc_value, *exc_args), traceback) 2026-04-17 23:30:00.678 | File "/usr/local/lib/python3.11/site-pac ──── (380 lines hidden) ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── m_string 2026-04-17 23:30:07.962 | module = importlib.import_module(module_str) 2026-04-17 23:30:07.962 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2026-04-17 23:30:07.962 | File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module 2026-04-17 23:30:07.962 | return _bootstrap._gcd_import(name[level:], package, level) 2026-04-17 23:30:07.962 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2026-04-17 23:30:07.962 | File "<frozen importlib._bootstrap>", line 1204, in _gcd_import 2026-04-17 23:30:07.962 | File "<frozen importlib._bootstrap>", line 1176, in _find_and_load 2026-04-17 23:30:07.962 | File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked 2026-04-17 23:30:07.962 | File "<frozen importlib._bootstrap>", line 690, in _load_unlocked 2026-04-17 23:30:07.962 | File "<frozen importlib._bootstrap_external>", line 940, in exec_module 2026-04-17 23:30:07.962 | File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed 2026-04-17 23:30:07.962 | File "/app/backend/open_webui/main.py", line 60, in <module> 2026-04-17 23:30:07.963 | from open_webui.utils.asgi_middleware import ( 2026-04-17 23:30:07.963 | File "/app/backend/open_webui/utils/asgi_middleware.py", line 44, in <module> 2026-04-17 23:30:07.963 | from open_webui.internal.db import ScopedSession 2026-04-17 23:30:07.963 | File "/app/backend/open_webui/internal/db.py", line 86, in <module> 2026-04-17 23:30:07.963 | handle_peewee_migration(DATABASE_URL) 2026-04-17 23:30:07.963 | File "/app/backend/open_webui/internal/db.py", line 78, in handle_peewee_migration 2026-04-17 23:30:07.963 | if db and not db.is_closed(): 2026-04-17 23:30:07.963 | ^^ ### Additional Information Root cause summary: v0.9 added an async SQLAlchemy + asyncpg code path for runtime queries (open_webui/models/functions.py, etc.) while retaining the peewee + psycopg2 path for migrations (open_webui/internal/db.py:handle_peewee_migration). Both drivers parse `DATABASE_URL` independently, and their query-string conventions for SSL are incompatible (`sslmode` vs `ssl`). No documentation covers the new requirement. Suggested fixes (pick any one, or combine): 1. Document that users with SSL-required Postgres should set `PGSSLMODE=require` as an env var and omit the SSL query string from DATABASE_URL. 2. Normalize the DSN inside both code paths — strip `sslmode` / `ssl` from the URL and inject the right kwarg per driver. 3. Translate `sslmode=<value>` → asyncpg `connect_args={"ssl": <value>}` at the SQLAlchemy engine setup site, so the libpq-style URL keeps working. Secondary cleanup: in `open_webui/internal/db.py:handle_peewee_migration`, initialize `db = None` at the top of the function so the outer `if db and not db.is_closed()` doesn't fail with `UnboundLocalError` when the connect itself raises. Right now the real error is masked by a NameError. Confirmed affected configurations: Azure Database for PostgreSQL Flexible Server (SSL required by default, cannot be disabled). Same failure pattern is expected on AWS RDS, DigitalOcean Managed PG, Supabase, etc., since all require SSL.
GiteaMirror added the bug label 2026-04-20 02:40:31 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#20089