Move a Django Site to a New Server

Migrate a production Django site to a new server or provider with minimal downtime: rebuild the stack from code, copy media with rsync, move PostgreSQL with dump and restore or replication, lower DNS TTL, cut over, and keep the old server as a fallback.

Problem statement

The server is old, the OS is out of support, the provider is too expensive, or the app has outgrown the machine. Moving a live Django site means moving code, configuration, secrets, the database, uploaded media, certificates, scheduled jobs, and DNS, without losing data written during the move and without a long outage. Doing it ad hoc on a Friday evening is how sites lose a day of orders. A rehearsed plan with a short write freeze makes it routine.

Moving to a new serverBuild new serverfrom scriptsSync dataDB + media rsyncFreeze, final syncshort downtimeSwitch DNSold server as fallbackPrepare everything in advance so the cutover is only a final sync and a DNS change.
  1. Lower the DNS TTL to 300 seconds at least a day before the move.
  2. Build the new server from your provisioning scripts (Ansible, cloud-init, or Compose), not by copying the old one.
  3. Copy media with rsync in advance; copy the database once to rehearse and measure.
  4. At cutover: enable maintenance mode, final rsync, pg_dump/pg_restore (or promote a replica), run migrate --check and smoke tests on the new server.
  5. Switch DNS (or a floating IP), confirm traffic arrives, then disable maintenance mode.
  6. Keep the old server read-only for a few days, then decommission and wipe it.

Step-by-step solution

1. Inventory what lives on the old server

Write down everything the app depends on:

  • Code version, Python version, system packages (libpq, image libraries).
  • Environment file and secrets; Nginx, Gunicorn, Celery, and timer units.
  • PostgreSQL version, extensions, roles, database size.
  • MEDIA_ROOT location and size; any files outside it the app writes.
  • Cron jobs and systemd timers; certificates and renewal hooks; firewall rules.

Anything not in your provisioning scripts is a gap to close before the move.

2. Build the new server from code

Provision it with the same scripts you would use for a fresh environment, install the target OS and PostgreSQL version, deploy the current release, and configure Nginx with a temporary hostname such as new.example.com to test.

3. Pre-copy media and rehearse the database

# media, repeatable; only changes are sent on later runs
rsync -aHAX --delete --info=progress2 old:/srv/app/shared/media/ /srv/app/shared/media/

# database rehearsal
ssh old 'pg_dump -Fc -d app' > /tmp/app.dump
pg_restore --no-owner --role=app -d app -j 4 /tmp/app.dump

Time the rehearsal: the dump and restore duration is roughly your downtime. For large databases where that is too long, set up logical replication from old to new ahead of time so cutover is just stopping writes and promoting; see Migrate a Django Database With Logical Replication.

cutover night, step by stepTTL lowered1-2 days beforeMaintenance onwrites stopFinal rsync + DB copyrehearsed durationSmoke tests on newvia hosts fileDNS switchedmaintenance offDowntime equals the final sync plus checks, which rehearsal measuresdjango-deployment.com
Announce a window slightly longer than the rehearsal took.

4. Cut over

  1. Enable maintenance mode on the old server so no new writes happen (or stop Gunicorn and Celery and serve a static page from Nginx).
  2. Run the final rsync (fast, only changes) and the final database copy.
  3. On the new server: python manage.py migrate --check, check --deploy, restart services.
  4. Test the new server before DNS changes by adding 203.0.113.20 example.com to your local hosts file: log in, place a test order, upload a file.

5. Switch traffic

Update the A/AAAA records (or move a floating or reserved IP, which is instant). With a 300-second TTL, most clients follow within minutes. Issue certificates on the new server: if DNS has not switched yet, use the DNS-01 challenge, or copy /etc/letsencrypt from the old server and run certbot renew --dry-run after the switch. Then disable maintenance mode.

6. After the move

Watch logs on both servers: requests still arriving at the old one show stale DNS caches. Keep the old server in maintenance mode, read-only, for a few days as a fallback, then take a final backup, decommission it, and wipe its disks. Restore the DNS TTL to a normal value.

nothing left behindInventory: env, units, timers, certseverything on the old hostNew server built from scriptsnot cloned by handMedia pre-synced, DB rehearseddowntime measuredSmoke tests via hosts filebefore DNS changesOld server kept read-only, then wipedfallback, then cleanupThe forgotten cron job is the classic casualty of a server movedjango-deployment.com
Compare timer and cron lists on both servers after the move.

Verification check: dig +short example.com returns the new IP from several resolvers, the new server's access log shows production traffic, row counts for key tables match the final dump, a newly uploaded file appears on the new server, and scheduled jobs run there.

Explanation

The data that changes during a move (database rows and uploaded files) determines the downtime; everything else can be prepared in advance. Pre-syncing media with rsync and rehearsing the database copy shrinks the final step to a short, measured window under a write freeze, which guarantees no writes are lost. Lowering the DNS TTL beforehand shortens how long clients keep the old address, and keeping the old server read-only gives a safe fallback while caches expire.

Edge cases / notes

  • Floating IPs within one provider make the switch instant and avoid DNS propagation entirely.
  • Email. If the server sends mail directly, update SPF records with the new IP before the move.
  • Webhooks and allow-lists. Third parties that allow-list your server IP (payment providers, partner APIs) need the new address in advance.
  • Upgrading at the same time. Moving and upgrading PostgreSQL or Python together is fine if rehearsed; change as little else as possible.

Next steps

FAQ

How do I move Django to a new server without downtime?

Truly zero downtime needs database replication; otherwise a short write freeze while you do a final sync keeps data consistent.

Should I copy the old server's disk image?

Prefer rebuilding from provisioning scripts. It catches undocumented configuration and gives a clean OS.

How long does DNS take to switch?

About the TTL. Lower it to 300 seconds a day or two before the move.

What should I do with the old server?

Keep it read-only for a few days as a fallback, then back it up, decommission it, and wipe the disks.

Keep going