This is a research guide based on cited documentation. It is not a report of firsthand testing. Our editorial method

Why passwords almost never come across

A well-run platform never stores the password a student typed. It stores a hash. The OWASP Password Storage Cheat Sheet describes hashing as a one-way function: it is impossible to decrypt a hash and obtain the original plaintext. So no export can hand you passwords, and a platform that could would be doing something wrong.

Hosted platforms also do not document exporting the hashes. Teachable's Users article lists the CSV export fields as user ID, full name, email, phone number, last login, joined date, course count, sign-up source, country, sign-in count, marketing opt-out, affiliate code, billing and delivery address, and tags. There is no password column. Its export article covers users, transactions and per-file course downloads, and does not mention passwords or saved payment methods.

Even if you had hashes, they would need to match a format WordPress can check. WordPress 6.8 moved new password hashes from phpass to bcrypt; the core announcement says older phpass hashes keep working and are rehashed to bcrypt the next time the user logs in or changes their password. That compatibility helps when you move between two WordPress sites by copying the users table. It does nothing for hashes produced by a hosted platform's own system.

The practical consequence: treat the migration as an account invitation, not a silent switch. Budget the email, the reset page and the support load from the start.

Export the data you can actually use

Pull three things from the old platform before you build anything: the user list, the enrollment list per course, and the transaction or subscription list. Teachable's Users article says only school owners can export user data, the CSV is sent by email, and the file must be downloaded within 24 minutes of being sent. Download each file immediately and keep the originals untouched; work on copies.

The user export gives each student a course count, not the list of courses they own. Teachable's Users page offers enrollment filters before you export; use them, one course at a time where the filters allow, to build a student-to-course list, check each student's total against the course count column, then map each old course to the course ID on the new WordPress site.

Export fieldUse on the new siteWatch for
EmailPrimary match key for the importDuplicates with different capitalisation; students who used two addresses
Full nameSplit into first_name and last_name or use as display_nameSingle-word names and non-Latin characters
Course enrollmentsCourse IDs for the enrollment passOld course titles that were renamed or merged
Marketing opt-outCarry into your email tool so opted-out students only get account noticesDo not treat the migration notice as a marketing send
Last login or sign-in countSegment active versus dormant students for the email planDormant accounts produce most bounces
Transactions or subscriptionsDecide who still has paid accessRecurring billing does not move with the user list

Import accounts: two routes

Route one is WP-CLI. The wp user import-csv command imports users from a local or remote CSV; its documented sample uses the columns user_login, user_email, display_name and role. If a user already exists, matched by email address or login, the command updates that user unless you pass --skip-update. The --send-email flag sends new users their account details, so leave it off during a migration and send your own message instead. The WP-CLI page does not document a password column, so do not rely on one.

Route two is a plugin that combines account creation with LMS enrollment. Uncanny Owl's Import LearnDash Users module, part of the paid Uncanny Toolkit Pro, needs only user_email; user_login is generated from the email if omitted, and learndash_courses and learndash_groups take semicolon-separated IDs. Its documentation says password hashes do not transfer between sites, recommends a reset-password link token in the welcome email, says enrollments are additive only, and notes courses cannot be marked complete through the import.

For a few hundred students either route works. For thousands, run the import on a staging copy first and time it, then repeat on production during a quiet window.

NeedWP-CLI import-csvUncanny Toolkit Pro import
Create accounts from CSVYesYes
Enroll in LearnDash courses in the same fileNo; separate stepYes, learndash_courses column
Existing usersUpdated unless --skip-updateEnrollments added; username and email cannot change
Emails on importOnly with --send-emailOnly if welcome email boxes are checked
CostFree command-line toolPaid plugin; price not on the documentation page
Access neededSSH or host terminalWordPress admin

Enroll students in a second, checkable pass

Keep enrollment separate from account creation so you can verify each step. After import, export the new user list with wp user list --fields=ID,user_login,user_email --format=csv and join it against your student-to-course map on email. That gives you a clean list of new user IDs and new course IDs.

On LearnDash, the developer reference documents ld_update_course_access( user_id, course_id, remove ), which updates the list of courses a user can access and returns true on success. A short script that loops through your joined list and calls it once per pair, logging every false result, gives you an auditable enrollment run. Other LMS plugins have their own enrollment functions or bulk tools; look for one that reports failures per row.

Progress and completion history are a separate problem. The Uncanny documentation says completion cannot be set through its import. If students need credit for finished lessons, decide before launch whether you will recreate it, grant certificates manually or tell students to skip ahead.

  • Count rows: students in the export, accounts created, enrollments attempted, enrollments succeeded.
  • Spot-check ten students across different courses by logging in as them on staging.
  • Check a student who owns several courses and one who owns none.
  • Confirm that unenrolled students cannot open paid lessons.

The password-reset email plan

Send one clear message per student from your own email service, not WordPress's default notifications. Tell them the school has moved, give the new login URL, and link to the lost-password page so they choose a new password themselves. Avoid mass-resetting with wp user reset-password: the WP-CLI page describes it as resetting the password and sending a change email, which generates a password the student did not choose and can read like a security alert.

Volume matters for deliverability and cost. At 2,000 students, the announcement plus one reminder is about 4,000 messages. Postmark's Basic plan lists 10,000 emails a month for $15.00; Amazon SES lists an a la carte outbound rate of $0.10 per 1,000 emails, about $0.40 for the same send. Either is cheaper than a support inbox full of locked-out students.

WhenMessageAudience
7 days beforeWe are moving; your courses come with you; nothing to do yetAll students
Cutover dayNew login URL and a link to set your passwordAll imported students
Cutover + 3 daysReminder with the same link and a support contactStudents who have not logged in
Cutover + 14 daysFinal reminder; old platform closes on a stated dateStudents active on the old platform in the last year who still have not logged in

Checklist before you send the cutover email

  • Imported on staging first, with counts matching the export.
  • Automatic welcome and new-user emails disabled for the import run.
  • Password reset email tested end to end, including spam-folder checks with two mailbox providers.
  • Lost-password page reachable without login and excluded from page caching.
  • Opted-out students receive only the account notice, not promotions.
  • A support reply template ready for "I never got the email" and "I used a different address".
  • The old platform stays live until the final reminder date passes.

Questions before you decide

Can I export student passwords from Teachable?

Teachable's Users article lists the CSV export fields, and none of them is a password. Passwords are stored as one-way hashes, so plan for every student to set a new one on the new site.

Will wp user import-csv email my students?

Only if you pass --send-email. Leave it off during a migration and send your own announcement with a link to the lost-password page, so students get one clear message.

What happens if a student already has an account on the new site?

WP-CLI's import-csv matches on email or login and updates the existing user unless you pass --skip-update. Uncanny Owl's LearnDash import adds enrollments to existing users but does not change their username or email.

Do WordPress password hashes survive a move between two WordPress sites?

If the users table moves intact, yes. The WordPress 6.8 announcement says older phpass hashes keep working and are rehashed to bcrypt at the next login or password change.

Sources & verification

Product details and prices can change. Check the linked provider before buying.

  1. OWASP: Password Storage Cheat Sheet Accessed 30 Oct 2026
  2. Make WordPress Core: WordPress 6.8 will use bcrypt for password hashing Accessed 30 Oct 2026
  3. Teachable: Users (CSV export fields) Accessed 30 Oct 2026
  4. Teachable: How to export school content and data Accessed 30 Oct 2026
  5. WP-CLI: wp user import-csv Accessed 30 Oct 2026
  6. WP-CLI: wp user list Accessed 30 Oct 2026
  7. WP-CLI: wp user reset-password Accessed 30 Oct 2026
  8. Uncanny Owl: Import LearnDash Users (Uncanny Toolkit Pro) Accessed 30 Oct 2026
  9. LearnDash developer reference: ld_update_course_access() Accessed 30 Oct 2026
  10. Postmark: pricing Accessed 30 Oct 2026
  11. Amazon SES: pricing Accessed 30 Oct 2026

Sources link directly to providers. Product buttons may use separately labeled affiliate links. Read our disclosure.