A forum backup is not a zip of the admin panel. It is the database (people, posts, permissions, settings) plus the files members uploaded, plus a way to put both back onto a machine that can run the same software version. Miss the files and avatars vanish. Miss the database and you have a pretty empty tree. Miss the restore drill and you have a rumour.
This is an engine-agnostic inventory: what class of thing to copy on XenForo, Discourse, phpBB, Flarum, and NodeBB, where those products actually put it, and the rules that do not change. It is not the XenForo-only backup, restore, and host-move article — that one owns same-version restore, Board URL, cookies, and the importer overview. It is not a platform migration map. Importing phpBB into XenForo is a different job from copying phpBB so you can put phpBB back.
Discourse backup settings below come from Meta topic 14855 (“Configure automatic backups for Discourse”). XenForo file rules come from the official upgrade/install manuals and the existing XF article. phpBB, Flarum, and NodeBB are described as classes of files. This article will not invent product-specific dump flags that were not sourced.
The invariant (every engine)
Write three lines. If you cannot, you do not have a backup procedure.
- Structured data — the database or data store (MySQL/MariaDB, PostgreSQL, MongoDB, Redis-as-store).
- Member bytes — avatars, attachments, images, other uploads. Sometimes a second object store.
- Config secrets — the file or env that points at (1) and (2), plus mail, cookies, and keys.
Then a fourth line people skip:
- A restore you have actually done onto a scratch machine, at the same software version (or the product’s documented restore path).
A snapshot of the VPS disk can be (1)+(2)+(3) if the snapshot is consistent and off-box. A zip of public_html is usually (3) plus PHP and a missing (1). A Discourse “backup” with uploads off is (1) without (2). A host “download website” button is a lottery.
Off-box means not the same disk that will die. A copy in /backups on the web root is a public leak waiting for a misconfigured server. A copy on the same VPS is a deleted droplet.
Close the door when the copy must be consistent
A backup taken while members post can still restore. It is a picture of a moving target. For a planned move, a migration snapshot, or a pre-upgrade copy you might have to trust:
- XenForo: Setup → Options → Board active off. Staff with ACP can still work. Wait a minute for last writes, then dump. Do not delete cron.
- Discourse: a scheduled backup already runs at a quiet time (default 3:30 UTC). A manual backup from the admin Backups tab is the “I am about to rebuild” copy.
- phpBB / Flarum: disable the board or put the site in a maintenance mode your product actually ships. Do not invent a XenForo option on phpBB.
- NodeBB: stop or pause writes if you need a frozen Mongo/Redis picture; otherwise use the store’s own consistent snapshot tools.
Then copy. Then open the door.
XenForo — database + data/ + internal_data/
XenForo’s public tree is PHP. The member-generated board is not.
| Piece | Where | If you omit it |
|---|---|---|
| Database | MySQL / MariaDB named in src/config.php |
Empty forum |
data/ |
Web root; public avatars, thumbs | Official: attachments and avatars may have been removed |
internal_data/ |
Web root (or moved off the web root); raw attachments, code_cache, temp, install-lock.php |
Installer offers a fresh install. Attachments also live here depending on config |
src/config.php |
Not in the Customer Area zip | Board cannot find the database |
Merge, never replace data/ and internal_data/ when you upload application files. Official upgrade note: some FTP clients replace the server directory with exactly what you uploaded. That is how those two folders disappear. If /install/ offers a fresh install after a file upload, recreate internal_data/install-lock.php (contents do not matter) and do not click through the wizard on a live database.
Restore is same XenForo version. A 2.3.6 dump on 2.3.10 files is an accidental upgrade. A 2.3 dump on 2.2 files is a crash. Official: you cannot downgrade; the restore is the undo.
After restore onto a new host: Board URL and cookie path. Everyone looking like a guest is usually cookies, not a missing table. The XF article owns that autopsy.
There is no official “Backup now” button that writes a complete disaster archive. You use the host’s tools. A typical database dump shape (names are yours; this is the same example the XF article already used, not a magic official flag set):
mysqldump --single-transaction --routines --triggers -u USER -p DBNAME > xf-$(date +%F).sql
--single-transaction is the InnoDB-friendly consistent read. Official 2.3 materials assume InnoDB for almost everything. Store the dump off the web root.
tar -czf xf-data-$(date +%F).tar.gz data internal_data
Also keep: PHP version, MySQL version, add-on list, parent style, whether Redis is on. If you cannot write that list, you have files, not a procedure.
php cmd.php xf:install --clear wipes xf_ tables. It is not a backup tool.
Deep restore, clone, and importer overview: safe backup and migration for XenForo.
Discourse — the product that already has a backup button
Official optional feature: automatic backups. Turn them on before the site has strangers. Download one copy to somewhere that is not the VPS.
Meta 14855 settings (self-host admin Backup section):
| Setting | What it does | Documented default / range |
|---|---|---|
backup_frequency |
Interval in days | Default 7 (weekly). 1 = daily. 0 = off. Maximum 30 |
backup_time_of_day |
When the job runs | Default 3:30 UTC |
backup_with_uploads |
Include uploads | Default on. Off = database only |
maximum_backups |
How many to keep | Default 5. Older are deleted |
remove_older_backups |
Age cap in days | Blank = disabled |
Local path on a standard standalone install: /var/discourse/shared/standalone/backups/default.
Daily is not the official default. Jeff Atwood’s position on that topic, as operations: weekly is a solid starting point; daily is a setting you choose. Discourse will not run hourly backups from this setting — more frequent copies are external.
Hosted retains whatever the plan says. Do not assume the local path exists on a box you cannot SSH. Official hosted ↔ self-host moves use a backup file. The pricing FAQ is explicit that Discourse-to-Discourse via backup is the easy direction.
Before every rebuild that scares you, take a backup. Rebuilds are routine. They are also when people discover they never downloaded one. The hosting cookbook already walked RAM, app.yml, and this table.
S3: backups and uploads are not the same bucket
Meta 14855 is blunt: storing backups and regular uploads in the same bucket and the same folder is not supported and will not work. Give s3_backup_bucket a prefix (my-awesome-bucket/backups) and keep those objects private.
Admin-panel path: create a bucket; set s3_backup_bucket, s3_access_key_id, s3_secret_access_key, s3_region; set backup_location to S3. After that, backups upload to S3 and are no longer kept locally (local disk is temp during backup/restore). Browse them under the admin Backups tab. Download one offsite anyway.
app.yml env vars work too. If you only want S3 for backups, omit DISCOURSE_USE_S3, DISCOURSE_S3_CDN_URL, and DISCOURSE_S3_BUCKET — those belong to the uploads story. Do not copy a full uploads config because you wanted offsite backups.
AWS lifecycle to Glacier (or another vendor’s archive class) is how five weekly backups stay cheap. Confirm minimum retention — Glacier-class storage often bills a minimum number of days even if you delete early.
IAM: instance roles work when s3 use iam profile is on and the region is set. Policies must list both the uploads bucket and the backups bucket if you use both. “Access Denied” is usually the policy.
A backup is not a backup until you have restored it once on a scratch site.
phpBB — database + the files tree
phpBB is a PHP app with a MySQL/MariaDB database and member files on disk.
Copy these classes:
- The database named in phpBB’s
config.php. - The
files/tree as phpBB stores attachments (and any customized attachment path). - Avatar storage as your board configured it (paths vary; do not assume one folder name if you changed it).
config.phpand any local modifications you would not want to reconstruct from memory.- The installed PHP application if you are not willing to re-download that exact phpBB version plus extensions.
The phpBB → XenForo article already said a migration snapshot is “database dump + files/.” That is also the disaster snapshot if you intend to stay on phpBB.
This article will not invent a phpBB-specific mysqldump flag set or an ACP “Download backup” button. Use your host’s SQL export or a consistent InnoDB dump the same way you would for any PHP app, and prove it by restoring onto a copy.
phpBB 3.1 or 3.0 is outside XenForo’s official importer range (3.2–3.3). That does not change how you back up those versions. It changes whether you can import them later.
Flarum — database + public / storage / Composer state
Flarum is PHP + Composer + a Mithril SPA. Backups are “the same species as any PHP app,” as the Flarum article already put it.
Copy these classes:
- The database.
- The public files the web server serves (including compiled assets you cannot cheaply rebuild if you do not have Composer on the restore box).
- The storage directories the docs name for your version (logs, cache, uploads — names move between 1.x and 2.0 RC; follow your version’s docs, do not memorize a 2021 gist).
composer.json/composer.lockand the env/config that holds DB credentials.- A note of PHP version and whether you are on 1.8.x life support or 2.0 RC. Restoring a 1.x dump onto a 2.0 code tree is an upgrade, not a restore.
There is no official “Flarum Cloud” with Discourse’s gravity. You own the box. Extension Manager can install any Composer package — treat admin like root when you decide who can take and restore backups.
This article will not invent an ACP backup button name the 2.0 RC might have renamed.
NodeBB — the data store + uploads
NodeBB is a long-running Node process. The store is MongoDB or Redis, not “a MySQL dump like phpBB.”
Copy these classes:
- A consistent dump/restore of the store you actually use. Mongo: dump/restore that database. Redis-as-primary-store: a Redis persistence story you have tested, not an unexamined
dump.rdbyou hope is current. - Uploaded files NodeBB stored on disk or on the object store you configured.
- Config (
config.jsonor your env), the Node version (≥ 22 in current docs), and whether the data store is Mongo or Redis so you do not restore the wrong tool. - Plugin list (
nodebb-plugin-*). An abandoned plugin is a restore blocker the same way an abandoned XF add-on is.
The NodeBB article already said: a Node process without a restore drill is a demo. Process managers (systemd / pm2 / docker) are your choice. Back them up as config, not as folklore.
This article will not invent a nodebb backup flag. Use the store’s tools plus the upload directory, and restore onto a second machine.
Object storage and CDNs (all engines)
If uploads live on S3 (or a clone):
- The bucket is part of the backup story. Versioning + a second account is better than “the forum DB knows the keys.”
- Discourse: backup bucket ≠ upload bucket/folder.
- XenForo: if you moved
data/to object storage, your disaster copy must include that bucket or you will restore a board with broken avatars. - A CDN in front of uploads is not a backup. Purging a CDN does not recreate a file.
If you only snapshot the VM and the bucket is elsewhere, a deleted-bucket incident is not covered. Copy both.
What to write down besides the files
A restore in the dark fails on trivia.
- Software version (XF 2.3.x, Discourse version at last rebuild, phpBB 3.3.x, Flarum 1.8.x vs 2.0 RC, NodeBB v4.x).
- PHP / Node / DB versions.
- How mail works (SMTP, Discourse ID without SMTP — no digest until you add it).
- TLS / hostname / whether the app lives in a subdirectory.
- Who has registrar, DNS, and the off-box store.
- The last restore date and the hostname you restored onto.
Store config.php, app.yml secrets, and S3 keys like passwords. A backup archive that includes them should not sit in a public ticket.
Cadence (so weekly is a decision)
| Situation | A reasonable starting cadence | Why |
|---|---|---|
| Discourse self-host, default | Weekly (backup_frequency 7) |
Official default; Atwood: weekly is a solid start |
| Busy XF / phpBB / Flarum | Weekly full + more frequent DB if you have the ops | Files change slower than posts |
| Legal / safety community | Daily DB, weekly files, off-box | You will be asked for a day, not a month |
| Pre-upgrade / pre-rebuild / pre-import | Now, then restore once | The only undo |
| “Hourly” | External, not Discourse’s setting | 14855: more frequent = external |
maximum_backups 5 on Discourse means you do not have a year of history on the box. If you need a quarter of archives, that is S3 + lifecycle, not a higher Discourse default.
Prove it (the only test that counts)
A file that has never been restored is a rumour. Once per quarter, or after any procedure change:
- Provision a scratch box (or a throwaway hosted instance).
- Install the same version.
- Restore store + files + config. Point config at the scratch credentials.
- Open: one old avatar, one old attachment, one staff login, one private room, mail disabled or sunk.
- Confirm you did not email real members from scratch.
- Write the date on the inventory sheet.
- Destroy the scratch site.
If step 4 fails, production’s last archive is also a rumour. Fix the archive before you need it.
XenForo-specific: login must survive a refresh after Board URL and cookie path are set. Discourse-specific: restore the .tar.gz the product wrote; do not hand-merge Postgres unless you know you should. NodeBB-specific: restore the store you actually run.
Security is why this is a checklist tag
Backups are a confidential copy of every private message and every email address. Treat the archive like the database.
- Encrypt off-box copies if the store is not already a locked bucket.
- Do not leave
.sqlunder a web-reachable folder. - Guest fetch of XenForo
/src/config.phpand/internal_data/should fail (security baseline). - Who can click Discourse Backups → download is who can take the community home. Limit that.
- After a staff departure, rotate storage keys. The old laptop may still have last month’s tar.
A restore is also how you survive ransomware on the VPS. If the only copy is on the VPS, you have a copy, not a backup.
How this sits next to migration
| You are about to… | Backup you need |
|---|---|
| Upgrade XF | Full XF trio; restore is the only downgrade |
| Rebuild Discourse | Product backup, downloaded off-box |
| Import phpBB → XF | Snapshot source (DB + files/) and backup destination XF (the thing the importer can destroy) |
| Import XF → Discourse | XF snapshot + a clean Discourse backup from before xenforo.rb |
| Move hosts, same engine | This inventory, then the product’s URL/cookie pass |
| Decide whether to move at all | Not a backup problem — should you migrate |
Never run the first import against the only copy of the destination.
Printable inventory
Copy one column per site you run.
Every site
- Structured data location named (DB name / Mongo / Redis / Discourse backup).
- Upload / file class named (
data/+internal_data/, Discourse uploads, phpBBfiles/, Flarum storage, NodeBB uploads). - Config / secrets location named.
- Off-box destination named (and not the web root).
- Version pinned.
- Last successful restore date.
XenForo
- DB +
data/+internal_data/(merge, never replace). -
install-lock.phpexists after any file upload. - Same XF version on restore.
- Board URL + cookie path on the list for after.
Discourse
-
backup_frequency(default 7; 0 = off; max 30). -
backup_time_of_day3:30 UTC unless you changed it. -
backup_with_uploadson unless you have another uploads copy. -
maximum_backupsunderstood (default 5). - Local path or S3
backup_location. -
s3_backup_bucket≠ uploads folder. - One backup downloaded off the VPS.
phpBB
- Database +
files/+ avatar paths +config.php.
Flarum
- Database + public + storage + Composer lock + PHP version.
NodeBB
- Store dump (Mongo or Redis) + uploads + Node version + plugin list.
After any restore drill
- Old avatar, old attachment, staff login, private room.
- Outbound mail sunk.
- Scratch site destroyed.
- Date written down.
What this is not
It is not a XenForo host-move runbook. It is not Meta 14855 reprinted in full. It is not a mysqldump manual for every host panel. It will not invent phpBB or Flarum dump switches. It is not encryption theatre that replaces taking the copy.
If you run one engine, read that engine’s section twice and ignore the romance of the others. If you run two, you need two restore drills.
Takeaways
- Backup = store + member files + config, off-box, restore tested, same version.
- XenForo: DB +
data/+internal_data/. Merge those folders. Never replace them with an empty upload. - Discourse: Meta 14855 — weekly default, 3:30 UTC, uploads on, keep 5, local path
…/backups/default, separate S3 backup bucket. - phpBB / Flarum / NodeBB: copy the class of files those apps use; do not invent flags.
- A zip you have never restored is not a backup.
Take tonight’s copy off the box. Restore it once on something you can throw away. Then you may upgrade, migrate, or sleep.

