Skip to main content

Restore a backup, or move to a new server

The product has no restore button and no restore command. Restoring is the same job as installing a fresh site and then putting the database and the files back by hand. Moving to a new server is the same job again. This page is for whoever runs the server.

You need the four things from What to back up: the database, the public and private files, and your settings.

The pattern​

  1. Install the site as you normally would on the new place, the same version or newer than the one the backup came from. See Choose a platform. Do not start it yet if you can avoid it.
  2. Give it the same settings: the same SITE_URL, LICENSE_KEY, admin credentials, email settings and so on. If the storage locations differ, use the new paths.
  3. Stop the site.
  4. Put the database back.
  5. Put the files back.
  6. Start the site.
  7. Check it (see the end).

Restore a SQLite site with local files​

This restores a copy made with the script in Back up a single-server site.

# 1. The site is stopped. Put the database back, and remove the old companion files.
cd /srv/choirmaster/data
rm -f choir.sqlite choir.sqlite-wal choir.sqlite-shm
cp /srv/backups/choirmaster/20261011-020000/choir.sqlite choir.sqlite

# 2. Put the files back
tar -xzf /srv/backups/choirmaster/20261011-020000/storage.tar.gz -C /srv/choirmaster/data

# 3. Make sure the service's user owns them
sudo chown -R choirmaster:choirmaster /srv/choirmaster/data

# 4. Start
sudo systemctl start choirmaster

Use your own paths and your service's user name. Remove choir.sqlite-wal and choir.sqlite-shm. Leftover companion files from a different copy of the database can corrupt the restored one.

The restored database brings back everything entered up to the time of the backup, including Site Settings, the admin list and sessions. People who were logged in still are, as long as SESSION_STORE is database (the default). Anything entered after the backup is lost.

Use the copy made before an update​

If an update went wrong and you want the database as it was just before, use the file in DATA_DIR/backups/ named before-<version>-<time>.sqlite. It is an ordinary SQLite file, made the same way as the copies above, so the restore is the same: stop the site, remove the database and its two companion files, copy the before-... file into place as choir.sqlite, and start.

Also return the site to the code it had before: see A version that starts and then misbehaves. If you leave the newer code running it adds anything missing to the old database when it starts, which is harmless.

Everything entered between the update and the restore is lost. Restore only if that is better than the alternative. This copy holds the database only, not uploaded files, and only exists for SQLite sites.

Restore a Docker site​

For the SQLite stack, stop the app, put a database file and a storage folder into the volume, and start it again. With the tar file made by the one-liner in the single-server page:

docker compose -f docker-compose.sqlite.yml stop app
docker run --rm -v choirmaster_data:/data -v "$PWD":/backup alpine \
sh -c 'rm -rf /data/* && tar xzf /backup/choir-backup.tgz -C /'
docker compose -f docker-compose.sqlite.yml start app

This replaces everything in the volume with the contents of the archive. The archive made by that one-liner holds the path data/..., hence -C /. Check the names with tar -tzf choir-backup.tgz | head before running it, and take a copy of the volume first if it holds anything you might still want.

For a database file and a storage archive made by the script (not the volume tar), use docker compose cp to put them in /app/data/ of the stopped container (docker compose run --rm --no-deps app ...), or restore into a fresh volume with a helper container as above. Remove old choir.sqlite-wal and choir.sqlite-shm files in the volume.

Restore PostgreSQL or MySQL, and S3 storage​

These use the standard tools. The commands mirror the ones in Back up PostgreSQL, MySQL, S3 storage and Cloudflare.

PostgreSQL. Restore into an empty database, with the site stopped:

createdb choir
pg_restore --no-owner --dbname="$DATABASE_URL" choir-20261011.dump

For the Compose container: docker compose exec -T db pg_restore -U choir -d choir --no-owner < choir-20261011.dump.

If the new site has already been started once, it will have created empty tables, and pg_restore will complain that they exist. Add --clean --if-exists to the pg_restore command to replace them.

MySQL and MariaDB. Into an empty database:

mysql -h db.example.org -u choir -p choir < choir-20261011.sql

S3 buckets and MinIO. Copy the files back into the two buckets with the same file names, using the same tool and the opposite direction (aws s3 sync ./backup/choir-public s3://choir-public).

When a restored database is older than the code, the server adds any missing tables as it starts. That is automatic unless you set DB_AUTO_MIGRATE=false, in which case run npm run db:migrate.

Restore a Cloudflare site​

Create the D1 database and the two R2 buckets as in a new install (Deploy to Cloudflare Workers), put your wrangler.toml in place with their ids, then:

npx wrangler d1 execute choir-db --remote --file=choir-db-20261011.sql
rclone copy ./backup/choir-public r2:choir-public
rclone copy ./backup/choir-private r2:choir-private
npx wrangler secret put ADMIN_PASSWORD # and each of your other secrets
npx wrangler deploy

Restore into an empty D1 database. Set each secret again, because secrets cannot be read from the old Worker.

Move to a new server​

Moving is a restore onto the new machine, plus switching your address over. Do it in this order:

  1. Install the same version on the new server, with the same settings, as in the pattern above. Use the same SITE_URL and the same LICENSE_KEY.
  2. Take a final backup from the old server, ideally with the site stopped so nothing changes after it. Restore it on the new server.
  3. Start the new server and test it by its own address or with a temporary hosts-file entry, before anyone else uses it. Run curl -s http://localhost:3001/api/health and log in.
  4. Point your domain's DNS at the new server. Keep the old server stopped, not deleted, for a while.

If the website address stays the same, the licence key keeps working with no help: it is tied to the website, not the server. See Your licence key.

If the domain name changes, the key needs moving first, by writing to support@choirmastercms.com with the new website, because a different website is refused (How a key is tied to its website). Also set SITE_URL to the new address. Login links already emailed point to the old address.

If you set PUBLIC_FILES_URL, the addresses of pictures already saved in your Site Settings (logo, posters) are saved with that address in them, so the new server must keep serving files there. Without PUBLIC_FILES_URL, the site saves them as /files/... paths, which follow the site to a new domain.

Things the product does not do​

  • No tool changes database engine. There is no way to move a site from SQLite to PostgreSQL, from MySQL to PostgreSQL, or the reverse. A backup made with one engine does not restore into another. If you must, ask support about the route.
  • No tool changes storage provider. Moving from local files to S3, or the reverse, is possible by hand (copy the files with the same names to the new place and set STORAGE_PROVIDER), but nothing does it for you.
  • Hosted to self-hosted and back is covered on Moving between a hosted site and your own server.

Check a restored site​

  1. curl -s http://localhost:3001/api/health answers {"status":"ok", ...} with the version you expect.
  2. The log has [db] schema is up to date and [server] version ... listening.
  3. Log in to the admin panel and check recent items: the latest concert, announcement or member you know was entered before the backup.
  4. Open a page with a photo and a member file, to be sure both file stores came back.
  5. Press Check now on the Updates page: Last checked should change and no error appear.