For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at /guide/self-hosted/operate/migration.md.
Migrate to a new server
Restore a full backup onto fresh infrastructure with data intact, and verify before cutting over.
This is the restore procedure, written as a server move. The same steps recover an instance after a failure — the only difference is whether the old server is still around.
Everything here runs on the NEW server
Unless a step says otherwise. Start from a complete backup — see Backup.
#1. Prepare the new server
| Requirement | Install | Minimum |
|---|---|---|
| Docker Engine | curl -fsSL https://get.docker.com | sh | 24.x |
| Docker Compose plugin | apt install docker-compose-plugin | 2.x |
| curl | apt install curl | any |
| rsync / scp | apt install rsync openssh-client | any |
docker --version
docker compose version
Meet the prerequisites too — migrating onto an undersized box just moves the problem.
#2. Extract the backup
cd /home/user
tar -xzf orangescrum-backup-YYYY-MM-DD.tar.gz
ls -lh backup/YYYY-MM-DD/
Confirm all ten files are present before going further.
#3. Restore host application data
mkdir -p /data
tar -xzf backup/YYYY-MM-DD/app-data.tar.gz -C /
ls -lh /data
#4. Prepare the project directory
mkdir -p /opt/orangescrum
cd /opt/orangescrum
cp /home/user/backup/YYYY-MM-DD/docker-compose.yml .
cp /home/user/backup/YYYY-MM-DD/osreports.Dockerfile . # if present
# Secrets
tar -xzf /home/user/backup/YYYY-MM-DD/secrets.tar.gz -C /opt/orangescrum/
ls secrets/
Verify every .txt file is present — a missing secret shows up later as a
container that restarts forever.
#5. Create the volumes and restore into them
Create each volume first, then populate it.
docker volume create orangescrum-ee_orangescrum-app-files
docker volume create orangescrum-ee_orangescrum-app-config
docker volume create orangescrum-ee_orangescrum-postgres-data
docker volume create orangescrum-ee_orangescrum-reports-data
for v in app-files app-config postgres-data reports-data; do
docker run --rm \
-v orangescrum-ee_orangescrum-$v:/data \
-v /home/user/backup/YYYY-MM-DD:/backup \
alpine tar -xzf /backup/$v.tar.gz -C /data
done
Volume names must match the compose project
If the prefix differs from the source server, the stack creates its own empty
volumes and starts with no data. Confirm with docker volume ls before
starting.
#6. Start the stack
cd /opt/orangescrum
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail=50
Allow one to two minutes for health checks. The reports service waits for its database to be healthy before it starts.
#7. If the database did not come back — restore from the dumps
The volume restore is the fast path; the SQL dumps are the reliable fallback.
docker compose ps orangescrum-postgresdb # wait for healthy
docker exec -i orangescrum-ee-orangescrum-postgresdb-1 \
psql -U orangescrum postgres \
< /home/user/backup/YYYY-MM-DD/main-db.sql
docker exec -i orangescrum-ee-orangescrum-reports-db-1 \
psql -U postgres superset \
< /home/user/backup/YYYY-MM-DD/reports-db.sql
#8. Verify before you cut over
docker compose ps
curl -f http://localhost:8080 # application
curl -f http://localhost:8088 # reports
docker compose logs orangescrum-app --tail=30
docker compose logs orangescrum-postgresdb --tail=30
Then confirm the data really arrived:
docker exec -it orangescrum-ee-orangescrum-postgresdb-1 \
psql -U orangescrum postgres -c '\dt'
docker exec -it orangescrum-ee-orangescrum-postgresdb-1 \
psql -U orangescrum postgres -c 'SELECT count(*) FROM users;'
#Verification checklist
Work through every item before switching DNS
- All four containers report healthy
- Application answers on 8080
- Reports answer on 8088
- You can log in with existing credentials
- Projects, tasks and users are visible
- A file attachment downloads — a database-only restore looks perfect until someone clicks a file
- The reports dashboard loads existing dashboards
/dataon the host is intact- Scheduled jobs (digests, reminders) are present in cron
#9. Cut over
Update DNS or the load balancer
Point at the new server's address.
Reissue or move TLS certificates
A migration with a stale certificate presents as a total outage.
Keep the source server running
Until verification is fully complete. It is your rollback.
Re-check background jobs
Cron lives outside Docker and is the thing most often lost in a move.
#Troubleshooting
| Symptom | Resolution |
|---|---|
| Container keeps restarting | docker compose logs <service> — usually a wrong password in a secrets file |
| Database connection refused | Ensure the database container is healthy before the app starts |
| Port not accessible | ufw allow 8080/tcp, ufw allow 8088/tcp, or fix the proxy |
| Volume looks empty after restore | The tar restore failed or the prefix was wrong: docker run --rm -v <volume>:/data alpine ls /data |
Permission denied on /data | Re-apply ownership: chown -R www-data:www-data /data |
| Reports service will not start | It waits on its database — give it ~60s, then check its logs |
Moving to a newer Orangescrum version.