Upgrading

Pull the latest image and restart. Your data and config are preserved.

Upgrading NocoDB means pulling the latest Docker image and restarting your stack. Your data (in Postgres) and your config (in nocodb/db.json and docker.env) are preserved across the upgrade.

TL;DR

If you used the Single-server install:

cd nocodb
./update.sh

If you used the Quickstart or Custom infrastructure examples:

docker compose pull
docker compose up -d

Both paths cause brief downtime while the containers restart.

Before upgrading

  • Back up your Postgres database. See Backups. Always back up before a major version upgrade.
  • Check the Changelog for breaking changes since your current version.
  • On an older bind-mount install? If your deployment keeps data in ./postgres and ./nocodb instead of Docker named volumes, do the bind-mount to named-volume migration first, or NocoDB will start with an empty database.

Step-by-step

# Move into your deployment directory (where docker-compose.yml lives)
cd nocodb

# 1. Pull the latest images
docker compose pull

# 2. Restart with the new images (NocoDB rolls forward; data is preserved)
docker compose up -d

# 3. Optional: clean up unused image layers to free disk
docker image prune -f

The installation wizard's ./update.sh does exactly these three steps.

Migrating from bind mounts to named volumes

Older NocoDB deployments stored data in host directories next to docker-compose.yml (./postgres, ./redis, ./nocodb). Current deployments use Docker-managed named volumes (postgres_data, redis_data, nocodb_data). If you pull a newer compose file, or re-run the installer, over an older bind-mount deployment, Docker mounts new, empty volumes and NocoDB starts with a fresh, empty database.

Your data is not lost: it stays in the old ./postgres and ./nocodb directories. But you must copy it into the new volumes before the first docker compose up -d on the new compose file, or NocoDB will initialize an empty database.

This is a one-time migration, and you only need it for the database and attachments. Redis holds cache and queue state, so a fresh, empty redis_data volume is fine.

Using an external (managed) Postgres? Your database lives outside Docker and is unaffected, so skip the Postgres copy below and migrate only the attachments.

1. Stop the old stack

cd nocodb   # your deployment directory
docker compose down

2. Switch to the new compose file, then create the empty volumes

Re-run the installer, or replace docker-compose.yml with the named-volume version, then create the volumes without starting anything:

docker compose up --no-start

Find the new volume names. Compose prefixes them with your deployment directory:

docker volume ls | grep -E '_(postgres|nocodb)_data'
# e.g. nocodb_postgres_data, nocodb_nocodb_data

3. Copy your data into the new volumes

A throwaway alpine container copies each old host directory into its volume. As long as the new stack uses the same Postgres major version as your old one, the raw data directory is compatible and transfers cleanly:

# Database (skip this if you use an external Postgres)
docker run --rm \
  -v "$(pwd)/postgres:/from:ro" \
  -v nocodb_postgres_data:/to \
  alpine sh -c "cp -a /from/. /to/"

# Attachments and app data
docker run --rm \
  -v "$(pwd)/nocodb:/from:ro" \
  -v nocodb_nocodb_data:/to \
  alpine sh -c "cp -a /from/. /to/"

If your directory is not named nocodb, replace nocodb_postgres_data and nocodb_nocodb_data with the names from step 2.

4. Start and verify

docker compose up -d

Sign in and confirm your bases, records, and attachments are all present. Once verified, the old data directories are safe to remove:

rm -rf ./postgres ./redis

Keep ./nocodb/, which still holds db.json, the database connection that the new compose mounts into the container.

A raw data-directory copy only works within the same Postgres major version. If you are also moving to a new major version, pg_dump from the old stack and restore into the new one instead. See Backups.

Pinning to a specific version

Production deployments often pin to a specific version rather than tracking latest:

# In docker-compose.yml
services:
  nocodb:
    image: nocodb/nocodb:2026.06.0
  worker:
    image: nocodb/nocodb:2026.06.0

Bump the tag, run docker compose up -d, and you've upgraded to that version.

Available tags are listed on Docker Hub.