Fix "relation does not exist" in Django Production

Fix ProgrammingError: relation does not exist (and SQLite 'no such table') in Django production: unapplied or uncommitted migrations, the wrong database, faked migrations, and import-time queries.

Problem statement

After a deploy, a page or the whole site fails with:

django.db.utils.ProgrammingError: relation "orders_invoice" does not exist
LINE 1: SELECT "orders_invoice"."id", ... FROM "orders_invoice" ...

On SQLite the same problem reads OperationalError: no such table: orders_invoice, and for a missing column, column orders_invoice.due_date does not exist.

Django's models describe a table that the database does not have. Either the migration that creates it was never applied to this database, the migration does not exist in the deployed code, or the application is connected to a different database than you think. This guide identifies which one in a few commands.

Where the table went missingModel changednew table or columnMigration filecommitted or not?migrateran on which DB?Queryrelation missingCompare models, migration files, and the migration table of the database the app really uses.
  1. Run python manage.py showmigrations <app> with production settings; unapplied migrations show [ ].
  2. If migrations are unapplied, run python manage.py migrate (and add it to the deploy).
  3. If makemigrations --check reports changes, the migration file was never created or committed; create it, commit it, deploy it.
  4. Confirm the app and the migrate command use the same database: print settings.DATABASES["default"] in both contexts.
  5. If migrations are recorded as applied but the table is missing, they were faked; fix the history carefully.
  6. If migrate itself fails with this error, find code that queries the database at import time.

Step-by-step solution

1. Check migration state with production settings

cd /srv/myapp/current
set -a; . /etc/myapp.env; set +a
.venv/bin/python manage.py showmigrations orders
.venv/bin/python manage.py migrate --plan
what does showmigrations say?Is the migration that createsthe table listed and applied?[ ] not appliedRun migratewith production settings[X] but table missingHistory brokenfaked or restored DBnot listed at allFile not deployedmissing from the releaseRun showmigrations with exactly the settings the app usesdjango-deployment.com
A different settings module can point at a different database entirely.

[ ] next to a migration means it is not applied to this database. migrate --plan lists what would run. If there are pending migrations, apply them:

.venv/bin/python manage.py migrate --noinput

Then make sure the deploy process runs migrate automatically before new code serves traffic, so this does not recur.

2. Check whether the migration file exists

.venv/bin/python manage.py makemigrations --check --dry-run
git status --short */migrations/
git log --oneline -- orders/migrations/ | head

If makemigrations --check reports changes, someone changed a model without generating a migration, or generated it locally and did not commit it. Generate it in development, review it, commit it, and deploy. Do not run makemigrations on the production server: the resulting file would exist only there and diverge from the repository.

Add makemigrations --check to CI so a model change without a migration fails the build.

3. Confirm which database the app uses

A common cause is migrate running against one database while Gunicorn connects to another, for example because the deploy script and the systemd unit load different environment files.

are you looking at the same database?What you checkedyour shell’s DATABASE_URLdefault settings module✓table is thereWhat the app usesEnvironmentFile of the unitproduction settings module✕table is missingLoad the app’s environment before running any checkdjango-deployment.com
Printing settings.DATABASES["default"]["NAME"] and HOST settles it.
.venv/bin/python -c "
import django, os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.production')
django.setup()
from django.conf import settings
db = settings.DATABASES['default']
print(db['ENGINE'], db.get('HOST'), db.get('PORT'), db['NAME'])
"

Run it in the deploy context, then compare with the environment the running service has:

sudo cat /proc/$(pgrep -f "gunicorn.*master" | head -1)/environ | tr '\0' '\n' | grep -E "DATABASE|DJANGO_SETTINGS"

In containers, check that the migration job or one-off container receives the same DATABASE_URL as the web container.

4. Look at the database directly

.venv/bin/python manage.py dbshell
what the database really has\dtshop_*SELECT app, name, applied FROMdjango_migrationsWHERE app = 'shop' ORDER BY id DESCLIMIT 3;1231Tables for the shop app, named app_model.2What Django believes was applied.3The latest rows: compare with the migration files.A recorded migration with no table means the history liesdjango-deployment.com
Check the search_path too if tables live in a non-default schema.
\dt orders_*
SELECT app, name, applied FROM django_migrations WHERE app = 'orders' ORDER BY id DESC LIMIT 5;
SHOW search_path;
  • Table missing and migration not recorded: step 1 applies.
  • Table missing but the migration recorded as applied: the migration was faked (migrate --fake) or the table was dropped manually. See step 5.
  • Table present in another schema: the connection's search_path differs (common with multi-tenant setups or custom OPTIONS).

5. Repair a faked or broken history

If django_migrations says a migration is applied but its table is missing, roll the record back and apply for real. For the latest migration of an app:

.venv/bin/python manage.py migrate orders 0011 --fake    # mark back to the migration before the missing one
.venv/bin/python manage.py migrate orders                 # apply 0012 for real

Only do this when you are sure the database state matches migration 0011. Take a backup first. If the history is tangled across several migrations, work through Fix InconsistentMigrationHistory in Django.

6. When migrate itself raises the error

If migrate crashes with relation does not exist before applying anything, some code queries the database while Django loads, before the tables exist. Typical examples:

# forms.py — evaluated at import time
class OrderForm(forms.Form):
    region = forms.ChoiceField(choices=[(r.pk, r.name) for r in Region.objects.all()])
# apps.py
def ready(self):
    Setting.objects.get_or_create(key="maintenance")   # runs before migrations

Move queries into functions evaluated per request (for example ModelChoiceField(queryset=Region.objects.all()), which is lazy), and never query the database in AppConfig.ready(). Use a data migration or a post-migrate signal for initial data.

Verification check:

.venv/bin/python manage.py showmigrations | grep "\[ \]" || echo "all migrations applied"
.venv/bin/python manage.py makemigrations --check --dry-run
curl -sI https://example.com/orders/ | head -1

Explanation

Django keeps three things in sync: model classes in code, migration files that describe how to build the schema, and the django_migrations table recording which migration files have been applied to a specific database. "Relation does not exist" is the database reporting that the schema is behind the models. Finding which of the three links is broken (missing file, unapplied file, or a different database) tells you the fix.

Edge cases / notes

Multiple databases

With database routers, migrate only migrates the default database unless you pass --database. Apps routed to another database need migrate --database=analytics.

Old code, new schema during deploys

If migrations run after new code starts serving, requests briefly hit the old schema and fail. Run migrations before switching traffic, and keep them backward compatible so old code also works with the new schema.

When to automate this

Enforce makemigrations --check in CI, run migrate as a mandatory deploy step before the switch, and fail the deploy if showmigrations reports anything unapplied afterwards.

Next steps

FAQ

What does "relation does not exist" mean in Django?

PostgreSQL has no table (relation) with that name in the schemas on the connection's search path. Usually the migration that creates it has not been applied.

Why does it work locally but not in production?

Your local database has the migration applied, or the migration file exists only on your machine. Commit the file and run migrate in production.

Should I run makemigrations on the production server?

No. Generate migrations in development, commit them, and only run migrate in production.

Why does migrate fail with relation does not exist?

Code runs a database query while Django starts, before the table is created. Remove queries from import time and AppConfig.ready().

Keep going