Skip to main content

Migration from Remnawave

XLADA is a fork of Remnawave under AGPL-3.0-only, so the migration comes down to swapping the images: service names, the database volume, and the network stay the same, and PostgreSQL comes up on top of the same data directory. Below is the in-place procedure, on the same server.

What is preserved​

Only the container images change. Table and column names are deliberately not renamed in the fork, so the live database stays valid:

ModelTable
Usersusers
Nodesnodes
Hostshosts
Administratorsadmin
API tokensapi_tokens
HWID deviceshwid_user_devices
Config profilesconfig_profiles
Internal and external squadsinternal_squads, external_squads
Settingsremnawave_settings

The .env file is carried over without renaming keys: APP_SECRET, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DATABASE_URL, PANEL_DOMAIN, FRONT_END_DOMAIN, SUB_PUBLIC_DOMAIN, and the rest are read the same way. The subscription page still uses variables with the REMNAWAVE_ prefix (REMNAWAVE_PANEL_URL, REMNAWAVE_API_TOKEN).

XLADA migrations are additive only: a table for the administrator action log and second-factor fields are added. So the migration is reversible — it is enough to bring back the previous panel image.

Preparation​

Before you start, verify the actual names and versions — whether the database comes up depends on them:

docker volume ls
docker ps --format 'table {{.Names}}\t{{.Image}}'
Volume and PostgreSQL version

The migration file keeps the volume name of the old installation (in the standard compose that is remnawave-db-data). If your volume is named differently, do not rename it — instead bring the name: directive in the compose file to the existing name.

The data directory is incompatible between major versions. The new XLADA compose file targets PostgreSQL 18, but for the migration you take the same major version that created your volume — in the ready-made file that is postgres:17.6. Moving to 18 happens later, as a separate step via dump and restore.

Backup​

Dump the database and save the .env. Keep the dump outside the volume with the database until the migration is verified.

docker exec remnawave-db pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \
> ~/remnawave-backup-$(date +%F).sql
tail -n 1 ~/remnawave-backup-*.sql # expected: -- PostgreSQL database dump complete

cp deploy/.env ~/remnawave-env-backup
chmod 600 ~/remnawave-env-backup

Stopping the old stack​

cd deploy && docker compose down

Stop without the -v flag: it removes volumes together with the database.

Starting XLADA on the same database​

Take the docker-compose.migrate-from-remnawave.yml file from the XLADA repository. It differs from the regular compose file only in that it keeps the service names and the volume of the old installation; the regular compose file would have created an empty database. In .env, add the optional variable with the image tag:

XLADA_VERSION=latest

Start:

docker compose -f docker-compose.migrate-from-remnawave.yml up -d
docker logs -f remnawave

Migrations are applied automatically when the container starts: the entrypoint runs prisma migrate deploy and prisma db seed. In the log, wait for the lines Migrations deployed successfully! and Nest application successfully started.

Automation

The same steps are performed by the migrate-to-xlada.sh script: it puts the dump, .env, compose file, and volume lists into backups/<date-time>/, checks the dump for completeness, stops the stack without -v, and brings up XLADA.

Verification​

# the panel responds
curl -s -o /dev/null -w '%{http_code}\n' https://panel.example.com/api/auth/status

# the database sees the users — the count before and after must match
docker exec remnawave-db psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SELECT count(*) FROM users;"

# the migrations were applied
docker exec remnawave-db psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c \
'SELECT migration_name FROM _prisma_migrations ORDER BY finished_at DESC LIMIT 5;'

The migration list must contain 20260912120000_add_admin_audit_log and 20260912130000_add_admin_totp. Then log in to the panel with the previous administrator credentials and check that the nodes on the Nodes page are connected.

What not to do​

danger
  • docker compose down -v — the -v flag removes the volume together with the database. This is the only command that actually destroys users.
  • Changing APP_SECRET — password hashes, the JWT signature, and TOTP secret encryption depend on it. After a change no one will be able to log in to the panel.
  • Renaming volumes, services, and networks — compose will create new empty ones, and the old ones will stay hanging next to them. DATABASE_URL points to the host remnawave-db.
  • Upgrading the PostgreSQL major version on an existing volume and editing already applied migrations — Prisma compares checksums in _prisma_migrations.

Do not delete the dump and the .env backup until you have made sure that login, nodes, and subscriptions work.

What's next​