Skip to content
arrow_backBack to insights
April 16, 20263 min read

Why I wrote wp_drupal_migrate: a direct-DB path from WordPress to Drupal

A contributed Drupal module that pulls WordPress content straight from the source database. No WXR exports, no brittle middle steps. Here is what it does, why I built it, and where it fits in a WP→Drupal migration.

WordPress-to-Drupal migrations tend to look simple on a slide and messy in practice. You agree on the target content model, pick a migration tool, run an import, and then spend the next two weeks reconciling truncated bodies, broken media references, lost authors, and comments that decided not to come along. I have been through this cycle enough times on client projects that I wanted a cleaner path, so I wrote one and contributed it back as wp_drupal_migrate.

The problem with WXR-first migrations

The long-standing approach is to export WordPress content as WXR (the XML format WordPress uses for export) and feed that file to a Drupal migration pipeline. It works, and for small blogs it works well. It starts to fail in the ways you would expect at scale:

  • WXR exports silently truncate on large sites, or time out behind PHP limits.
  • Media references in post bodies point at filesystem paths that the export does not carry.
  • Authors, taxonomies, and comments need to round-trip through an intermediate file format that loses fidelity at every step.
  • Re-running the migration means re-exporting, so your "source of truth" is a stale file on someone's laptop.

If the WordPress database is reachable from the Drupal host, the intermediate file is noise. You already have the source of truth; you should be able to read from it directly.

What the module does

wp_drupal_migrate connects to a live WordPress MySQL database as a secondary Drupal database connection and runs a set of migrations on top of it. Content is read as it exists today, with no WXR and no staging file, and written into native Drupal entities.

Out of the box it covers the entities that matter for most WP-to-Drupal moves:

  • Users: WordPress users become Drupal users, with roles mapped to sensible defaults.
  • Posts and pages: mapped to Drupal node types, with body, slug, and publish status preserved.
  • Categories and tags: imported as taxonomy terms on vocabularies you choose.
  • Media: attachments imported as Drupal media entities, with file copies handled as part of the run.
  • Comments: optional, but available when the client wants to keep the conversation history.

Each of these is a real Drupal migration plugin, so you get the usual rollback, update, and re-import behaviour you expect from Migrate API, not a one-shot importer that leaves you stranded if something goes wrong mid-run.

Running it

Once the module is installed and the secondary DB connection is configured in settings.php, the workflow lives under Configuration → Content authoring → WordPress Migration → Execute Migrations (/admin/config/content/wp-migrate/execute). From there you can run migrations individually (useful while you are still tuning field mappings), or run them as a group once the mapping is stable.

For anything non-trivial I still recommend doing the first pass on a staging copy of the live DB and diffing a sample of nodes against the source before signing off. Direct-DB migrations are faster, but "fast" does not absolve you from verification.

When to use it, and when not

This module is the right call when:

  • The WordPress database is reachable from the Drupal environment (same host, private network, SSH tunnel, etc.).
  • The content model maps cleanly onto Drupal node types and vocabularies: standard blog, news, or editorial sites.
  • You want migrations you can re-run, iterate on, and hand over as code.

It is the wrong call when you have no database access at all, or when the WordPress site relies heavily on custom plugins that encode meaningful data in unserialised wp_postmeta blobs. In that case you are writing custom process plugins no matter which migration tool you start from, so pick the one you are most comfortable extending.

Contributing

The module is on drupal.org at drupal.org/project/wp_drupal_migrate. Issues, patches, and field-mapping contributions are welcome, particularly for WordPress plugins with well-known data shapes (ACF, Yoast, WooCommerce) where a shared mapping would save everyone time.

If you are staring down a WP-to-Drupal migration and want a second opinion on whether this module fits, the contact form at the bottom of this site is the fastest way to reach me.