# Migration Guide

> Part of the NocoDB documentation (Self-hosting). Index of all pages: https://nocodb.com/llms.txt. Any docs page is available as Markdown by adding `.md` to its URL.

URL: https://nocodb.com/docs/self-hosting/migration-guide
Last updated: 2026-10-03

Migrate a self-hosted NocoDB instance from a non-Postgres database to PostgreSQL so you can activate a paid license without losing data.

## Why this guide?

We recommend PostgreSQL as the database for the `NC_DB` environment variable in all NocoDB deployments. But many instances start on a different database. The reasons can be legacy setups, the resources of the user, or convenience. SQLite is the zero-config default and needs nothing more to run.

This becomes a problem when you move from the Community (free) edition to a paid edition. License activation needs PostgreSQL as the database. SQLite and MySQL cannot activate a license. This guide shows how to migrate an existing SQLite instance to PostgreSQL. You can then upgrade cleanly and keep all of your data.

<Callout type="info">
  This guide was contributed by a member of the NocoDB community, based on a migration they performed themselves. Steps may need small adjustments for your specific setup, so always back up your data before you begin.
</Callout>

## SQLite to PostgreSQL migration

### Prerequisites

* Docker and Docker Compose
* A running NocoDB instance using the default SQLite backend
* A NocoDB Business license (required for the built-in migration tool)

### Step 1: Back up your SQLite data

Before you change anything, make several independent backups:

```bash
# Stop NocoDB so the SQLite file is in a consistent state
docker stop nocodb

# Copy the entire volume to a timestamped backup location
docker run --rm -v nocodb-data:/data -v "$(pwd)":/backup \
  alpine sh -c "cp -a /data /backup/nocodb-backup-$(date +%Y%m%d)"

# Copy the SQLite database file out of the container
docker cp nocodb:/usr/app/data/noco.db ./noco-snapshot-$(date +%Y%m%d).db

# Create a SQL dump as a secondary backup
sqlite3 noco-snapshot-$(date +%Y%m%d).db ".dump" > nocodb-dump-$(date +%Y%m%d).sql

# Restart NocoDB
docker start nocodb
```

### Step 2: Set up Postgres, Redis, and a worker

Update your `docker-compose.yml` to add Postgres, Redis and a worker container next to NocoDB. For this, follow the [Quickstart guide](/docs/self-hosting/installation/quickstart).

Remove the mapping to the SQLite container, but **do not** remove its volume.

### Step 3: Start only Postgres, then migrate the data

#### The recommended approach: NocoDB's built-in migration tool

**Do not use `sqlite3 .dump` piped into `psql`, or `pgloader`, for this migration.** These tools can transfer raw data, but SQLite and PostgreSQL have fundamentally incompatible schemas. You will get problems with:

* Boolean columns (SQLite stores 0/1, PostgreSQL expects true/false)
* Type mismatches (SQLite `datetime` vs PostgreSQL `timestamp`, `integer` vs `bigint`)
* Identifier case sensitivity (SQLite is case-insensitive, PostgreSQL folds unquoted identifiers to lowercase)
* Junction table integrity (SQLite allows NULL in NOT NULL columns, PostgreSQL does not)
* Migration tracking tables (`xc_knex_migrationsv0`) that NocoDB needs to recognize the database as initialized

In theory, you can fix these problems with `sed` transformations and post-import `ALTER TABLE` scripts. But this work is slow, and errors are likely.

**Instead, use the built-in migration feature of NocoDB:**

1. **Start a temporary NocoDB container with your old SQLite data.** Map the old volume that you disconnected from your old container.

   ```bash
   docker run -d --name nocodb-old \
     -v nocodb-data:/usr/app/data \
     -p 8082:8080 \
     nocodb/nocodb:latest
   ```

2. **Start your new Postgres-backed NocoDB (fresh, empty database).**

   ```bash
   # Make sure NC_MIGRATIONS_DISABLED is NOT set
   # Let NocoDB initialize a clean Postgres schema
   docker compose up -d nocodb
   ```

3. **Activate your license on the new Postgres instance**. Then use the migration tool:
   * On the **new instance**: go to Base homepage, then Import Data, then NocoDB, then Generate and Copy URL.
   * On the **old instance**: open the base context menu, then Settings, then Migrate. Paste the URL and click Migrate.

4. **Verify the migration completed.** All tables must show with correct row counts and no errors.

### Step 4: Fix auto-increment sequences

After the migration, the PostgreSQL auto-increment sequences can be out of sync with the imported data. New rows then get ID 1, which already exists. Reset all sequences:

```bash
docker exec nocodb-postgres psql -U nocodb -d nocodb -c "
DO \$\$
DECLARE
  r RECORD;
  max_id bigint;
BEGIN
  FOR r IN
    SELECT
      n.nspname AS schemaname,
      c.relname AS tablename,
      a.attname AS columnname,
      pg_get_serial_sequence(format('%I.%I', n.nspname, c.relname), a.attname) AS sequencename
    FROM pg_class c
    JOIN pg_namespace n ON n.oid = c.relnamespace
    JOIN pg_attribute a ON a.attrelid = c.oid
    WHERE c.relkind = 'r'
      AND n.nspname NOT IN ('pg_catalog','information_schema')
      AND a.attnum > 0
      AND NOT a.attisdropped
      AND (
        a.attidentity <> ''
        OR pg_get_serial_sequence(format('%I.%I', n.nspname, c.relname), a.attname) IS NOT NULL
      )
  LOOP
    EXECUTE format('SELECT MAX(%I) FROM %I.%I', r.columnname, r.schemaname, r.tablename)
      INTO max_id;

    IF max_id IS NULL THEN
      EXECUTE format('SELECT setval(%L, 1, false)', r.sequencename);
    ELSE
      EXECUTE format('SELECT setval(%L, %s, true)', r.sequencename, max_id);
    END IF;
  END LOOP;
END \$\$;
"
```

### Step 5: Post-migration cleanup

1. **Recreate broken views.** Some filters can work differently on the new backend, especially datetime filters. Check your views for changes.

2. **Remove old containers and volumes**. Do this only after you check everything for a few days:

   ```bash
   docker rm -f nocodb-old
   docker volume rm nocodb-data  # Only after you are confident everything works
   ```

---

## Related pages

- [Self-Hosting](https://nocodb.com/docs/self-hosting.md): Self-host NocoDB on your own infrastructure and keep full control of your data.
- [Environment Variables](https://nocodb.com/docs/self-hosting/environment-variables.md): Set environment variables to configure a self-hosted NocoDB instance: database, storage, authentication, cache, rate limits and more.
- [Purchasing a License](https://nocodb.com/docs/self-hosting/purchase-license.md): Purchase a Business or Scale plan license for your self-hosted NocoDB instance through NocoDB Cloud.
- [Activating a License](https://nocodb.com/docs/self-hosting/license-activation.md): Activate a self-hosted NocoDB license with the standard, airgapped or fully offline method.
- [White Label](https://nocodb.com/docs/self-hosting/white-label.md): Replace the NocoDB branding on your self-hosted instance with your own product name, logos, favicon, brand color, email branding and support contact.
- [Troubleshooting](https://nocodb.com/docs/self-hosting/troubleshooting.md): Find and fix common problems with self-hosted NocoDB: startup, network, SSL, database, attachments and login.
- [License](https://nocodb.com/docs/self-hosting/license.md): The NocoDB Sustainable Use License (SUL), its Fair-Code principles, and when you need a commercial license.
- [FAQs](https://nocodb.com/docs/self-hosting/FAQs.md): Answers to common questions about self-hosted NocoDB: what the Community Edition includes, upgrades, shifted timestamps and instance details.
