Open WebUI stores users, chats, settings, prompts, API keys, and feature state in its application database. The default local database is convenient for a single container, but shared or scaled deployments need PostgreSQL so writes and migrations are handled by a database server instead of a file inside the data volume.

The database backend is selected when Open WebUI starts. Set DATABASE_URL to a PostgreSQL connection string before the application container is recreated, keep WEBUI_SECRET_KEY stable, and point every replica at the same database. A fresh PostgreSQL database is initialized through the normal migration path during startup.

Docker Compose keeps the database service, application service, persistent volumes, and environment values in one project. The Compose layout starts from an empty PostgreSQL database; an existing SQLite deployment with production data needs a backup and migration plan before the database URL is changed.

Steps to configure PostgreSQL for Open WebUI:

  1. Stop the running Open WebUI service before changing the database backend.
    $ docker compose stop open-webui
    [+] Stopping 1/1
     ✔ Container open-webui-open-webui-1  Stopped

    Open WebUI does not copy existing SQLite data into PostgreSQL just because DATABASE_URL changes. Back up the existing data volume and plan the migration separately when accounts, chats, uploads, or settings already matter.
    Related: How to back up and restore Open WebUI data

  2. Store the PostgreSQL password and Open WebUI secret key in the Compose environment file.
    COMPOSE_PROJECT_NAME=open-webui
    OPEN_WEBUI_PORT=3000
    POSTGRES_PASSWORD=replace-with-a-long-database-password
    WEBUI_SECRET_KEY=replace-with-the-existing-or-new-webui-secret

    Use the existing WEBUI_SECRET_KEY for a deployment that already has users. Changing it can invalidate sessions and break state that depends on the signing key.
    Related: How to set WEBUI_SECRET_KEY in Open WebUI

  3. Add PostgreSQL and DATABASE_URL to the Compose file.
    services:
      postgres:
        image: postgres:16-alpine
        restart: unless-stopped
        environment:
          POSTGRES_DB: openwebui
          POSTGRES_USER: openwebui
          POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}"
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U openwebui -d openwebui"]
          interval: 10s
          timeout: 5s
          retries: 10
        volumes:
          - postgres-data:/var/lib/postgresql/data
     
      open-webui:
        image: ghcr.io/open-webui/open-webui:main
        restart: unless-stopped
        depends_on:
          postgres:
            condition: service_healthy
        ports:
          - "${OPEN_WEBUI_PORT:-3000}:8080"
        environment:
          WEBUI_SECRET_KEY: "${WEBUI_SECRET_KEY:?set WEBUI_SECRET_KEY in .env}"
          DATABASE_URL: "postgresql://openwebui:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@postgres:5432/openwebui"
        volumes:
          - open-webui:/app/backend/data
     
    volumes:
      postgres-data:
      open-webui:

    For a managed PostgreSQL service, omit the local postgres service and replace the host, user, password, port, and database name in DATABASE_URL with the provider values. URL-encode special characters in the username or password.
    Tool: Docker Compose Healthchecks Checker

  4. Confirm that Compose reads both services.
    $ docker compose config --services
    postgres
    open-webui
  5. Start the PostgreSQL service first.
    $ docker compose up -d postgres
    [+] Running 2/2
     ✔ Network open-webui_default       Created
     ✔ Container open-webui-postgres-1  Started
  6. Start Open WebUI after PostgreSQL is healthy.
    $ docker compose up -d open-webui
    [+] Running 2/2
     ✔ Container open-webui-postgres-1    Healthy
     ✔ Container open-webui-open-webui-1  Started

    Start one Open WebUI instance first when several replicas share the same database. Let startup migrations finish before scaling out the remaining instances.

  7. Request the Open WebUI health endpoint through the mapped port.
    $ curl -fsS http://127.0.0.1:3000/health
    {"status":true}
  8. Check the startup log for PostgreSQL migration messages.
    $ docker compose logs open-webui
    open-webui-1  | INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
    open-webui-1  | INFO  [alembic.runtime.migration] Will assume transactional DDL.
    open-webui-1  | INFO  [alembic.runtime.migration] Running upgrade  -> 7e5b5dc7342b
    open-webui-1  | INFO  [alembic.runtime.migration] Running upgrade 7e5b5dc7342b -> ca81bd47c050, Add config table.
    ##### snipped #####

    The PostgresqlImpl line confirms that migrations are running against PostgreSQL instead of the default local database.

  9. List the Open WebUI tables in PostgreSQL.
    $ docker compose exec postgres psql -U openwebui -d openwebui -c '\dt'
                      List of relations
     Schema |          Name           | Type  |   Owner
    --------+-------------------------+-------+-----------
     public | access_grant            | table | openwebui
     public | alembic_version         | table | openwebui
     public | api_key                 | table | openwebui
     public | auth                    | table | openwebui
     public | chat                    | table | openwebui
     public | chat_message            | table | openwebui
     public | config                  | table | openwebui
     public | user                    | table | openwebui
    ##### snipped #####
    (43 rows)

    Tables such as alembic_version, chat, config, and user show that Open WebUI initialized its schema in the PostgreSQL database.