Migrate a XenForo forum to Discourse

You put the files wherever you want that is accessible from the machine doing the import and put that path here:

  ATTACHMENT_DIR = '/tmp/attachments'

If you’re running inside a docker container, and put it under var/discourse/shared/standalone/tmp/attachments then you’d use /shared/tmp/attachments for the path. You can check by looking for the files when you’re inside the container.


Okay Jay thank you for information, I’m just doing clean install this time with attachments folder setup, I will put then in /shared/tmp/attachments and get back here with results.

One quick question… is there anyway to import password for account’s too?

Can someone look where is problem?

root@my-app:/var/www/discourse# RAILS_ENV=production bundle exec ruby script/import_scripts/xenforo.rb
Loading existing groups...
Loading existing users...
Loading existing categories...
Loading existing posts...
Loading existing topics...

creating users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 100 already imported users
Skipping 4 already imported users

importing categories...
       23 / 23 (100.0%)  [326682 items/min]
creating topics and posts
       74 / 74752 (  0.1%)  [99817 items/min]  Traceback (most recent call last):
        18: from script/import_scripts/xenforo.rb:396:in `<main>'
        17: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        16: from script/import_scripts/xenforo.rb:32:in `execute'
        15: from script/import_scripts/xenforo.rb:174:in `import_posts'
        14: from /var/www/discourse/script/import_scripts/base.rb:869:in `batches'
        13: from /var/www/discourse/script/import_scripts/base.rb:869:in `loop'
        12: from /var/www/discourse/script/import_scripts/base.rb:870:in `block in batches'
        11: from script/import_scripts/xenforo.rb:180:in `block in import_posts'
        10: from /var/www/discourse/script/import_scripts/base.rb:490:in `create_posts'
         9: from /var/www/discourse/script/import_scripts/base.rb:490:in `each'
         8: from /var/www/discourse/script/import_scripts/base.rb:491:in `block in create_posts'
         7: from script/import_scripts/xenforo.rb:186:in `block (2 levels) in import_posts'
         6: from script/import_scripts/xenforo.rb:315:in `process_xenforo_post'
         5: from script/import_scripts/xenforo.rb:324:in `process_xf_attachments'
         4: from /usr/local/lib/ruby/2.6.0/set.rb:338:in `each'
         3: from /usr/local/lib/ruby/2.6.0/set.rb:338:in `each_key'
         2: from script/import_scripts/xenforo.rb:326:in `block in process_xf_attachments'
         1: from /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/core_ext/string/filters.rb:22:in `squish!'
/usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/core_ext/string/filters.rb:22:in `gsub!': can't modify frozen String (FrozenError)

I’ve put this time attachments to: var/discourse/shared/standalone/tmp/attachments

I’ve found it… problem is that a folder containing attachments for import MUST be inside Docker app in patch /var/www/discourse/…/tmp/attachments

Now it’s OK script is going on, it will finish in ~1.5h

edit: It seems that again import attachments not working… im currently at 78% of import, and in every post where we had some picture or attachments stay [ATTACH]9788[/ATTACH] and number between tags and number of uploaded so next one is [ATTACH]9789[/ATTACH]…

Any help with this will be glad :confused:

1 Like

If you imported any posts before you properly set the attachment_dir, you’ll need to delete all posts and start the import again.

Unfortunately import is done right now and I dont see in log any information regardings attachments?

If you ran the import script and the attachment directory didn’t exist it skipped the attachments.

I didn’t do that… I’ve runned fresh import with attachments in place.

I don’t think that I can help you here. If you have a budget, please see https://www.literatecomputing.com/discourse-migration/

Here is my last piece of advice: I’m fairly certain that the attachments are not where the script could find them and that you will need to delete everything and start again.

Before you run the script again, from inside the folder, you should type

 ls /tmp/attachments

where /tmp/attachments is whatever you put in the

  ATTACHMENT_DIR = '/tmp/attachments'

line. If it doesn’t list the attachments, you need to keep trying to figure out where they are.


Okay could you tell me exectly where to put attachments from Xenforo into new servere so i can for sure know that script will find it?


I’m doing now fresh import (5th time)… I’m tired… :roll_eyes:

Do I need to sort in some order attachments fro Xenforo or just to Copy/Paste structure and /tmp/attachments ?

I’am now doing installation on new VM with snapshot so I can quickly chnage details and start again with migration.

Yesterday i put attachments folder from Xenforo which has structure like, main folder with name attachements and inside 9 subfolder with name from 0 to 9 and inside of each of them are around 600-700 attachments (.jpg) I’ve put folder in var/discourse/shared/standalone/tmp/attachments and chnage xenforo.rb script and put ```
ATTACHMENT_DIR = ‘/shared/tmp/attachments’

I’ve test it with “ls /tmp/attachments” and I’ve got true structure, but i face same error like first time:

creating topics and posts
74 / 74752 ( 0.1%) [99817 items/min] Traceback (most recent call last):
18: from script/import_scripts/xenforo.rb:396:in <main>' 17: from /var/www/discourse/script/import_scripts/base.rb:47:in perform’
16: from script/import_scripts/xenforo.rb:32:in execute' 15: from script/import_scripts/xenforo.rb:174:in import_posts’
14: from /var/www/discourse/script/import_scripts/base.rb:869:in batches' 13: from /var/www/discourse/script/import_scripts/base.rb:869:in loop’
12: from /var/www/discourse/script/import_scripts/base.rb:870:in block in batches' 11: from script/import_scripts/xenforo.rb:180:in block in import_posts’
10: from /var/www/discourse/script/import_scripts/base.rb:490:in create_posts' 9: from /var/www/discourse/script/import_scripts/base.rb:490:in each’
8: from /var/www/discourse/script/import_scripts/base.rb:491:in block in create_posts' 7: from script/import_scripts/xenforo.rb:186:in block (2 levels) in import_posts’
6: from script/import_scripts/xenforo.rb:315:in process_xenforo_post' 5: from script/import_scripts/xenforo.rb:324:in process_xf_attachments’
4: from /usr/local/lib/ruby/2.6.0/set.rb:338:in each' 3: from /usr/local/lib/ruby/2.6.0/set.rb:338:in each_key’
2: from script/import_scripts/xenforo.rb:326:in block in process_xf_attachments' 1: from /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/core_ext/string/filters.rb:22:in squish!’
/usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/core_ext/string/filters.rb:22:in gsub!': can't modify frozen String (FrozenError) root@my-app:/var/www/discourse#Preformatted text`


I’ve remove ! from gsub! string at location:


Now, import script does not stop but continiue and show this information about attachments:

Anyway for example last string:

Could not find file /shared/tmp/attachments//2/2485-c3da9f6ee1e33d3ebf418c5dea1f9e8a.data. Skipping attachment id 2485

…it IS on that path, but there is no .data. instend data there is .JPG

Any help is welcome.


Script has a bug I’ve find it and fix it now i manage to import attachments.

I will post it later for everyone who have problem with import so they dont paid to this guys who know for this problem and try to extort money with kind word “If you have a budget” :rage: shame of you and what you did.

1 Like

I’ve got an issue where it wont let me install mysql inside the docker as this guide describes you should do.
I get told there is no matching package.


Okay I got it installed with mariadb-server instead. However, my install doesn’t have any of the script folders mentioned.

Are you looking inside the container? (./launcher enter app). If memory serves, the import scripts are in /var/www/discourse/scripts/import_scripts).

1 Like

No I wasn’t aware of that command to enter the container. I was using the docker exec command provided by the guide and assumed that was putting me in the right place. My apologies. Let me go through the process again.

These instructions (which I may never have read) say to enter the container this way. The advantage of the ./launcher enter app way is that it puts you in the /var/www/discourse directory to start.

Hi, starting at step 7, I’m running into trouble. Any help would be greatly appreciated. I’ve never worked with Ruby before, so there may be some obvious steps needed right before step 7 that I just don’t know about due to lack of experience. Forgive me if this question comes across as naive.

When I run:

I get the following result:

-bash: Gemfile: Permission denied

This is on a brand new server with a working install of Discourse.

Are there other components or pieces of software I need to install to get the steps starting at 7 to work?

And I noticed this thread is from a few years ago originally. Is every step in the OP still correct?


Ok, I had a full post submitted here but I removed it because as I dig into this more, I realized that I had a serious misunderstanding of how dockers work with the Discourse install. My previous post would have confused people because, basically I didn’t know what I was talking about. :joy:

I was adding the import files manually to /var/discourse/ directory when I should have been looking for them within the docker. A clear mistake looking back at it, but I was pretty confused at the time. That could be more clear in the instructions, but I managed to figured that out and solve my own problem.


I managed to get this import to work on a 1M+ post forum. I noticed a few things.

  1. Avatars are missing. After looking at the script, I don’t see a reference to avatars. Just to confirm, they aren’t imported?

  2. Thread views are all 0. Is this how it’s supposed to be?

  3. Guest posts (if a user account had been deleted and their username marked as guest in xenforo) all show up as “system.” Is there a way to at least make these all day “guest” instead of system? Does Discourse support posts not attributed to a registered account?

Are there any import scripts that do support import of avatars and thread views? I’d like to take a look at those and see how difficult it would be to modify the xenforo.rb script.

I know vBulletin and xenforo databases very well. Just need to be pointed in the right direction for how thread views and avatars are stored in Discourse so I can properly map them.


None of those things is surprising.

I generally grep the script directory for the thing I’m looking for (Avatar) . I believe for the views you can just add it to the sql query and add the field to the create record. For the guest user there is a line that uses system if the user lookup fails. You can replace it with the guest user that you create.