Migrate from the All-in-One Container
How to migrate your data from the legacy surfsense all-in-one Docker image to the current multi-container setup
The original SurfSense all-in-one image (ghcr.io/modsetter/surfsense:latest, run via docker-compose.quickstart.yml) stored all data — PostgreSQL, Redis, and configuration — in a single Docker volume named surfsense-data. The current setup uses separate named volumes and has upgraded PostgreSQL from version 14 to 17.
Because PostgreSQL data files are not compatible between major versions, a logical dump and restore is required. This is a one-time migration.
This guide only applies to users who ran the legacy docker-compose.quickstart.yml (the all-in-one surfsense container). If you were already using docker/docker-compose.yml, you do not need to migrate.
Option A — One command (recommended)
install.sh detects the legacy surfsense-data volume and handles the full migration automatically — no separate migration script needed. Just run the same install command you would use for a fresh install:
curl -fsSL https://raw.githubusercontent.com/MODSetter/SurfSense/main/docker/scripts/install.sh | bashWhat it does automatically:
- Downloads all SurfSense files (including
migrate-database.sh) into./surfsense/ - Detects the
surfsense-datavolume and enters migration mode - Stops the old all-in-one container if it is still running
- Starts a temporary PostgreSQL 14 container and dumps your database
- Recovers your
SECRET_KEYfrom the old volume - Starts PostgreSQL 17, restores the dump, runs a smoke test
- Starts all services
Your original surfsense-data volume is never deleted — you remove it manually after verifying.
After it completes
- Open http://localhost:3000 and confirm your data is intact.
- Once satisfied, remove the old volume (irreversible):
docker volume rm surfsense-data - Delete the dump file once you no longer need it as a backup:
rm ./surfsense_migration_backup.sql
If the migration fails mid-way
The dump file is saved to ./surfsense_migration_backup.sql as a checkpoint. Simply re-run install.sh — it will detect the existing dump and skip straight to the restore step without re-extracting.
Option B — Manual migration script (custom credentials)
If you launched the old all-in-one container with custom database credentials (POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB environment variables), the automatic path will use wrong credentials. Run migrate-database.sh manually first:
# 1. Extract data with your custom credentials
bash ./surfsense/scripts/migrate-database.sh --db-user myuser --db-password mypass --db-name mydb
# 2. Install and restore (detects the dump automatically)
curl -fsSL https://raw.githubusercontent.com/MODSetter/SurfSense/main/docker/scripts/install.sh | bashOr download and run if you haven't run install.sh yet:
curl -fsSL https://raw.githubusercontent.com/MODSetter/SurfSense/main/docker/scripts/migrate-database.sh -o migrate-database.sh
bash migrate-database.sh --db-user myuser --db-password mypass --db-name mydbMigration script options
| Flag | Description | Default |
|---|---|---|
--db-user USER | Old PostgreSQL username | surfsense |
--db-password PASS | Old PostgreSQL password | surfsense |
--db-name NAME | Old PostgreSQL database | surfsense |
--yes / -y | Skip confirmation prompts (used automatically by install.sh) | — |
Troubleshooting
install.sh runs normally with a blank database (no migration happened)
The legacy volume was not detected. Confirm it exists:
docker volume ls | grep surfsense-dataIf it doesn't appear, the old container may have used a different volume name. Check with:
docker volume ls | grep -i surfsenseExtraction fails with permission errors
The script detects the UID of the data files and runs the temporary PG14 container as that user. If you see permission errors in ./surfsense-migration.log, run migrate-database.sh manually and check the log for details.
Cannot find /data/.secret_key
The all-in-one entrypoint always writes the key to /data/.secret_key unless you explicitly set SECRET_KEY= as an environment variable. If the key is missing, the migration script auto-generates a new one (with a warning). You can update it manually in ./surfsense/.env afterwards. Note that a new key invalidates all existing browser sessions — users will need to log in again.
Restore errors after re-running install.sh
If surfsense-postgres volume already exists from a previous partial run, remove it before retrying:
docker volume rm surfsense-postgres