TabSlate LogoTabSlateDocs
Self-hosting

Upgrade & Backup

What the server does on startup, and how to back up and upgrade an instance safely.

What's documented server behavior vs. what's a recommendation

The server itself only does one thing relevant here on every startup: it runs its embedded database migration against whatever DATABASE_URL points at, and that migration is idempotent — safe to run again against a database it has already migrated. Everything else on this page — backup cadence, reading release notes, keeping old image tags around — is an operating recommendation, not something the server enforces or reminds you about.

Back up before you touch anything

Two things hold all the state that matters for a self-hosted instance, and the server backs up neither for you:

  • Your PostgreSQL database. This needs an actual database-level backup, not a filesystem copy of a container — the Compose deployment doesn't even bundle a Postgres container to copy, since api connects out to whatever external Postgres instance DATABASE_URL names. Use your database's own backup mechanism: pg_dump against that connection string,

    pg_dump "$DATABASE_URL" -Fc -f tabslate-backup.dump

    or your managed provider's built-in backup/snapshot feature if you're not self-hosting Postgres too.

  • Your .env file. It holds JWT_SECRET, DATABASE_URL, and every other credential the server needs — losing it without a copy means regenerating secrets and reconfiguring every provider from scratch, even though no user data was lost.

docker compose down -v removes the named volumes declared in docker-compose.ymlcaddy_data and caddy_config, which hold Caddy's cached TLS state. It does not touch your PostgreSQL data, since Postgres isn't one of the containers this Compose file manages. Still, run it deliberately: recreating those volumes means Caddy re-requests a certificate from Let's Encrypt, which is rate-limited per domain.

Upgrading

  1. Read the release notes for the version you're moving to before upgrading, since the server doesn't summarize what changed for you.

  2. Take the backups above first.

  3. Pull the new image and recreate the container:

    docker compose pull api
    docker compose up -d --force-recreate api
  4. Watch it come up and confirm the migration ran cleanly:

    docker compose logs -f api

    Look for database ready in the log output — that line only prints after db.Migrate has returned successfully.

Keep a rollback path

Pin or note the image tag/digest you were running before pulling latest, so you can pin back to it (docker compose pull a specific tag, or edit the image: line in docker-compose.yml) if the new version doesn't work out. Combined with a recent database backup, this is what actually makes an upgrade reversible — latest by itself gives you no way to know what you were running before.

Last updated on

On this page