Guides / Django users logged out after every deploy

Django users logged out after every deploy

Every time you deploy, all your users are logged out. Or they get logged out at random, as if the site forgets them. Sessions survive restarts only if both halves survive: the data (in the database, cache or cookie) and the key that signs it. A deploy that loses either one signs everyone out.

What it means

A session has two parts. The browser holds a session ID in a cookie. Django looks up the data for that ID on the server and checks it against a hash derived from SECRET_KEY. If the data is gone, or the key has changed, the user is anonymous again.

Why it happens

  1. SECRET_KEY changes on each start. Settings generate a random key, get_random_secret_key() is called in settings.py, or a fallback default is regenerated. Every deploy invalidates every session.
  2. Each instance has a different key. With two servers, a user gets logged out whenever the load balancer sends them to the other one.
  3. Sessions live in memory or on local disk. LocMemCache (per process, wiped on restart) with the cache session backend, or sessions.backends.file on an ephemeral disk.
  4. The session table is wiped because a SQLite database was replaced on deploy (see SQLite in production).
  5. Cookie settings don't match the way users reach the site: SESSION_COOKIE_SECURE = True on http, a SESSION_COOKIE_DOMAIN for a different domain, or www versus the bare domain.
  6. The cache evicts sessions (Redis or Memcached with a low memory limit).

The fix

Use one stable key everywhere, read from the environment:

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]   # same value on every instance and every release

Store sessions somewhere that outlives a deploy and is shared by all instances. The simplest choice is the database (the default):

SESSION_ENGINE = "django.contrib.sessions.backends.db"
# or keep them in a shared cache with a database fallback:
# SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"

Make the cookie settings match your site:

SESSION_COOKIE_SECURE = True          # only if the site is https everywhere
SESSION_COOKIE_DOMAIN = None          # leave unset unless you share cookies across subdomains
SESSION_COOKIE_AGE = 60 * 60 * 24 * 14

When you do need to change the key, move the old one into the fallback list so sessions stay valid (Django 4.1 and newer):

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
SECRET_KEY_FALLBACKS = [os.environ["DJANGO_SECRET_KEY_OLD"]] if os.environ.get("DJANGO_SECRET_KEY_OLD") else []

Making it stick in production

Or skip it

On Django Cloud your SECRET_KEY is stored once with your settings and every release gets the same value, and a rollback brings back the settings of that release. Keep sessions in Postgres and they survive deploys.

pip install djangocloud-cli
djangocloud deploy

Get startedSee pricing

Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.

Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.