Data

Moving from another server

Bring an app and its data over from a pm2 server or any other host, without losing a write.

You have an app running under pm2, or by hand, on another server, with its data in SQLite files and an uploads folder. This moves it to Self Host Layer.

1. Deploy the app here first

Import it from GitHub as usual (New project), on the same branch. Its data will live in DATA_DIR (/srv/apps/<app>/data), the one folder the app can write that survives deploys.

  • If your app reads process.env.DATA_DIR, there's nothing to change.

  • If it reads its own variable, like SQLITE_PATH, set it on the app's Environment page to a path inside the data folder, like /srv/apps/<app>/data/app.db.

  • If it opens ./data relative to its working folder, change it to read DATA_DIR first:

    const dataDir = process.env.DATA_DIR || path.join(process.cwd(), "data");
    

    Otherwise it reads an empty copy inside the build folder instead of your data.

2. Export the data on the old server

Never copy a database file with cp while the app runs: you can get a half-written copy, and recent writes may still sit in its -wal file. This script copies the data folder, then replaces every SQLite database in the copy with a consistent snapshot of the live one, whatever its name. Set the first two lines:

APP=myapp
DATA=/var/www/myapp/data

command -v sqlite3 >/dev/null || apt-get install -y sqlite3
rm -rf /root/export && mkdir -p /root/export
cp -a "$DATA/." /root/export/
find /root/export -type f \( -name '*-wal' -o -name '*-shm' \) -delete
(cd "$DATA" && find . -type f ! -name '*-wal' ! -name '*-shm' | while IFS= read -r f; do
  if [ "$(head -c 15 "$f" | tr -d '\0')" = "SQLite format 3" ]; then
    rm -f "/root/export/$f"
    sqlite3 "$f" ".backup '/root/export/$f'"
    echo "$f: $(sqlite3 "/root/export/$f" 'PRAGMA integrity_check')"
  fi
done)
tar -czf "/root/$APP-data.tar.gz" -C /root/export . && rm -rf /root/export
ls -lh "/root/$APP-data.tar.gz"

Each database should print ok. The app keeps running while it does this.

If you don't remember where the data folder is, pm2 describe <app> shows the app's folder under exec cwd.

3. Import it

Either way, the result is the same:

  • In the panel: download the archive to your computer (scp root@old-server:/root/myapp-data.tar.gz .), open the app, go to Backups → Import data, choose the file and type the app's name to confirm.

  • On the new server: copy it over and import it there.

    scp myapp-data.tar.gz root@new-server:/root/
    ssh root@new-server layer import myapp /root/myapp-data.tar.gz
    

Before anything is replaced, the archive is unpacked and every SQLite database is checked with PRAGMA integrity_check. If one is damaged, nothing changes and you see which file failed. macOS clutter (._*, .DS_Store) is dropped, and every file becomes readable by the app alone.

Then the app stops (its Redis too), the folders are swapped and everything starts again. The data that was there before is kept in /srv/apps/<app>/data.before-restore-… until the next import or restore.

Postgres and MySQL

They aren't part of the archive. Dump PostgreSQL on the old server with pg_dump --format=custom and load it with pg_restore --no-owner into the DATABASE_URL shown on the app's Databases page. For MySQL, mysqldump the database and load it with mariadb using the same URL's credentials.

4. Switch traffic

  1. Add the app's domain on its Domains page. It goes live only once public DNS points here, so nobody lands on a broken site.
  2. Change the DNS record at your provider.
  3. When traffic has moved, stop the old app: pm2 stop myapp.

If the old app took writes after your export, like sales or signups, stop it first, then export and import once more before switching DNS.

Delete the archive on both servers afterwards: it holds your app's data.