Database Schema
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.11.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.batCopy 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.dbAlternatively, you can access the database within the container using:
docker exec -it open-webui /bin/shTable 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 | Legacy. Pre-Knowledge documents table; data migrated to knowledge and no longer used (see note below) |
| 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) |
| 39 | chat_message | Normalized per-message store for chat conversations |
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
Note on the document table: it is a legacy table from before the Knowledge feature. Its rows were migrated into the knowledge table (migration 6a39f3d8e55c) and nothing writes to it anymore, but no migration drops it, so it may still be present (empty) in databases that predate the Knowledge feature. There is no backing model for it in current code.
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, group or anyone |
| 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_controlJSON column that was previously embedded in each resource table principal_typeofuserwithprincipal_idof*represents public access, meaning every signed-in user. It does not reach visitors who are not logged inprincipal_typeofanyone(added in v0.11.0) is the no-sign-in grant behind open share links. It is only ever stored asanyone/*/read, any other combination is rejected, and it is only honoured for theshared_chatresource type. Every other resource strips it- Supports both group-level and individual user-level access grants
Auth Table
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | String | PRIMARY KEY | Unique identifier |
| 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
userstable (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, anduser_idfor 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 |
| current_message_id | Text | nullable | Current (active leaf) message of the chat's history |
| variables | JSON | nullable | Values filled in for the model's chat variables |
Things to know about the chat table:
tasksandsummarysupport structured planning/status UX in chat sessions.last_read_atis used by sidebar unread state logic (compare withupdated_at).share_idreferences theshared_chat.idtoken when the chat has an active share link.current_message_idwas added in v0.11.0 (migration9a1b2c3d4e5f). It records the chat's current message, the leaf of the active branch that a new reply continues from, and is backfilled from the existing history when the migration runs. Context compaction and context-usage resolution read it so they work on the branch actually in play rather than the whole message tree.variableswas added in v0.11.0 (migrationc49178636c78). It holds the values a user filled in for the chat variables declared by the model's system prompt, as a flat map keyed by variable name, and is copied along when a chat is forked or cloned. Temporary chats keep their values in the request instead, so nothing is stored.- A migration (
242a2047eae0) adds anold_chatcolumn (Text) that backs up the original JSONchatblob as text. It is a migration safety net, not part of the active model, and is not read at runtime.
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
chattable withuser_idset toshared-{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_granttable withresource_type = 'shared_chat'.
Chat Message Table
The chat_message table is the normalized per-message store for chat conversations: one row per message, separate from the JSON history blob in chat.chat and distinct from the channel Message Table (which holds channel/thread messages, not chat-model turns).
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | Text | PRIMARY KEY | Unique identifier (UUID) |
| chat_id | Text | FOREIGN KEY(chat.id) CASCADE, NOT NULL | Parent chat |
| user_id | Text | indexed | Author of the message |
| role | Text | NOT NULL | Message role: user, assistant, or system |
| parent_id | Text | nullable | Parent message id (for branched conversations) |
| content | JSON | nullable | Message content (a string or a list of content blocks) |
| output | JSON | nullable | Generated output payload |
| model_id | Text | nullable, indexed | Model that produced the message |
| files | JSON | nullable | Attached files |
| sources | JSON | nullable | Retrieval/citation sources |
| embeds | JSON | nullable | Embedded artifacts |
| meta | JSON | nullable | Message metadata; marks internal sub-agent and timer messages (added in v0.11.0) |
| done | Boolean | default=True | Whether generation completed |
| status_history | JSON | nullable | Streamed status updates during generation |
| error | JSON | nullable | Error payload when generation failed |
| usage | JSON | nullable | Token/usage statistics |
| context_summary | Text | nullable | Per-message context summary (added in v0.10.0) |
| created_at | BigInteger | indexed | Creation timestamp |
| updated_at | BigInteger | - | Last update timestamp |
Things to know about the chat_message table:
- Deleting a chat cascades to delete its messages (
chat_idforeign key withON DELETE CASCADE). - Composite indexes back the common access patterns: (
chat_id,parent_id), (model_id,created_at), and (user_id,created_at). context_summarywas added in v0.10.0 (migration4c5ce3d2f27f) to store a summary of the message's context.metawas added in v0.11.0 (migration856c5b02fb54). It carries per-message metadata and is what marks the messages Open WebUI injects on a user's behalf, such as a sub-agent result or a fired timer, so the interface can render them differently from a message the user typed.
Automation Table
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | Text | PRIMARY KEY | Unique identifier (UUID) |
| user_id | Text | NOT NULL | Owner of the automation |
| folder_id | Text | nullable | Folder the runs' chats are created in |
| 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_atis indexed for efficient due-run polling.data.rruledefines recurrence and drives scheduler calculations.folder_idwas added in v0.11.0 (migration959eaac8f909) together with a (user_id,folder_id) index, so an owner's automations can be listed per folder. It is not a foreign key: deleting a folder clears the column on that owner's automations instead of deleting the automation, and a run whose folder has disappeared in the meantime clears the column and files its chat outside any folder.
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_idfor 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_idfor 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_granttable withresource_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.