Skip to content

Migrating from another platform

We ship free, open-source migration tools as a standalone CLI. It’s not a plugin on purpose: you run it once on your server, it writes straight into the Xentium database, and all it leaves behind is the migration log (ACP → System → Migration History). You can watch an import there while it runs: progress for each kind of record, the log as it is written, and the issues so far. If the process dies, the page says the run stopped responding instead of showing it as running forever. The page also gives you the command back for a dry run or a re-run, with connection strings left out, because we never store them.

Adapter Source Status
@xentium/migrate-wp WordPress, from a WXR export file (wp-admin → Tools → Export). You don’t need a WordPress install or database access. ✅ Available
@xentium/migrate-phpbb phpBB 3.1+, over a direct MySQL connection. The xentium-forum plugin must be installed and enabled on the target (we check before anything runs). Users keep their passwords. ✅ Available
@xentium/migrate-xenforo XenForo 2.x, over a direct MySQL connection. Needs the xentium-forum plugin on the target. Users with native bcrypt hashes keep their passwords. ✅ Available
@xentium/migrate-vbulletin vBulletin 3, 4, 5 and 6, over a direct MySQL connection (we detect the version). Needs the xentium-forum plugin on the target. Users keep their passwords. ✅ Available
@xentium/migrate-woltlab WoltLab Suite 5 and 6 with or without Burning Board, and Burning Board 4, over a direct MySQL connection. Brings members, articles and the forum. Needs the xentium-forum plugin on the target. Users keep their passwords. ✅ Available
Terminal window
# 1. Always start with a dry run: it inspects, extracts and reports, and writes NOTHING
pnpm --filter @xentium/migrate-wp migrate -- \
--file my-export.xml \
--db "postgresql://user:pass@localhost:5432/xentium" \
--dry-run
# 2. The real run (asks you to confirm; --yes skips that)
pnpm --filter @xentium/migrate-wp migrate -- --file my-export.xml --db "postgresql://…"
# Forums: point at the board's MySQL database instead of a file
# (same flags for @xentium/migrate-xenforo, -vbulletin and -woltlab;
# --prefix overrides the table prefix; for WoltLab it's the instance number, e.g. 1)
pnpm --filter @xentium/migrate-phpbb migrate -- \
--source-db "mysql://user:pass@host:3306/phpbb" \
--db "postgresql://…" --dry-run
# Avatars are files on the old site. Point --files at the old site's folder and
# --uploads at Xentium's upload folder (UPLOAD_DIR); without them, the run reports how
# many avatars are waiting, and running again with both flags brings just those over.
pnpm --filter @xentium/migrate-phpbb migrate -- \
--source-db "mysql://…" --db "postgresql://…" \
--files /var/www/old-forum --uploads /srv/xentium/var/uploads

The target has to be an installed Xentium (run the wizard first). Migrations can be resumed: we track every imported item in an import map, so if a run is interrupted, the next one continues where it stopped instead of creating duplicates. Accounts that match an existing user by email are merged into that user. Big boards are fine: posts, private messages and likes are read and written in pages of 2,000, so a board with millions of posts needs no more memory than a small one.

Add --redirects <folder> to the real run and we write two files there once the import is done: an nginx file that sends every old address (threads, posts, boards, members, articles) to its new page with a permanent redirect, and a CSV of the same list for any other web server. The top of the nginx file says where each part goes. Posts land on the right page of their thread, scrolled to the post. We know every platform’s URL styles, with or without friendly URLs and in any folder, and WordPress permalinks come from the export itself.

Terminal window
pnpm --filter @xentium/migrate-phpbb migrate -- \
--source-db "mysql://…" --db "postgresql://…" --redirects /srv/xentium/redirects
# Write them again later without importing anything (say, after changing the forum's
# posts per page): the same command with --redirects-only
  • Users: accounts, display names, join dates and a best-effort role mapping (admins → Administrator, moderators → Moderator). The old board’s other member groups (VIP, Donors and so on) are recreated by name with members in them, but with no permissions: what a group may do differs too much between platforms, so you grant it under ACP → Members → Groups. A name you already use for a group that grants something gets the platform in brackets, so nobody gains rights by accident. Their last visit on the old site shows as “Last seen” on their profile, and smiley codes in posts and messages (:), :D, :lol:…) become emoji, leaving code and links as written. Imported members never saw your terms, so while you have terms or a privacy policy published, each of them is asked once to accept them on their first visit (declining signs them out). Where the source has password hashes (the database adapters; for XenForo we pull them out of its serialized auth data), users keep their passwords: we check the old hash on their first login and quietly upgrade it to argon2. WXR exports don’t contain hashes, so WordPress users reset their password once. Personal info comes too: date of birth (only full dates) and website, into Xentium’s own profile fields. Avatars are copied when you pass --files and --uploads (vBulletin can also keep them in its database): only real JPEG, PNG, GIF or WebP images up to 2 MB, like a normal upload. Bans and warnings still in force carry over (XenForo, phpBB, vBulletin; WoltLab’s warning system isn’t public), under the moderator who issued them where they came over too; expired, reversed and pardoned ones stay out. A username that’s already taken gets a suffix, and the report lists every rename.
  • Content: categories with their hierarchy, posts/articles (converted to the TipTap format; headings, lists, quotes, code, images and links survive), tags and member comments (threaded one level deep). For forums: the whole node tree, threads (locked, pinned, view counts, tags, and their prefix: XenForo and vBulletin prefixes and WoltLab labels become thread prefixes with their colour and boards) and posts with their BBCode converted. Posts and messages arrive in exactly the HTML our editor saves (paragraphs, code blocks, editor links), so they look like native posts and editing one changes nothing; outside images become links, as the editor keeps no outside images in posts. Thread subscriptions carry over for the threads that came over. Profile wall posts with their replies (XenForo, vBulletin, WoltLab) and WoltLab article comments, on the articles that came over, come too.
  • Social graph where the source has one: XenForo reactions become likes (every reaction type turns into a like), follows carry over 1:1 and ignores become blocks. phpBB foes become blocks. vBulletin 5/6 positive reputation on a post becomes a like and member follows carry over; vBulletin ignore lists become blocks. WoltLab reactions on posts become likes, and its follows and ignores carry over. phpBB’s friend flag is a private buddy list, so we don’t turn it into public follows. A block cancels follows in both directions, as it does in Xentium, and we recount every like and follower total.
  • Private messages (phpBB, XenForo, vBulletin, WoltLab) become conversations when the xentium-messaging plugin is enabled on the target; without it they’re counted and reported, and a later run brings them over. Each conversation goes only to its participants, ones a member deleted or hid arrive archived, ones they left show them as having left, and imported conversations count as read. vBulletin 3/4 and phpBB store single messages, so we group their reply chains back into conversations.
  • Not migrated: themes and styling (Xentium has its own appearance system), the old platform’s plugins, guest comments and guest forum posts (posts on Xentium need an account), signatures, polls, custom profile fields, and attachments (imported posts link to them by URL). The migration report lists everything we skipped and why.