Migrating to a new machine¶
Moving a whole Longbox install from one host to another — a failing disk, a better box, a move from a laptop to a real server.
This is written for bare metal to bare metal (systemd on both ends), because that is the case with sharp edges. Docker is a shorter version of the same thing and is covered at the end.
Read this before you start copying
Two things catch almost everyone, and both are silent — nothing errors, the library just comes up subtly wrong.
- Some settings are not in
/etc/longbox.env. SMTP and OAuth credentials set through the admin pages live in the database, which takes precedence over the environment. Copy the env file alone and mail quietly stops working. See Configuration. - Comic paths are stored absolutely. If the collection lands at a
different path on the new host, every comic is marked missing until you
run
relocate.
What moves, and what does not¶
| Move it? | Why | |
|---|---|---|
<data>/longbox.db |
Yes | Accounts, catalogue, reading state, ratings, every metadata edit, and the settings that are not in the env file. Irreplaceable. |
<data>/uploads/ |
Yes | The only copy of anything added through the browser. Not in the default backups. |
<data>/covers/ |
Optional | Regenerates on re-import, but copying it is far faster than rebuilding thousands of thumbnails. |
<data>/backups/ |
Optional | Old snapshots. Carry them if you want the history. |
/etc/longbox.env |
Yes | Paths, ports, public URL, trusted proxies. Review it on arrival rather than copying blindly. |
| The comics themselves | Yes | Longbox only ever reads them, but it cannot catalogue what is not there. |
/var/cache/longbox |
No | Extracted pages. Rebuilt on demand; copying gigabytes of derived JPEGs achieves nothing. |
longbox.db-wal, longbox.db-shm |
No | They belong to a running database. Copying them alongside the database mixes two states together. Stop the service and they go away. |
/opt/longbox |
No | The application and its virtualenv. The installer builds both, and a virtualenv is not portable between hosts anyway. |
Before you begin¶
Check the new host will run a version at least as new as the old one. Migrations are one-way: a database at schema v6 will not open on a release that only knows v5.
python3 -c "import sqlite3;print(sqlite3.connect('/var/lib/longbox/longbox.db').execute('PRAGMA user_version').fetchone()[0])"
Installing from the same git commit, or a newer one, satisfies this. Newer is fine — migrations run forward automatically on first start.
1. Install Longbox on the new host¶
Get a working, empty install running first. Proving the plumbing works before any data is involved means a later failure has only one possible cause.
git clone <your-repo> longbox && cd longbox
sudo ./deploy/install.sh
Do not register an account. The first account to register adopts an unowned library, and you are about to bring a database that already has one.
sudo systemctl stop longbox
2. Stop the old server¶
sudo systemctl stop longbox
Stopping first is not optional. SQLite in WAL mode spreads committed data
across longbox.db and longbox.db-wal, so copying the database out from
under a running server can capture a torn state.
With the service stopped the data directory is consistent and can be copied as it stands. Take a labelled snapshot as well, so there is a known-good artefact independent of the copy:
sudo longbox-cli backup --label pre-migration --include-uploads
3. Copy the data directory¶
sudo rsync -aHAX --info=progress2 /var/lib/longbox/ newhost:/tmp/longbox-data/
-a preserves ownership by numeric uid, which is exactly the thing to be
careful about: the longbox service user almost certainly has a different uid
on the new host, because both were allocated by whichever packages happened to
be installed first. Do not try to make the numbers match — fix it on arrival:
sudo rsync -a /tmp/longbox-data/ /var/lib/longbox/
sudo chown -R longbox:longbox /var/lib/longbox
sudo rm -f /var/lib/longbox/longbox.db-wal /var/lib/longbox/longbox.db-shm
Removing the WAL and shared-memory files matters even after a clean stop — they are worthless on the new host and actively harmful if they are stale.
4. Copy the comics¶
sudo rsync -aHAX --info=progress2 /srv/comics/ newhost:/srv/comics/
Put them at the same path if you possibly can. It makes the next step unnecessary.
5. Tell the library where the comics went¶
Only needed if the collection landed somewhere different. Comic paths are stored absolutely, so without this every comic shows as missing — and re-importing is not the fix, because it strands reading state, ratings and metadata edits on the old rows.
sudo longbox-cli relocate --dry-run /old/path /srv/comics
sudo longbox-cli relocate /old/path /srv/comics
It rewrites comic paths, watched folders and per-account library folders in one pass, touching the database only and never a file. A rewrite that would land on a path another comic already occupies is skipped rather than forced, and anything still absent afterwards is reported rather than quietly marked present.
6. Review the configuration¶
Copy /etc/longbox.env across, then read it — several values describe the
old host rather than the service:
sudo rsync -a oldhost:/etc/longbox.env /etc/longbox.env
sudo chmod 640 /etc/longbox.env
| Setting | Check |
|---|---|
LONGBOX_BROWSE_ROOTS |
Do those paths exist on this host? |
LONGBOX_DATA_DIR, LONGBOX_UPLOAD_DIR, LONGBOX_BACKUP_DIR |
If any sits outside the defaults, re-run sudo ./deploy/install.sh so the ReadWritePaths drop-in is regenerated here. Without it every write fails with EROFS on a directory that exists and is owned correctly. |
LONGBOX_PUBLIC_URL |
Only changes if the name changes. |
LONGBOX_TRUSTED_PROXIES |
Is the proxy still on loopback, or somewhere else now? |
LONGBOX_PORT |
Free on this host? |
7. Start it¶
sudo systemctl start longbox
journalctl -u longbox -f
Migrations run automatically on startup if the new host is on a newer release.
8. Verify — properly¶
Worth not skipping, because it can prove something much stronger than "the site loads".
curl -s localhost:8080/healthz
sudo longbox-cli list-users
sudo longbox-cli verify
verify re-reads every comic in full and compares SHA-256 against the hashes
stored on the old host. A clean run is end-to-end proof that the collection
arrived byte-identical — a far better answer than eyeballing the library grid.
On a large collection it takes a while; it is still the right thing to run once.
Then, in a browser: open a comic and confirm it resumes at the page you were on. Reading progress is the loss people notice.
9. Move the name¶
Only once the above passes.
- Point DNS, or the LAN
dnsmasqentries, at the new address. - Move the reverse-proxy site block to the new backend — see Reverse proxy and HTTPS.
- Update the OAuth redirect URIs in the Google, Apple and Facebook consoles if the public URL changed. External sign-in fails with a redirect-mismatch error until you do, and that error surfaces at the provider, not in Longbox's logs.
Sessions survive the move, because tokens are stored as hashes in the database and the database came along. If the host name changed everyone signs in again regardless, since cookies are scoped to the domain.
OPDS tokens survive too, so phones and e-readers keep working — provided they were pointed at a name rather than an address.
10. Decommission deliberately¶
Keep the old machine's data directory until you have read a few comics on the new one and taken at least one fresh backup there. The old host is the only rollback that exists.
Docker¶
Shorter, because the container is disposable and only the volumes matter.
docker compose stop
sudo rsync -aHAX ./data/ newhost:/srv/longbox/data/
sudo rsync -aHAX /srv/comics/ newhost:/srv/comics/
On the new host, clone the repository, restore docker-compose.yml with the
same mount paths, put data/ where the compose file expects it, and:
docker compose up -d --build
Copying the image is not the migration. The image is rebuilt from the
repository in seconds and holds none of your data; data/ is the migration.
Everything under What moves, and what does not
still applies, including relocate if the comics mount lands at a different
path inside the container. The path stored in the database is the one the
container sees, not the one on the host.