Preserving user sessions when migrating to a new host

Continuing the discussion from Move your Discourse Instance to a Different Server:

One issue in migrating a Discourse instance to a new server, or even restoring a backup to a newly built instance, is that all logged in user sessions maybe lost - requiring users to log in again. Depending on the community this could be undesirable because:

  • Some users may be discouraged to participate if they have difficulty re-logging in.
  • Some users may need help recovering their passwords.
  • Some users may create new accounts, split from their posting history and User Notes while leaving behind a zombie account.

The loss of sessions can happen because the Discourse session cookies are encrypted with a randomly generated secret key which is stored in Redis by default. Redis data are not included in a backup so a new secret key is regenerated when you restore the site onto a new host.

The benefit to this approach is that a compromised backup file doesn’t contain the key needed to compromise user sessions - which could have very bad consequences.

When migrating an Discourse instance, this may however be worked around by following these steps to copy the secret key, enabling host migration while persisting user sessions.

Step 1: Retrieve the secret

Enter your existing Discourse instance, run a rails console, then get the secret key:

admin@host:/var/discourse$ ./launcher enter app
root@host:/var/www/discourse# rails console
[1] pry(main)> GlobalSetting.safe_secret_key_base
=> "90.......fed"

Note the value printed on the last line and keep it secure.

Step 2: Set the secret in the the new instance

Assuming you already have a Discourse instance prepared to receive a backup in order to migrate your installation, edit the app.yml to set the secret you just obtained. Add it to the env section:

env:
  DISCOURSE_SECRET_KEY_BASE: "90.......fed"

After adding this, rebuild the instance:

admin@newhost $ launcher rebuild app

Step 3: Verify the secret in the new instance

This is the same as Step 1, except run the commands on the new host:

admin@newhost:/var/discourse$ ./launcher enter app
root@newhost:/var/www/discourse# rails console
[1] pry(main)> GlobalSetting.safe_secret_key_base
=> "90.......fed"

The secret key that is printed should be exactly the same as the key at step 1.

Step 4: Proceed with migration

Set the source Discourse to read-only, take a backup, copy it to the destination Discourse and restore. Update DNS records (or use a hosts file hack for testing) so users are pointed to the new site.

Previously logged in sessions should now persist to the new host.

Warning

You absolutely should not share the secret key between multiple discourse instances. Possession of the secret key would allow someone to decrypt and modify their session cookie on a site, and could have very bad security consequences.

As always, practice and very the above steps on a sandbox instance or test installations prior to working on a real Discourse instance. Prepare for success!

Last, but not least, many thanks to @david for describing this method - I merely tested and wrote it up here (and please feel free to edit or enhance!).