Moving Off Per-Seat SaaS Without Breaking Anything: A Weekend Migration Walkthrough
Description and use
A small team migrated Sentry, 1Password/Bitwarden, and Google Analytics to a single VPS using Docker Compose, shared PostgreSQL and Redis, and Caddy for TLS, without data loss and with rollback capability.
Problem
Small teams pay per seat for SaaS tools (error tracking, password manager, web analytics) and want to self‑host without data loss or downtime.
Outcome
All three SaaS services were successfully migrated to a single VPS with Docker Compose, sharing a Postgres and Redis instance, and secured with Caddy TLS. The migration required no code changes and allowed rollback at each step.
Workflow
- 1. Provision a VPS with 4 GB RAM, 2 vCPUs and point DNS A records to it.
- 2. Install Docker and Docker‑Compose, open ports 80/443.
- 3. Create a shared Postgres and Redis container and a Caddy reverse proxy.
- 4. Write a docker‑compose.yml that defines services for GlitchTip, Vaultwarden, Umami, and Caddy, sharing the database and Redis.
- 5. Create init‑dbs.sql to create users and databases for each app.
- 6. Run docker‑compose up, ensuring glitchtip‑migrate and glitchtip‑worker run before the web service.
- 7. Verify TLS certificates are issued by Caddy and services are reachable.
- 8. Invite team members to Vaultwarden via admin page, update DSNs for Sentry to point to GlitchTip.
- 9. Test data integrity and rollback if needed.
Evidence
Three swaps: Sentry → GlitchTip, 1Password/Bitwarden Teams → Vaultwarden, Google Analytics → Umami.
A VPS with 4 GB of RAM and 2 vCPUs is plenty. Rough idle memory use from my notes: Vaultwarden around 128 MB, Umami around 384 MB, GlitchTip around 512 MB, plus Postgres and Redis shared between them.
Point three DNS A records at the server before you do anything else, so TLS certificates can be issued on first boot.
The GlitchTip worker is easy to forget. Without it, events get accepted but never processed, and you'll think the migration failed.
The init‑dbs.sql file creates a database and user per app, so each one is isolated even though they share a server.
- Record date: 2026-11-01T21:13:49.519Z