Skip to main content

Database Migration

Overview

Open WebUI automatically runs database migrations on startup. Manual migration is rarely needed and should only be performed in specific failure scenarios or maintenance situations.

When Manual Migration is Required

You need manual migration only if:

  • Open WebUI logs show specific migration errors during startup
  • You're performing offline database maintenance
  • Automatic migration fails after a version upgrade
  • You're migrating between database types (SQLite ↔ PostgreSQL)
  • A developer has instructed you to run migrations manually
Quick Fix: Migration Errors After Upgrading

"No such table" — Your migrations didn't apply. Enter your container, set the required environment variables (see Step 2), and run alembic upgrade head. See details.

"Table already exists" — A previous migration partially completed. You need to stamp the partially-applied migration and then upgrade. See details.

Multiple errors after a major version jump (e.g., "duplicate column" then "table already exists" then "no such column") — Your database is partially migrated across several migrations. You need to step through them one at a time. See details.

Critical Warning

Manual migration can corrupt your database if performed incorrectly. Always create a verified backup before proceeding.

Prerequisites Checklist

Before starting, ensure you have:

  • Root/admin access to your Open WebUI installation
  • Database location confirmed (default: /app/backend/data/webui.db in Docker)
  • Open WebUI completely stopped (no running processes)
  • Backup created and verified (see below)
  • Access to container or Python environment where Open WebUI runs
Stop All Processes First

Database migrations cannot run while Open WebUI is active. You must stop all Open WebUI processes before attempting manual migration.

Step 1: Create and Verify Backup

Backup Your Database

Terminal
# Find your database location first
docker inspect open-webui | grep -A 5 Mounts

# Create timestamped backup
cp /path/to/webui.db /path/to/webui.db.backup.$(date +%Y%m%d_%H%M%S)

Verify Backup Integrity

Critical: Test that your backup is readable before proceeding.

Terminal - Verify Backup
# Test backup can be opened
sqlite3 /path/to/webui.db.backup "SELECT count(*) FROM user;"

# Verify schema matches
sqlite3 /path/to/webui.db ".schema" > current-schema.sql
sqlite3 /path/to/webui.db.backup ".schema" > backup-schema.sql
diff current-schema.sql backup-schema.sql
Backup Storage

Store backups on a different disk or volume than your database to protect against disk failure.

Step 2: Diagnose Current State

Before attempting any fixes, gather information about your database state.

Access Your Environment

Terminal
# Stop Open WebUI first
docker stop open-webui

# Enter container for diagnostics
docker run --rm -it \
-v open-webui:/app/backend/data \
--entrypoint /bin/bash \
ghcr.io/open-webui/open-webui:main
Verify Your Location

Check where you are after entering the container:

pwd

The Alembic configuration is at /app/backend/open_webui/alembic.ini. Navigate there regardless of your starting directory.

Navigate to the directory containing alembic.ini and configure required environment variables:

Terminal - Navigate and Configure Environment
# First, verify where you are
pwd

# Navigate to Alembic directory (adjust path if your pwd is different)
cd /app/backend/open_webui # Docker
# OR
cd /path/to/open-webui/backend/open_webui # Local

# Verify alembic.ini exists in current directory
ls -la alembic.ini

Set Required Environment Variables

Terminal - Set Environment Variables (Docker)
# Required: Database URL
# For SQLite (4 slashes for absolute path)
export DATABASE_URL="sqlite:////app/backend/data/webui.db"

# For PostgreSQL
export DATABASE_URL="postgresql://user:password@localhost:5432/open_webui_db"

# Required: WEBUI_SECRET_KEY
# Get from existing file in backend directory (NOT data directory)
export WEBUI_SECRET_KEY=$(cat /app/backend/.webui_secret_key)

# If that fails, try the data directory location
# export WEBUI_SECRET_KEY=$(cat /app/backend/data/.webui_secret_key)

# If neither file exists, generate one
# export WEBUI_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
# echo $WEBUI_SECRET_KEY > /app/backend/.webui_secret_key

# Verify both are set
echo "DATABASE_URL: $DATABASE_URL"
echo "WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:0:10}..."
Both Variables Required

Alembic commands will fail with Required environment variable not found if WEBUI_SECRET_KEY is missing. Open WebUI's code imports env.py which validates this variable exists before Alembic can even connect to the database.

Path Syntax for SQLite
  • sqlite:////app/... = 4 slashes total (absolute path: sqlite:// + / + /app/...)
  • sqlite:///../data/... = 3 slashes total (relative path)

Run Diagnostic Commands

Execute these read-only diagnostic commands:

Terminal - Diagnostics (Safe - Read Only)
# Check current migration version
alembic current -v

# Check target (latest) version
alembic heads

# List all migration history
alembic history

# Show pending migrations (what would be applied)
alembic upgrade head --sql | head -30

# Check for branching (indicates issues)
alembic branches

Expected output:

# alembic current should show something like:
ae1027a6acf (head)

# If you see multiple heads or branching, your migration history has issues
Understanding Output
  • alembic current = what version your database thinks it's at
  • alembic heads = what version the code expects
  • alembic upgrade head --sql = preview SQL that would be executed (doesn't apply changes)
  • If current is older than heads, you have pending migrations
  • If current equals heads, your database is up-to-date
Check Actual Database Tables

Verify what's actually in your database:

Terminal
sqlite3 /app/backend/data/webui.db ".tables"
sqlite3 /app/backend/data/webui.db "SELECT * FROM alembic_version;"

Step 3: Apply Migrations

Standard Upgrade (Most Common)

If diagnostics show you have pending migrations (current < heads), upgrade to latest:

Terminal - Upgrade to Latest
# Ensure you're in the correct directory
cd /app/backend/open_webui

# Run upgrade
alembic upgrade head

Watch for these outputs:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade abc123 -> def456, add_new_column
"Will assume non-transactional DDL"

This is a normal informational message for SQLite, not an error. SQLite doesn't support rollback of schema changes, so migrations run without transaction protection.

If the process appears to hang after this message, wait 2-3 minutes - some migrations take time, especially:

  • Migrations that add indexes to large tables (1M+ rows: 1-5 minutes)
  • Migrations with data transformations (100K+ rows: 30 seconds to several minutes)
  • Migrations that rebuild tables (SQLite doesn't support all ALTER operations)

For very large databases (10M+ rows), consider running migrations during a maintenance window and monitoring progress with sqlite3 /path/to/webui.db ".tables" in another terminal.

Upgrade to Specific Version

If you need to apply migrations up to a specific point:

Terminal - Upgrade to Specific Version
# List available versions first
alembic history

# Upgrade to specific revision
alembic upgrade ae1027a6acf

Downgrade (Rollback)

Data Loss Risk

Downgrading can cause permanent data loss if the migration removed columns or tables. Only downgrade if you understand the consequences.

Terminal - Downgrade Migrations
# Downgrade one version
alembic downgrade -1

# Downgrade to specific version
alembic downgrade <revision_id>

# Nuclear option: Remove all migrations (rarely needed)
alembic downgrade base

Step 4: Verify Migration Success

After running migrations, confirm everything is correct:

Terminal - Post-Migration Verification
# Verify current version matches expected
alembic current
# Should show (head) indicating you're at latest
# Example: ae1027a6acf (head)

# Confirm no pending migrations
alembic upgrade head --sql | head -20
# If output contains only comments or is empty, you're up to date

# Verify key tables exist (SQLite)
sqlite3 /app/backend/data/webui.db ".tables" | grep -E "user|chat|model"
# Should show user, chat, model tables among others

# Test a simple query to ensure schema is intact
sqlite3 /app/backend/data/webui.db "SELECT COUNT(*) FROM user;"
# Should return a number, not an error

Test Application Startup

Terminal
# Exit the diagnostic container
exit

# Start Open WebUI normally
docker start open-webui

# Watch logs for migration confirmation
docker logs -f open-webui

# Look for successful startup, then test in browser
# Navigate to http://localhost:8080 and verify login page loads

Successful startup logs:

INFO:     [db] Database initialization complete
INFO: [main] Open WebUI starting on http://0.0.0.0:8080

Smoke test after startup:

  • Can access login page
  • Can log in with existing credentials
  • Can view chat history
  • No JavaScript console errors

Troubleshooting

"No such table" Errors

Symptom: Open WebUI crashes on startup with an error like:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: access_grant

or similar errors referencing other missing tables (e.g., message, channel).

Cause: One or more Alembic migrations did not apply successfully. This can happen when:

  • A migration silently failed during an automated upgrade (Open WebUI logs the error but continues startup)
  • The upgrade process was interrupted while migrations were running
  • ENABLE_DB_MIGRATIONS=False was set in the environment (disables automatic migrations on startup)
  • Multiple workers or replicas attempted to run migrations simultaneously
ENABLE_DB_MIGRATIONS Does Not Fix This

Setting ENABLE_DB_MIGRATIONS=True (the default) only tells Open WebUI to attempt automatic migrations on the next startup. It will not retroactively fix migrations that already failed or were skipped. If your database is already in a bad state, you must apply the migrations manually.

Solution:

Follow Step 2 to access your environment, then run:

Terminal
cd /app/backend/open_webui  # Docker
alembic upgrade head

This will apply all pending migrations, including creating any missing tables. After a successful migration, restart Open WebUI normally.

If you see additional errors during the manual migration (such as "table already exists"), check the other troubleshooting sections below for specific error messages.

"Table Already Exists" Errors

Symptom: Running alembic upgrade head fails with:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table chat_message already exists

or similar errors for other tables (e.g., access_grant).

Cause: A previous migration partially completed — the table was created in the database, but Alembic's version tracking was not updated (typically because the migration was interrupted during the data backfill step that runs after table creation). Alembic still thinks the migration hasn't been applied, so it tries to create the table again.

Diagnosis:

Terminal - Identify the Stuck Migration
# Check where Alembic thinks you are
alembic current
# Example output: 374d2f66af06 (head)

# Check what the next migration is
alembic history
# Look for the migration immediately after your current version
# Example: 374d2f66af06 -> 8452d01d26d7, Add chat_message table

Solution:

There are three ways to resolve this, listed from safest to most lossy:

Restore your database from the backup you created in Step 1, then run alembic upgrade head on the clean backup. This guarantees the full migration — including all data backfills — completes correctly.

Option 2: Drop the Table and Re-Run

If you don't have a backup, you can drop the partially-created table and let the migration run from scratch. Before doing this, verify it is safe:

Terminal - Verify Before Dropping
# 1. Confirm the migration is incomplete (current revision should be BEFORE the failing one)
alembic current

# 2. Check how much data the table has (if any)
# SQLite:
sqlite3 /app/backend/data/webui.db "SELECT COUNT(*) FROM <table_name>;"
# PostgreSQL:
psql $DATABASE_URL -c "SELECT COUNT(*) FROM <table_name>;"

# 3. Open the migration file and verify the source data still exists
# Find the file by its revision ID:
ls migrations/versions/ | grep <revision_id>
# Read it and look for which source columns/tables it copies FROM.
# Then verify those source columns still exist in your database.

Once you've confirmed the migration is incomplete and the source data is intact, drop the table and re-run:

Terminal - Drop and Re-Run
# SQLite:
sqlite3 /app/backend/data/webui.db "DROP TABLE <table_name>;"

# PostgreSQL:
psql $DATABASE_URL -c "DROP TABLE <table_name>;"

# Re-run migrations
alembic upgrade head
Check the Migration File First

This is only safe if the migration copies data from old columns into the new table (the original data remains intact). Open the migration file and verify it uses INSERT INTO ... SELECT FROM or similar — not destructive operations that modify or delete the source data. If you're unsure, use Option 1 instead.

Option 3: Stamp Past It (Last Resort)

If neither option above is possible, you can tell Alembic to skip the stuck migration entirely:

Terminal
# Mark the migration as applied without running it
alembic stamp <revision_id>

# Continue with remaining migrations
alembic upgrade head
This Skips the Data Backfill

Stamping marks the migration as done but skips any remaining steps like copying historical data into the new table. Your old data is not deleted — it still exists in the original columns — but the application may not read from those old columns anymore. Some features may work with gaps in historical data, while others may lose settings entirely.

If alembic upgrade head fails again with another "table already exists" error for a different migration, repeat the process for each stuck migration.

Multiple Stuck Migrations?

If you're hitting different errors on each retry — "duplicate column", then "table already exists", then "no such column" — your database is partially migrated across several steps. This typically happens after a major version jump (e.g., 0.7.x → 0.8.x). See Multiple Failures After a Major Version Jump for a step-by-step recovery workflow.

Multiple Failures After a Major Version Jump

Symptom: After upgrading across several versions (e.g., 0.7.x → 0.8.x), Open WebUI crashes on startup with a "no such column" error (e.g., no such column: user.scim). Running alembic upgrade head fails with a "duplicate column" or "already exists" error on an earlier migration. Fixing that one reveals another error on the next migration, and so on.

Cause: When Open WebUI starts after a major version jump, it attempts to run all pending migrations in sequence. If any single migration in the chain fails partway through (common causes: SQLite NOT NULL constraints without defaults, interrupted processes, memory limits on large backfills), the partial schema changes stick — because SQLite migrations are non-transactional — but alembic_version does not advance. Every subsequent startup retries the same failing migration, crashes on the already-applied parts, and never reaches the later migrations. The result is a database where the schema is a patchwork: some changes from early migrations applied, none from later ones.

Diagnosis:

Terminal - Confirm Partial Migration Chain
# Check where Alembic thinks you are
alembic current
# Example: 37f288994c47 (several versions behind head)

# Check where head is
alembic heads
# Example: b2c3d4e5f6a7 (head)

# List the full chain between current and head
alembic history

# Check for schema elements that shouldn't exist yet at your current revision
# (these indicate partial application of later migrations)
sqlite3 /app/backend/data/webui.db "PRAGMA table_info(channel_member);" | grep status
sqlite3 /app/backend/data/webui.db "SELECT name FROM sqlite_master WHERE type='table' AND name='chat_message';"

If alembic current shows an old revision but you can see tables or columns from later migrations already in the database, you have a partially-applied migration chain.

Solution — Step Through One Migration at a Time:

The approach: upgrade to each revision individually. If it succeeds, move on. If it fails with "duplicate column" or "already exists", stamp past it. If it fails with a different error, stop and investigate.

Terminal - Step-by-Step Recovery
# Get the ordered list of pending migrations
alembic history

# Try the first pending migration
alembic upgrade <first_pending_revision>

If it succeeds:

# Move to the next one
alembic upgrade <next_revision>

If it fails with "duplicate column name" or "already exists":

# The migration's schema changes already applied in a previous partial run.
# Stamp past it to advance alembic_version without re-running the SQL.
alembic stamp <that_revision>

# Continue to the next migration
alembic upgrade <next_revision>

Repeat until you reach head.

When is stamping safe here?

Stamping is safe when you've confirmed that the migration's schema changes are already present in the database — which the "already exists" or "duplicate column" error itself confirms. This is different from blindly running alembic stamp head, which skips all pending migrations regardless of whether they applied.

Special case — migrations with data backfills:

Some migrations don't just change schema — they also copy data from old tables into new ones (e.g., the chat_message migration backfills from the chat table's JSON column). If such a migration's table creation succeeded but the backfill was interrupted:

Terminal - Check if Backfill Completed
# Check if the table has data
sqlite3 /app/backend/data/webui.db "SELECT COUNT(*) FROM <table_name>;"

If the count is greater than zero, the backfill likely completed — stamp past it. If the count is zero, you have two options:

  1. Drop and re-run (preserves data, but the backfill may take a long time on large databases):

    sqlite3 /app/backend/data/webui.db "DROP TABLE <table_name>;"
    alembic upgrade <that_revision>
  2. Stamp past it (fast, but the backfill is skipped — some features that depend on the new table may show gaps in historical data):

    alembic stamp <that_revision>

Verify and start:

Terminal - Verify Recovery
alembic current
# Should show: <latest_revision> (head)

Then exit the container and start normally:

exit
docker start open-webui
docker logs -f open-webui
Stop if You See Unexpected Errors

The stamp-and-continue approach only applies to "already exists" and "duplicate column" errors, which confirm that schema changes from that migration are already in the database. If a migration fails with a different error (e.g., a data type mismatch, a foreign key violation, or a Python traceback unrelated to schema conflicts), do not stamp past it. Post the full error in a GitHub issue or on Discord.

"Required environment variable not found"

Cause: WEBUI_SECRET_KEY environment variable is missing.

Solution:

Terminal - Fix Missing Secret Key (Docker)
# Method 1: Check backend directory first (most common location)
export WEBUI_SECRET_KEY=$(cat /app/backend/.webui_secret_key)

# Method 2: If that fails, try data directory
# export WEBUI_SECRET_KEY=$(cat /app/backend/data/.webui_secret_key)

# Method 3: If neither file exists, generate new key
# export WEBUI_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
# echo $WEBUI_SECRET_KEY > /app/backend/.webui_secret_key

# Verify it's set
echo "WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:0:10}..."

# Try alembic again
alembic current -v
Why This Happens

Open WebUI's env.py file imports models, which import open_webui.env, which validates that WEBUI_SECRET_KEY exists. Without it, Python crashes before Alembic can even connect to the database.

"No config file 'alembic.ini' found"

Cause: You're in the wrong directory.

Solution:

Terminal
# Find your container name if not 'open-webui'
docker ps

# Find alembic.ini location
find /app -name "alembic.ini" 2>/dev/null # Docker
find . -name "alembic.ini" # Local

# Navigate to that directory
cd /app/backend/open_webui # Most common path

# Verify you're in the right place
ls -la alembic.ini

"Target database is not up to date"

Cause: Your database version doesn't match expected schema.

Diagnosis:

Terminal - Diagnose Version Mismatch
# Check what database thinks its version is
alembic current

# Check what code expects
alembic heads

# Compare

Solution depends on diagnosis:

Scenario: alembic current shows older version than alembic heads

Fix: You simply need to apply pending migrations.

Terminal
alembic upgrade head
Never Use "alembic stamp head" as a Fix

You may see advice to run alembic stamp head to "fix" version mismatches. This is dangerous.

alembic stamp head tells Alembic "pretend all migrations were applied" without actually running any of them. This creates permanent database corruption where Alembic thinks your schema is up-to-date when it isn't.

alembic stamp <specific_revision> is safe only when:

  • You have confirmed that the migration's schema changes are already present in the database (e.g., the "already exists" or "duplicate column" error proves it) — see Multiple Failures After a Major Version Jump
  • You manually created all tables using create_all() and need to mark them as migrated
  • You're a developer initializing a fresh database that matches current schema
  • You imported a database backup from another system and need to mark it at the correct revision
  • You've manually applied migrations via raw SQL and need to update the version tracking

Never use alembic stamp head to skip all pending migrations at once.

Process Hangs After "Will assume non-transactional DDL"

Understanding the message: This is not an error. It's informational. SQLite doesn't support transactional DDL, so Alembic is warning that migrations can't be rolled back automatically.

If genuinely stuck:

Some migrations (especially those adding indexes or modifying large tables) take several minutes.

Action: Wait 3-5 minutes before assuming it's stuck.

Autogenerate Detects Removed Tables

Symptom: You ran alembic revision --autogenerate and it wants to drop existing tables.

Don't Run Autogenerate

Regular users should NEVER run alembic revision --autogenerate. This command is for developers creating new migration files, not for applying existing migrations.

The command you want is alembic upgrade head (no revision, no --autogenerate).

If you accidentally created a bad migration file:

Terminal - Remove Bad Migration
# List migration files
ls -la /app/backend/open_webui/migrations/versions/

# Delete the incorrect auto-generated file (newest file)
rm /app/backend/open_webui/migrations/versions/<newest_timestamp>_*.py

# Restore to known good state
git checkout /app/backend/open_webui/migrations/ # If using git

Technical context: The "autogenerate detects removed tables" issue occurs because Open WebUI's Alembic metadata configuration doesn't import all model definitions. This causes autogenerate to compare against incomplete metadata, thinking tables should be removed. This is a developer-level issue that doesn't affect users running alembic upgrade.

PostgreSQL Foreign Key Errors

PostgreSQL Only

This troubleshooting applies only to PostgreSQL databases. SQLite handles foreign keys differently.

Symptom: Errors like psycopg2.errors.InvalidForeignKey: there is no unique constraint matching given keys for referenced table "user"

Cause: PostgreSQL requires explicit primary key constraints that were missing in older schema versions.

Solution for PostgreSQL:

PostgreSQL Fix
-- Connect to your PostgreSQL database
psql -h localhost -U your_user -d open_webui_db

-- Add missing primary key constraint (PostgreSQL syntax)
ALTER TABLE public."user" ADD CONSTRAINT user_pk PRIMARY KEY (id);

-- Verify constraint was added
\d+ public."user"

Note: The public. schema prefix and quoted "user" identifier are PostgreSQL-specific. This SQL will not work on SQLite or MySQL.

Duplicate Column Errors

Critical Issue

Duplicate column errors indicate schema corruption, usually from failed migrations or manual database modifications. This requires careful manual intervention.

Symptom: Migration fails with error like:

sqlite3.OperationalError: duplicate column name: message.reply_to_id

Or when starting Open WebUI:

UNIQUE constraint failed: alembic_version.version_num

Cause:

  • A previous migration partially completed, leaving duplicate columns
  • Database was manually modified
  • Migration was interrupted mid-execution
  • Upgrading directly across many versions (skipping intermediate migrations)

Diagnosis:

Terminal - Check for Duplicate Columns
# List all columns in the message table
sqlite3 /app/backend/data/webui.db "PRAGMA table_info(message);"

# Look for duplicate column names in the output
# Example problematic output:
# 10|reply_to_id|TEXT|0||0
# 15|reply_to_id|TEXT|0||0 <- Duplicate!

Solution - Manual Column Removal:

Data Loss Risk

Removing columns can cause data loss. Backup your database first before proceeding.

Terminal - Remove Duplicate Column (SQLite)
# 1. Backup database first!
cp /app/backend/data/webui.db /app/backend/data/webui.db.pre-fix

# 2. Enter SQLite
sqlite3 /app/backend/data/webui.db

# 3. Check current table structure
PRAGMA table_info(message);

# 4. SQLite doesn't support DROP COLUMN directly - must recreate table
# First, get the CREATE TABLE statement
.schema message

# 5. Create new table without duplicate column
-- Copy the CREATE TABLE statement but remove duplicate column definition
-- Example (adjust to your actual schema):
CREATE TABLE message_new (
id TEXT PRIMARY KEY,
content TEXT,
role TEXT,
-- ... other columns ...
reply_to_id TEXT, -- Only one instance
-- ... remaining columns ...
FOREIGN KEY (reply_to_id) REFERENCES message(id)
);

# 6. Copy data from old table to new table
INSERT INTO message_new SELECT * FROM message;

# 7. Drop old table
DROP TABLE message;

# 8. Rename new table
ALTER TABLE message_new RENAME TO message;

# 9. Exit SQLite
.quit

# 10. Verify fix worked
sqlite3 /app/backend/data/webui.db "PRAGMA table_info(message);"

# 11. Try migration again
cd /app/backend/open_webui
alembic upgrade head

Alternative - Simpler approach if you know the duplicate column:

Terminal - Quick Fix for Known Duplicate
# This only works if the column truly is completely duplicate
# and SQLite's table rebuilding handles it correctly

sqlite3 /app/backend/data/webui.db <<EOF
-- Create temporary table with correct schema
CREATE TABLE message_temp AS SELECT DISTINCT * FROM message;

-- Drop original table
DROP TABLE message;

-- Recreate with proper schema (get from .schema message originally)
-- Then copy data back
EOF
When to Seek Help

If you're not comfortable with SQL or aren't sure which column is the duplicate, stop here and seek help on the Open WebUI GitHub issues or Discord. Provide:

  • Output of PRAGMA table_info(message);
  • The full migration error message
  • Your Open WebUI version history (what version you upgraded from/to)

Prevention:

  • Never skip major versions when upgrading (don't jump from v0.1.x to v0.4.x)
  • Always backup before upgrading
  • Test upgrades on a copy of your database first
  • Review migration scripts for your version upgrade path

Peewee to Alembic Transition Issues

Background: Older Open WebUI versions (pre-0.4.x) used Peewee migrations. Current versions use Alembic.

Symptoms:

  • Both migratehistory and alembic_version tables exist
  • Errors about "migration already applied"

What happens automatically:

  1. Open WebUI's internal/db.py runs old Peewee migrations first via handle_peewee_migration()
  2. Then config.py runs Alembic migrations via run_migrations()
  3. Both systems should work transparently

If automatic transition fails:

Terminal - Manual Transition
# Check if old Peewee migrations exist
sqlite3 /app/backend/data/webui.db "SELECT * FROM migratehistory;" 2>/dev/null

# If Peewee migrations exist, ensure they completed
# Then run Alembic migrations
cd /app/backend/open_webui
alembic upgrade head
tip

If upgrading from very old Open WebUI versions (< 0.3.x), consider a fresh install with data export/import rather than attempting to migrate the database schema across multiple major version changes.

Advanced Operations

Production and Multi-Server Deployments

Rolling Updates Can Cause Failures

In multi-server deployments, running different code versions simultaneously during rolling updates can cause errors if the new code expects schema changes that haven't been applied yet, or if old code is incompatible with new schema.

Recommended deployment strategies:

Run migrations as a one-time job before deploying new application code:

Kubernetes Job Example
# 1. Run migration job
kubectl apply -f migration-job.yaml

# 2. Wait for completion
kubectl wait --for=condition=complete job/openwebui-migration

# 3. Deploy new application version
kubectl rollout restart deployment/openwebui

This ensures schema is updated before any new code runs.

Generate SQL Without Applying

For review or audit purposes, generate the SQL that would be executed:

Terminal - Generate Migration SQL
# Generate SQL for pending migrations
alembic upgrade head --sql > /tmp/migration-plan.sql

# Review what would be applied
cat /tmp/migration-plan.sql

Use cases:

  • DBA review in enterprise environments
  • Understanding what changes will occur
  • Debugging migration issues
  • Applying migrations in restricted environments
When to Use This

This is advanced functionality for DBAs or DevOps engineers. Regular users should just run alembic upgrade head directly.

Offline Migration (No Network)

If your database server is offline or isolated:

Terminal - Offline Migration Workflow
# 1. Generate SQL on development machine
alembic upgrade head --sql > upgrade-to-head.sql

# 2. Transfer SQL file to production
scp upgrade-to-head.sql production-server:/tmp/

# 3. On production, apply SQL manually
sqlite3 /app/backend/data/webui.db < /tmp/upgrade-to-head.sql

# 4. Update alembic_version table manually
sqlite3 /app/backend/data/webui.db \
"UPDATE alembic_version SET version_num='<target_revision>';"
Manual alembic_version Updates

Only update alembic_version if you've actually applied the corresponding migrations. Lying to Alembic about migration state causes permanent corruption.

Recovery Procedures

Recovery from Failed Migration

SQLite Has No Rollback

SQLite migrations are non-transactional. If a migration fails halfway through, your database is in a partially-migrated state. The only safe recovery is restoring from backup.

Symptoms of partial migration:

  • Some tables exist, others don't match expected schema
  • Foreign key violations
  • Missing columns that migration should have added
  • Application errors about missing database fields

Recovery steps:

Terminal - Restore from Backup
# 1. Stop Open WebUI immediately
docker stop open-webui

# 2. Verify backup integrity
sqlite3 /path/to/webui.db.backup "PRAGMA integrity_check;"

# 3. Restore backup
cp /path/to/webui.db.backup /path/to/webui.db

# 4. Investigate root cause before retrying
docker logs open-webui > migration-failure-logs.txt

# 5. Get help with logs before attempting migration again
Do Not Use "stamp" to Skip Incomplete Migrations

Do not use alembic stamp to mark a migration as complete if its schema changes did not actually apply. If the migration failed with an error other than "already exists" or "duplicate column", the schema change is genuinely missing and stamping past it will leave your database in a corrupt state. See Multiple Failures After a Major Version Jump for when stamping is appropriate.

Validate Database Integrity

Before and after migrations, verify your database isn't corrupted:

Terminal - SQLite Integrity Check
sqlite3 /app/backend/data/webui.db "PRAGMA integrity_check;"

# Should output: ok
# If it outputs anything else, database is corrupted

Post-Migration Checklist

After successful migration, verify:

  • alembic current shows (head) indicating latest version
  • Open WebUI starts without errors
  • Can log in successfully
  • Core features work (chat, model selection, etc.)
  • No error messages in logs
  • Data appears intact (users, chats, models)
  • Backup can be safely archived after 1 week of stability
Keep Recent Backups

Retain backups from before major migrations for at least 1-2 weeks. Issues sometimes appear days later during specific workflows.

Getting Help

If migrations continue to fail after following this guide:

Gather diagnostic information:

Terminal - Collect Diagnostic Data
# Version information
docker logs open-webui 2>&1 | head -20 > diagnostics.txt

# Migration state
cd /app/backend/open_webui
alembic current -v >> diagnostics.txt
alembic history >> diagnostics.txt

# Database info (SQLite)
sqlite3 /app/backend/data/webui.db ".tables" >> diagnostics.txt
sqlite3 /app/backend/data/webui.db "SELECT * FROM alembic_version;" >> diagnostics.txt

# Full migration log
alembic upgrade head 2>&1 >> diagnostics.txt

Where to get help:

  1. Open WebUI Discord Community

    • Real-time support from community members
    • Share error messages and diagnostics
  2. Provide this information:

    • Open WebUI version
    • Installation method (Docker/local)
    • Database type (SQLite/PostgreSQL)
    • Output of alembic current and alembic history
    • Complete error messages
    • What you were doing when it failed
note

Do not share your webui.db database file publicly - it contains user credentials and sensitive data. Only share the diagnostic text output.