Files
docs/docs/reference/database-schema.md
T
Classic298 fb0cc37033 0.9.3
2026-05-08 22:53:11 +02:00

57 KiB

sidebar_position, title
sidebar_position title
50 Database Schema

:::warning

This tutorial is a community contribution and is not supported by the Open WebUI team. It serves only as a demonstration on how to customize Open WebUI for your specific use case. Want to contribute? Check out the contributing tutorial.

:::

Warning

This documentation reflects schema changes up to Open WebUI v0.9.0.

Open-WebUI Internal SQLite Database

For Open-WebUI, the SQLite database serves as the backbone for user management, chat history, file storage, and various other core functionalities. Understanding this structure is essential for anyone looking to contribute to or maintain the project effectively.

Internal SQLite Location

You can find the SQLite database at root -> data -> webui.db

📁 Root (/)
├── 📁 data
│   ├── 📁 cache
│   ├── 📁 uploads
│   ├── 📁 vector_db
│   └── 📄 webui.db
├── 📄 dev.sh
├── 📁 open_webui
├── 📄 requirements.txt
├── 📄 start.sh
└── 📄 start_windows.bat

Copy Database Locally

If you want to copy the Open-WebUI SQLite database running in the container to your local machine, you can use:

docker cp open-webui:/app/backend/data/webui.db ./webui.db

Alternatively, you can access the database within the container using:

docker exec -it open-webui /bin/sh

Table Overview

Here is a complete list of tables in Open-WebUI's SQLite database. The tables are listed alphabetically and numbered for convenience.

No. Table Name Description
01 access_grant Stores normalized access control grants for all resources
02 auth Stores user authentication credentials and login information
03 calendar Stores user-owned calendars with access control
04 calendar_event Stores calendar events with recurrence (RRULE) support
05 calendar_event_attendee Tracks attendee RSVPs for shared calendar events
06 channel Manages chat channels and their configurations
07 channel_file Links files to channels and messages
08 channel_member Tracks user membership and permissions within channels
09 chat Stores chat sessions and their metadata
10 chat_file Links files to chats and messages
11 chatidtag Maps relationships between chats and their associated tags
12 config Maintains system-wide configuration settings
13 document Stores documents and their metadata for knowledge management
14 feedback Captures user feedback and ratings
15 file Manages uploaded files and their metadata
16 folder Organizes files and content into hierarchical structures
17 function Stores custom functions and their configurations
18 group Manages user groups and their permissions
19 group_member Tracks user membership within groups
20 knowledge Stores knowledge base entries and related information
21 knowledge_file Links files to knowledge bases
22 memory Maintains chat history and context memory
23 message Stores individual chat messages and their content
24 message_reaction Records user reactions (emojis/responses) to messages
25 migrate_history Tracks database schema version and migration records
26 model Manages AI model configurations and settings
27 note Stores user-created notes and annotations
28 oauth_session Manages active OAuth sessions for users
29 prompt Stores templates and configurations for AI prompts
30 prompt_history Tracks version history and snapshots for prompts
31 shared_chat Stores snapshots of shared chats for link sharing
32 skill Stores reusable markdown instruction sets (Skills)
33 tag Manages tags/labels for content categorization
34 tool Stores configurations for system tools and integrations
35 user Maintains user profiles and account information
36 automation Stores user-defined scheduled automations
37 automation_run Stores execution history for automation runs
38 pinned_note Tracks per-user note pins (each row = one user pinning one note)

Note: there are two additional tables in Open-WebUI's SQLite database that are not related to Open-WebUI's core functionality, that have been excluded:

  • Alembic Version table
  • Migrate History table

Now that we have all the tables, let's understand the structure of each table.

Access Grant Table

Column Name Data Type Constraints Description
id Integer PRIMARY KEY, AUTOINCREMENT Unique identifier
resource_type Text NOT NULL Type of resource (e.g., model, knowledge, tool)
resource_id Text NOT NULL ID of the specific resource
principal_type Text NOT NULL Type of grantee: user or group
principal_id Text NOT NULL ID of the user or group (or * for public)
permission Text NOT NULL Permission level: read or write
created_at BigInteger nullable Grant creation timestamp

Things to know about the access_grant table:

  • Unique constraint on (resource_type, resource_id, principal_type, principal_id, permission) to prevent duplicate grants
  • Indexed on (resource_type, resource_id) and (principal_type, principal_id) for efficient lookups
  • Replaces the former access_control JSON column that was previously embedded in each resource table
  • principal_type of user with principal_id of * represents public (open) access
  • Supports both group-level and individual user-level access grants

Auth Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier
email String - User's email
password Text - Hashed password
active Boolean - Account status

Things to know about the auth table:

  • Uses UUID for primary key
  • One-to-One relationship with users table (shared id)

Channel Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text - Owner/creator of channel
type Text nullable Channel type
name Text - Channel name
description Text nullable Channel description
data JSON nullable Flexible data storage
meta JSON nullable Channel metadata

| created_at | BigInteger | - | Creation timestamp (nanoseconds) | | updated_at | BigInteger | - | Last update timestamp (nanoseconds) |

Things to know about the auth table:

  • Uses UUID for primary key
  • Case-insensitive channel names (stored lowercase)

Channel Member Table

Column Name Data Type Constraints Description
id TEXT NOT NULL Unique identifier for the channel membership
channel_id TEXT NOT NULL Reference to the channel
user_id TEXT NOT NULL Reference to the user
created_at BIGINT - Timestamp when membership was created

Channel File Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL Owner of the relationship
channel_id Text FOREIGN KEY(channel.id), NOT NULL Reference to the channel
file_id Text FOREIGN KEY(file.id), NOT NULL Reference to the file
message_id Text FOREIGN KEY(message.id), nullable Reference to associated message
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the channel_file table:

  • Unique constraint on (channel_id, file_id) to prevent duplicate entries
  • Foreign key relationships with CASCADE delete
  • Indexed on channel_id, file_id, and user_id for performance

Chat Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier (UUID)
user_id String - Owner of the chat
title Text - Chat title
chat JSON - Chat content and history
created_at BigInteger - Creation timestamp
updated_at BigInteger - Last update timestamp
share_id Text UNIQUE, nullable Sharing identifier
archived Boolean default=False Archive status
pinned Boolean default=False, nullable Pin status
meta JSON server_default="{}" Metadata including tags
folder_id Text nullable Parent folder ID
tasks JSON nullable Chat-level task/todo list used by agentic workflows
summary Text nullable Optional chat summary text
last_read_at BigInteger nullable Last read timestamp used for unread indicators

Things to know about the chat table:

  • tasks and summary support structured planning/status UX in chat sessions.
  • last_read_at is used by sidebar unread state logic (compare with updated_at).
  • share_id references the shared_chat.id token when the chat has an active share link.

Shared Chat Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Share token (UUID) used in /s/{id} URLs
chat_id Text FOREIGN KEY(chat.id) CASCADE, NOT NULL Reference to the original chat
user_id Text NOT NULL User who created the share
title Text nullable Chat title at time of sharing
chat JSON nullable Snapshot of chat content at share time
created_at BigInteger nullable Share creation timestamp
updated_at BigInteger nullable Last re-snapshot timestamp

Things to know about the shared_chat table:

  • Replaces the previous pattern of storing shared chat snapshots as phantom rows in the chat table with user_id set to shared-{chat_id}.
  • Each row is an immutable snapshot of the original chat at the time of sharing (or last re-share). The snapshot is updated when the user clicks "Update and Copy Link".
  • Deleting the original chat cascades to delete the shared snapshot.
  • Access control for shared chats is managed via the access_grant table with resource_type = 'shared_chat'.

Automation Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL Owner of the automation
name Text NOT NULL Automation display name
data JSON NOT NULL Automation payload (prompt, model_id, rrule, optional terminal config)
meta JSON nullable Optional metadata
is_active Boolean NOT NULL, default=True Active/paused state
last_run_at BigInteger nullable Last execution time
next_run_at BigInteger nullable Next scheduled execution time
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the automation table:

  • next_run_at is indexed for efficient due-run polling.
  • data.rrule defines recurrence and drives scheduler calculations.

Automation Run Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
automation_id Text NOT NULL Reference to automation
chat_id Text nullable Chat created by this run (if available)
status Text NOT NULL Run status (success / error)
error Text nullable Error details when status is error
created_at BigInteger NOT NULL Execution record timestamp

Things to know about the automation_run table:

  • Indexed by automation_id for fast per-automation run history queries.
  • Rows are deleted when an automation is deleted.

Calendar Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL Owner of the calendar
name Text NOT NULL Calendar display name
color Text nullable Display color (hex, e.g. #3b82f6)
is_default Boolean NOT NULL, default=False Whether this is the user's default calendar
data JSON nullable Extensible data payload
meta JSON nullable Optional metadata
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the calendar table:

  • Indexed on user_id for efficient per-user calendar listing.
  • A default "Personal" calendar is auto-created on first access.
  • The "Scheduled Tasks" calendar is virtual — it is not stored in this table. Instead, the API synthesizes it at response time (with constant ID __scheduled_tasks__) for users who have Automations access. Automation RRULE future runs and past execution records are rendered as virtual events on this calendar.
  • Access control is managed via the access_grant table with resource_type = 'calendar', enabling calendar sharing between users and groups.
  • A user can only delete non-default calendars. Deleting a calendar cascades to all its events, attendees, and access grants.

Calendar Event Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
calendar_id Text NOT NULL Reference to parent calendar
user_id Text NOT NULL User who created the event
title Text NOT NULL Event title
description Text nullable Event description
start_at BigInteger NOT NULL Start time (epoch nanoseconds)
end_at BigInteger nullable End time (epoch nanoseconds)
all_day Boolean NOT NULL, default=False Whether this is an all-day event
rrule Text nullable iCalendar RRULE for recurrence
color Text nullable Per-event color override
location Text nullable Event location
data JSON nullable Extensible data payload
meta JSON nullable Optional metadata (e.g. automation_id)
is_cancelled Boolean NOT NULL, default=False Soft-cancel flag
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the calendar_event table:

  • Composite index on (calendar_id, start_at) for efficient range queries within a calendar.
  • Composite index on (user_id, start_at) for efficient per-user date range queries.
  • Recurring events store an rrule string and are expanded into individual instances at query time (server-side Python expansion using dateutil).
  • Cancelled events (is_cancelled = True) are excluded from range queries but retained in the database.

Calendar Event Attendee Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
event_id Text NOT NULL Reference to the calendar event
user_id Text NOT NULL User invited to the event
status Text NOT NULL, default='pending' RSVP status: pending, accepted, declined, tentative
meta JSON nullable Optional metadata
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the calendar_event_attendee table:

  • Unique constraint on (event_id, user_id) to prevent duplicate attendee entries.
  • Indexed on (user_id, status) for efficient lookups of events a user is invited to.
  • Attendees are replaced in bulk when an event is updated with a new attendee list.
  • Deleting an event cascades to delete all attendee records.

Chat File Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL User associated with the file
chat_id Text FOREIGN KEY(chat.id), NOT NULL Reference to the chat
file_id Text FOREIGN KEY(file.id), NOT NULL Reference to the file
message_id Text nullable Reference to associated message
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the chat_file table:

  • Unique constraint on (chat_id, file_id) to prevent duplicate entries
  • Foreign key relationships with CASCADE delete
  • Indexed on chat_id, file_id, message_id, and user_id for performance

Why this table was added:

  • Query Efficiency: Before this, files were embedded in message objects. This table allows direct indexed lookups for finding all files in a chat without iterating through every message.
  • Data Consistency: Acts as a single source of truth for file associations. In multi-node deployments, all nodes query this table instead of relying on potentially inconsistent embedded data.
  • Deduplication: The database-level unique constraint prevents duplicate file associations, which is more reliable than application-level checks.

Chat ID Tag Table

Column Name Data Type Constraints Description
id VARCHAR(255) NOT NULL Unique identifier
tag_name VARCHAR(255) NOT NULL Name of the tag
chat_id VARCHAR(255) NOT NULL Reference to chat
user_id VARCHAR(255) NOT NULL Reference to user
timestamp INTEGER NOT NULL Creation timestamp

Config

Column Name Data Type Constraints Default Description
id INTEGER NOT NULL - Primary key identifier
data JSON NOT NULL - Configuration data
version INTEGER NOT NULL - Config version number
created_at DATETIME NOT NULL CURRENT_TIMESTAMP Creation timestamp
updated_at DATETIME - CURRENT_TIMESTAMP Last update timestamp

Feedback Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text - User who provided feedback
version BigInteger default=0 Feedback version number
type Text - Type of feedback
data JSON nullable Feedback data including ratings
meta JSON nullable Metadata (arena, chat_id, etc)
snapshot JSON nullable Associated chat snapshot
created_at BigInteger - Creation timestamp
updated_at BigInteger - Last update timestamp

File Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier
user_id String - Owner of the file
hash Text nullable File hash/checksum
filename Text - Name of the file
path Text nullable File system path
data JSON nullable File-related data
meta JSON nullable File metadata

| created_at | BigInteger | - | Creation timestamp | | updated_at | BigInteger | - | Last update timestamp |

The meta field's expected structure:

{
    "name": string,          # Optional display name
    "content_type": string,  # MIME type
    "size": integer,         # File size in bytes
    # Additional metadata supported via ConfigDict(extra="allow")
}

Folder Table

Column Name Data Type Constraints Description
id Text PK (composite) Unique identifier (UUID)
parent_id Text nullable Parent folder ID for hierarchy
user_id Text PK (composite) Owner of the folder
name Text - Folder name
items JSON nullable Folder contents
data JSON nullable Additional folder data
meta JSON nullable Folder metadata
is_expanded Boolean default=False UI expansion state
created_at BigInteger - Creation timestamp
updated_at BigInteger - Last update timestamp

Things to know about the folder table:

  • Primary key is composite (id, user_id)
  • Folders can be nested (parent_id reference)
  • Root folders have null parent_id
  • Folder names must be unique within the same parent

Function Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier
user_id String - Owner of the function
name Text - Function name
type Text - Function type
content Text - Function content/code
meta JSON - Function metadata
valves JSON - Function control settings
is_active Boolean - Function active status
is_global Boolean - Global availability flag
created_at BigInteger - Creation timestamp
updated_at BigInteger - Last update timestamp

Things to know about the function table:

  • type can only be: ["filter", "action"]

Group Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY, UNIQUE Unique identifier (UUID)
user_id Text - Group owner/creator
name Text - Group name
description Text - Group description
data JSON nullable Additional group data
meta JSON nullable Group metadata
permissions JSON nullable Permission configuration
created_at BigInteger - Creation timestamp
updated_at BigInteger - Last update timestamp

Note: The user_ids column has been migrated to the group_member table.

Group Member Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY, UNIQUE Unique identifier (UUID)
group_id Text FOREIGN KEY(group.id), NOT NULL Reference to the group
user_id Text FOREIGN KEY(user.id), NOT NULL Reference to the user
created_at BigInteger nullable Creation timestamp
updated_at BigInteger nullable Last update timestamp

Things to know about the group_member table:

  • Unique constraint on (group_id, user_id) to prevent duplicate memberships
  • Foreign key relationships with CASCADE delete to group and user tables

Knowledge Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY, UNIQUE Unique identifier (UUID)
user_id Text - Knowledge base owner
name Text - Knowledge base name
description Text - Knowledge base description
data JSON nullable Knowledge base content
meta JSON nullable Additional metadata

| created_at | BigInteger | - | Creation timestamp | | updated_at | BigInteger | - | Last update timestamp |

Knowledge File Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL Owner of the relationship
knowledge_id Text FOREIGN KEY(knowledge.id), NOT NULL Reference to the knowledge base
file_id Text FOREIGN KEY(file.id), NOT NULL Reference to the file
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the knowledge_file table:

  • Unique constraint on (knowledge_id, file_id) to prevent duplicate entries
  • Foreign key relationships with CASCADE delete
  • Indexed on knowledge_id, file_id, and user_id for performance

Access control for resources (models, knowledge bases, tools, prompts, notes, files, channels) is managed through the access_grant table rather than embedded JSON. Each grant entry specifies a resource, a principal (user or group), and a permission level (read or write). See the Access Grant Table section above for details.

Memory Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier (UUID)
user_id String - Memory owner
content Text - Memory content
created_at BigInteger - Creation timestamp
updated_at BigInteger - Last update timestamp

Message Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text - Message author
channel_id Text nullable Associated channel
parent_id Text nullable Parent message for threads
content Text - Message content
data JSON nullable Additional message data
meta JSON nullable Message metadata
created_at BigInteger - Creation timestamp (nanoseconds)
updated_at BigInteger - Last update timestamp (nanoseconds)

Message Reaction Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text - User who reacted
message_id Text - Associated message
name Text - Reaction name/emoji
created_at BigInteger - Reaction timestamp

Model Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Model identifier
user_id Text - Model owner
base_model_id Text nullable Parent model reference
name Text - Display name
params JSON - Model parameters
meta JSON - Model metadata

| is_active | Boolean | default=True | Active status | | created_at | BigInteger | - | Creation timestamp | | updated_at | BigInteger | - | Last update timestamp |

Note Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier
user_id Text nullable Owner of the note
title Text nullable Note title
data JSON nullable Note content and data
meta JSON nullable Note metadata
created_at BigInteger nullable Creation timestamp
updated_at BigInteger nullable Last update timestamp

Pin state is no longer stored on this table — the legacy is_pinned column was removed in migration 4de81c2a3af1 and replaced by a per-user Pinned Note Table. Pre-existing pins were backfilled to the note owner; the API surfaces is_pinned as a per-request join against the calling user's rows.

Pinned Note Table

Per-user note pins. Each row records that one user has pinned one note for their own sidebar — pinning is private and does not affect any other user with access to the same note.

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL The user who pinned the note
note_id Text NOT NULL, FOREIGN KEY(note.id) ON DELETE CASCADE The pinned note
created_at BigInteger NOT NULL Pin creation timestamp (used for ordering)

A UNIQUE(user_id, note_id) constraint prevents duplicate pins for the same user/note pair. The pinned-note list is ordered by created_at DESC per user, so the most recently pinned note appears first. Deleting a note cascades through this table; toggling a pin does not modify note.updated_at.

OAuth Session Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique session identifier
user_id Text FOREIGN KEY(user.id) Associated user
provider Text - OAuth provider (e.g., 'google')
token Text - OAuth session token
expires_at BigInteger - Token expiration timestamp
created_at BigInteger - Session creation timestamp
updated_at BigInteger - Session last update timestamp

Prompt Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
command String UNIQUE, INDEX Unique command identifier
user_id String NOT NULL Owner of the prompt
name Text NOT NULL Display name of the prompt
content Text NOT NULL Prompt content/template
data JSON nullable Additional prompt data
meta JSON nullable Prompt metadata

| is_active | Boolean | default=True | Active status | | version_id | Text | nullable | Current version identifier | | tags | JSON | nullable | Associated tags | | created_at | BigInteger | NOT NULL | Creation timestamp | | updated_at | BigInteger | NOT NULL | Last update timestamp |

Prompt History Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
prompt_id Text FOREIGN KEY(prompt.id), INDEX Reference to the prompt
parent_id Text nullable Reference to the parent version
snapshot JSON NOT NULL Snapshot of the prompt at version
user_id Text NOT NULL User who created the version
commit_message Text nullable Version commit message
created_at BigInteger NOT NULL Creation timestamp

Skill Table

Column Name Data Type Constraints Description
id Text PRIMARY KEY Unique identifier (UUID)
user_id Text NOT NULL Owner/creator of the skill
name Text NOT NULL Display name of the skill
description Text nullable Short description (used in manifest)
content Text NOT NULL Full skill instructions (Markdown)
data JSON nullable Additional skill data
meta JSON nullable Skill metadata
is_active Boolean default=True Active status
created_at BigInteger NOT NULL Creation timestamp
updated_at BigInteger NOT NULL Last update timestamp

Things to know about the skill table:

  • Uses UUID for primary key
  • Access control is managed through the access_grant table (resource_type skill)
  • description is injected into the system prompt as part of the manifest; content is loaded on-demand via the view_skill builtin tool

Tag Table

Column Name Data Type Constraints Description
id String PK (composite) Normalized tag identifier
name String - Display name
user_id String PK (composite) Tag owner
meta JSON nullable Tag metadata

Things to know about the tag table:

  • Primary key is composite (id, user_id)

Tool Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier
user_id String - Tool owner
name Text - Tool name
content Text - Tool content/code
specs JSON - Tool specifications
meta JSON - Tool metadata
valves JSON - Tool control settings

| created_at | BigInteger | - | Creation timestamp | | updated_at | BigInteger | - | Last update timestamp |

User Table

Column Name Data Type Constraints Description
id String PRIMARY KEY Unique identifier
username String(50) nullable User's unique username
name String - User's name
email String - User's email
role String - User's role
profile_image_url Text - Profile image path
bio Text nullable User's biography
gender Text nullable User's gender
date_of_birth Date nullable User's date of birth
last_active_at BigInteger - Last activity timestamp
updated_at BigInteger - Last update timestamp
created_at BigInteger - Creation timestamp
api_key String UNIQUE, nullable API authentication key
settings JSON nullable User preferences
info JSON nullable Additional user info
oauth_sub Text UNIQUE OAuth subject identifier
scim JSON nullable SCIM provisioning data

Things to know about the user table:

  • Uses UUID for primary key
  • One-to-One relationship with auth table (shared id)
  • One-to-One relationship with oauth_session table (via user_id foreign key)

The scim field's expected structure:

{
    "<provider>": {
        "external_id": string,  # externalId from the identity provider
    },
    # Multiple providers can be stored simultaneously
    # Example:
    # "microsoft": { "external_id": "abc-123" },
    # "okta": { "external_id": "def-456" }
}

Why this column was added:

  • SCIM account linking: Stores per-provider externalId values from SCIM provisioning, enabling identity providers (like Azure AD, Okta) to match users by their external identifiers rather than relying solely on email.
  • Multi-provider support: The per-provider key structure allows a single user to be provisioned from multiple identity providers simultaneously, each storing their own externalId.
  • OAuth fallback: When looking up a user by externalId, the system falls back to matching against oauth_sub if no scim entry is found, enabling seamless linking of SCIM-provisioned and OAuth-authenticated accounts.

Entity Relationship Diagram

To help visualize the relationship between the tables, refer to the below Entity Relationship Diagram (ERD) generated with Mermaid.

erDiagram
    %% User and Authentication
    user ||--o{ auth : "has"
    user ||--o{ chat : "owns"
    user ||--o{ shared_chat : "shares"
    user ||--o{ calendar : "owns"
    user ||--o{ calendar_event : "creates"
    user ||--o{ channel : "owns"
    user ||--o{ message : "creates"
    user ||--o{ folder : "owns"
    user ||--o{ file : "owns"
    user ||--o{ feedback : "provides"
    user ||--o{ function : "manages"
    user ||--o{ group : "manages"
    user ||--o{ knowledge : "manages"
    user ||--o{ memory : "owns"
    user ||--o{ model : "manages"
    user ||--o{ prompt : "creates"
    user ||--o{ prompt_history : "creates"
    prompt ||--o{ prompt_history : "has"
    user ||--o{ tag : "creates"
    user ||--o{ skill : "manages"
    user ||--o{ tool : "manages"
    user ||--o{ note : "owns"
    user ||--o{ pinned_note : "pins"
    note ||--o{ pinned_note : "pinned_by"
    user ||--|| oauth_session : "has"

    %% Content Relationships
    message ||--o{ message_reaction : "has"
    chat ||--o{ tag : "tagged_with"
    chat ||--o{ shared_chat : "shared_via"
    chat }|--|| folder : "organized_in"
    calendar ||--o{ calendar_event : "contains"
    calendar_event ||--o{ calendar_event_attendee : "has"
    channel ||--o{ message : "contains"
    message ||--o{ message : "replies"

    user {
        string id PK
        string username
        string name
        string email
        string role
        text profile_image_url
        text bio
        text gender
        date date_of_birth
        bigint last_active_at
        string api_key
        json settings
        json info
        text oauth_sub
        json scim
    }

    auth {
        string id PK
        string email
        text password
        boolean active
    }

    chat {
        string id PK
        string user_id FK
        string title
        json chat
        text share_id
        boolean archived
        boolean pinned
        json meta
        text folder_id FK
    }

    shared_chat {
        text id PK
        text chat_id FK
        text user_id FK
        text title
        json chat
        bigint created_at
        bigint updated_at
    }

    calendar {
        text id PK
        text user_id FK
        text name
        text color
        boolean is_default
        json data
        json meta
    }

    calendar_event {
        text id PK
        text calendar_id FK
        text user_id FK
        text title
        text description
        bigint start_at
        bigint end_at
        boolean all_day
        text rrule
        text color
        text location
        json data
        json meta
        boolean is_cancelled
    }

    calendar_event_attendee {
        text id PK
        text event_id FK
        text user_id FK
        text status
        json meta
    }

    channel {
        text id PK
        text user_id FK
        text name
        text description
        json data
        json meta
        json access_control
    }

    message {
        text id PK
        text user_id FK
        text channel_id FK
        text parent_id FK
        text content
        json data
        json meta
    }

    message_reaction {
        text id PK
        text user_id FK
        text message_id FK
        text name
    }

    feedback {
        text id PK
        text user_id FK
        bigint version
        text type
        json data
        json meta
        json snapshot
    }

    file {
        string id PK
        string user_id FK
        text hash
        text filename
        text path
        json data
        json meta
        json access_control
    }

    folder {
        text id PK "composite"
        text user_id PK "composite"
        text parent_id FK
        text name
        json items
        json data
        json meta
        boolean is_expanded
    }

    function {
        string id PK
        string user_id FK
        text name
        text content
        json meta
        json valves
        boolean is_active
        boolean is_global
    }

    group {
        text id PK
        text user_id FK
        text name
        text description
        json data
        json meta
        json permissions
        json user_ids
    }

    knowledge {
        text id PK
        text user_id FK
        text name
        text description
        json data
        json meta
        json access_control
    }

    memory {
        string id PK
        string user_id FK
        text content
    }

    model {
        text id PK
        text user_id FK
        text base_model_id FK
        text name
        json params
        json meta
        json access_control
        boolean is_active
    }

    note {
        text id PK
        text user_id FK
        text title
        json data
        json meta
        json access_control
    }

    pinned_note {
        text id PK
        text user_id FK
        text note_id FK
        bigint created_at
    }

    oauth_session {
        text id PK
        text user_id FK
        text provider
        text token
        bigint expires_at
    }

    prompt {
        text id PK
        string command
        string user_id FK
        text name
        text content
        json data
        json meta
        json access_control
        boolean is_active
        text version_id
        json tags
    }

    prompt_history {
        text id PK
        text prompt_id FK
        text parent_id FK
        json snapshot
        text user_id FK
        text commit_message
    }

    tag {
        string id PK "composite"
        string user_id PK "composite"
        string name
        json meta
    }

    skill {
        text id PK
        text user_id FK
        text name
        text description
        text content
        json data
        json meta
        boolean is_active
    }

    tool {
        string id PK
        string user_id FK
        text name
        text content
        json specs
        json meta
        json valves
        json access_control
    }

Database Encryption with SQLCipher

For enhanced security, Open WebUI supports at-rest encryption for its primary SQLite database using SQLCipher. This is recommended for deployments handling sensitive data where using a larger database like PostgreSQL is not needed.

Prerequisites

SQLCipher encryption requires additional dependencies that are not included by default. Before using this feature, you must install:

  • The SQLCipher system library (e.g., libsqlcipher-dev on Debian/Ubuntu, sqlcipher on macOS via Homebrew)
  • The sqlcipher3-wheels Python package (pip install sqlcipher3-wheels)

For Docker users, this means building a custom image with these dependencies included.

Configuration

To enable encryption, set the following environment variables:

# Required: Set the database type to use SQLCipher
DATABASE_TYPE=sqlite+sqlcipher

# Required: Set a secure password for database encryption
DATABASE_PASSWORD=your-secure-password

When these are set and a full DATABASE_URL is not explicitly defined, Open WebUI will automatically create and use an encrypted database file at ./data/webui.db.

Important Notes

:::danger

  • The DATABASE_PASSWORD environment variable is required when using sqlite+sqlcipher.
  • The DATABASE_TYPE variable tells Open WebUI which connection logic to use. Setting it to sqlite+sqlcipher activates the encryption feature.
  • Keep the password secure, as it is needed to decrypt and access all application data.
  • Losing the password means losing access to all data in the encrypted database.

:::

:::warning Migrating Existing Data to SQLCipher

Open WebUI does not support automatic migration from an unencrypted SQLite database to an encrypted SQLCipher database. If you enable SQLCipher on an existing installation, the application will fail to read your existing unencrypted data.

To use SQLCipher with existing data, you must either:

  1. Start fresh - Enable SQLCipher on a new installation and have users export/re-import their chats manually
  2. Manual database migration - Use external SQLite/SQLCipher tools to export data from the unencrypted database and import it into a new encrypted database (advanced users only)
  3. Use filesystem-level encryption - Consider alternatives like LUKS (Linux) or BitLocker (Windows) for at-rest encryption without database-level changes
  4. Switch to PostgreSQL - For multi-user deployments, PostgreSQL with TLS provides encryption in transit and can be combined with encrypted storage

:::

Variable Default Description
DATABASE_TYPE None Set to sqlite+sqlcipher for encrypted SQLite
DATABASE_PASSWORD - Encryption password (required for SQLCipher)
DATABASE_ENABLE_SQLITE_WAL False Enable Write-Ahead Logging for better performance
DATABASE_SQLITE_PRAGMA_SYNCHRONOUS NORMAL SQLite sync mode (safe with WAL, avoids fsync per txn)
DATABASE_SQLITE_PRAGMA_BUSY_TIMEOUT 5000 Write-lock wait time in milliseconds
DATABASE_SQLITE_PRAGMA_CACHE_SIZE -65536 Page cache size (negative = KiB; ≈ 64 MB)
DATABASE_SQLITE_PRAGMA_TEMP_STORE MEMORY Temp table storage (MEMORY keeps temps in RAM)
DATABASE_SQLITE_PRAGMA_MMAP_SIZE 268435456 Memory-mapped I/O size in bytes (≈ 256 MB)
DATABASE_SQLITE_PRAGMA_JOURNAL_SIZE_LIMIT 67108864 Max WAL file size after checkpoint (≈ 64 MB)
DATABASE_POOL_SIZE None Database connection pool size
DATABASE_POOL_TIMEOUT 30 Pool connection timeout in seconds
DATABASE_POOL_RECYCLE 3600 Pool connection recycle time in seconds

For more details, see the Environment Variable Configuration documentation.