Free guide
Drupal 7 migration kickoff checklist
Most Drupal 7 migrations do not stall on the import. They stall on ten decisions nobody wrote down. Here is the list, with a test for each one so you can tell when it is genuinely settled instead of merely discussed.
Drupal 7 reached community end of life on 5 January 2025, so most remaining projects are moving to Drupal 10 or 11. Core ships the Migrate API for that path, and the mechanics are well documented on drupal.org. This checklist covers the part that is not mechanical: the planning decisions that determine whether your first real import produces a reviewable site or a pile of questions.
Work through it before you write a migration YAML file. Each item names what to record, and a done when line — a specific, checkable condition. If you cannot meet the done when test, that item is still an open risk, not a completed step.
1. Name the decision owners
Migrations generate a steady stream of judgement calls that developers cannot settle alone: whether a 2011 news archive is worth carrying forward, whether a field can lose its formatting, whether a URL may change. If each of those waits for a meeting, the build stops.
Record: one named content owner, one technical owner, and one person who can approve scope cuts. For each, how you reach them and how fast you can expect an answer.
Done when: you can point at a name for the question "who decides if we drop a content type?" without asking anyone.
2. Inventory what exists, not what you remember
The site almost always contains more than the team thinks: content types created for a single campaign, vocabularies with three terms, views nobody visits, a block referencing a module removed years ago. Estimating against the remembered site is how migrations double in length.
Record, per entity type: the machine name, the row count, the last-created and last-updated dates, and whether it is still published or linked from navigation. Node, user, taxonomy term, file, menu link, block, and any custom entity.
Counts and dates matter more than the list. A content type with 40,000 rows and nothing created since 2014 is an archive decision, not a migration task. A type with 60 rows edited last week is business-critical no matter how small it looks.
Done when: every content type in the inventory has a row count and a named disposition — migrate, archive, or drop — and the total of "migrate" rows is a number you could put in an estimate.
3. Choose a route per content type
There is no single correct migration route, and picking one for the whole site is usually a mistake. The realistic options:
- Automated upgrade. Core's Migrate Drupal modules read the Drupal 7 database and generate migrations for recognised core structures. Strongest for standard nodes, users, taxonomy, and menus.
- Custom migration. You write the source, process, and destination plugins yourself. Right when the source data is messy, when fields need real transformation, or when the destination model deliberately differs from Drupal 7.
- Remodel and re-enter. For low-volume, high-value content — landing pages, the about section, anything already being rewritten — re-creating it by hand is often cheaper and better than mapping it.
- Archive. Keep it readable as a static export or a database snapshot, and do not migrate it. This is a legitimate outcome and the one teams under-use.
Record: the chosen route beside each content type in the inventory, and one sentence saying why.
Done when: no content type is left with a blank route, and anything marked "remodel" or "archive" has been agreed by the content owner from item 1 — not assumed by the developer.
4. Resolve every field that has no obvious destination
Field-level surprises are the most common source of rework, because they only surface when someone reviews migrated content. The recurring ones:
- Dates. Drupal 7's date module stored several formats and timezone behaviours. Core's date and date-range fields are stricter. Decide the storage timezone and what an empty or partial date becomes.
- Rich text. Drupal 10 and 11 use CKEditor 5, which enforces a text format's allowed tags far more consistently than CKEditor 4 did. Inline markup that survived in Drupal 7 bodies can be normalised or stripped. Review text formats as part of the field map, not after launch.
- References. Entity reference targets change machine names surprisingly often. Any reference whose target is being remodelled needs an explicit mapping rule.
- Compound fields. Addresses, geolocation, and similar Drupal 7 field types rarely map one-to-one. Decide whether you are preserving the structure or flattening it.
- Multi-value and empty values. Write down the cardinality change and the default for empty, per field. "It will probably be fine" is where data loss hides.
Record, per field: source name and type, destination name and type, the transformation, the default for empty, and — for anything unresolved — the person who owes you the answer.
Done when: every field is either marked direct, has a written transformation rule, or is on an open-questions list with a name against it. Zero fields in the "we'll see what happens" state.
5. Give every contributed module a verdict
Each Drupal 7 contributed module needs one of four verdicts: replace with the modern equivalent, rebuild the behaviour with core features, remove because the need is gone, or blocked because there is no path yet. The last category is what you need early — a blocked module can change the whole plan, and finding it in week six is expensive.
The patterns worth checking first, because they usually mean rebuild rather than replace: page and layout tooling (Drupal 10/11 use Layout Builder and the block layout rather than Drupal 7's panel-based stack), rule-based automation, form builders, configuration packaging (now core configuration management), and multilingual setups built on Drupal 7's translation modules.
For the site's own code, Upgrade Status reports compatibility and deprecated API use, and is the fastest way to size custom module work.
Record, per module: version in use, whether it is actually enabled and used, the verdict, the replacement, dependencies, and the rebuild estimate for anything not a straight replacement.
Done when: the module list has no blank verdicts, and every "blocked" item has either a workaround or a scope decision from the owner in item 1.
6. Decide the URL and redirect policy
URL changes are the failure that is invisible in testing and obvious to everyone else after launch. Search rankings, inbound links, bookmarks, printed material, and QR codes all depend on paths you may be about to regenerate.
Decide the policy before you configure alias patterns, because the policy determines the patterns: preserve existing paths exactly, or adopt a new structure and redirect everything old to it. Mixed approaches are fine but must be written down per section.
Record: the alias pattern per content type, the redirect source and destination for every path that changes, the status code, and how you will verify each one on staging. Include paths that are not nodes — views pages, feeds, files, and any legacy path already redirecting today.
Get the list of paths that actually matter from your analytics and server logs, not from the sitemap. Traffic tells you which of 40,000 URLs are worth an individual redirect and which can go to a section landing page.
Done when: you have a redirect list covering the top-traffic paths, each with a destination and an expected status code, and a repeatable way to test it against staging.
7. Decide what happens to files and media
Files are handled separately from the content that references them, and the Drupal 7 site probably mixes several conventions: direct file and image fields, an older media implementation, files in public and private directories, and files uploaded through the rich-text editor that no field knows about.
Record: total file count and size, the public and private split, which fields become core media references and which stay plain file fields, what happens to unreferenced files, and how editor-embedded images are rewritten.
Done when: you know the total bytes to move, the copy method, and what a missing source file should do — fail the migration row, or import with a logged warning. Decide that now; it will happen.
8. Decide the user, role, and permission cutover
Accounts are both a data problem and a launch-day problem. Drupal 7 password hashes can carry over, but if the destination site changes authentication — single sign-on, a new provider, an enforced reset — that is a communications task with a deadline, not a migration setting.
Record: the role map from old to new, which roles change permissions, what happens to blocked and never-logged-in accounts, whether passwords carry over or reset, and who tells users if they do reset.
Done when: each destination role has a named human who will log in as that role during acceptance testing, and the password decision is written down rather than inherited by default.
9. Set a rehearsal cadence and a data freeze
A migration that has been run once is not a migration you can schedule. Real projects run the full import repeatedly, on current data, until the run time and the error list are both predictable. Core's Migrate API supports rollback for exactly this reason, and Migrate Tools provides the day-to-day status, import, and rollback commands.
Record: how often you re-run the full import, where the source snapshot comes from, the measured run time for the whole set, and the date content editing stops on the Drupal 7 site — or the plan for migrating the delta if it does not stop.
Done when: you have run the complete migration end to end on a recent snapshot at least twice, the run time is a number you would commit to, and the content freeze date is in the calendar of the people it constrains.
10. Write acceptance criteria and a rollback plan
"Looks right" does not close a migration. Without written criteria, launch readiness becomes an argument, and the project either ships with known gaps nobody agreed to or slips indefinitely on unlisted concerns.
Record, as a checklist somebody other than the developer can run: expected row counts per entity type, the pages to review by hand, the redirects to verify, the roles to log in as, the forms to submit, performance expectations, and accessibility checks. Each with an owner and a pass or fail state.
Then the part teams skip: the rollback. Write down what you do if the launch import fails at 80% — whether you roll back the migration, restore a snapshot, or repoint DNS — and who decides.
Done when: every criterion has an owner and a result, the open ones are visible in one place, and the rollback plan has been read by the person who would have to execute it.
Where to keep all of this
Nine times out of ten, these decisions end up split across a ticket tracker, three spreadsheets, and a chat thread — which is why they get re-litigated. Keep them in one place that the content owner can open without a developer.
If you would rather not build that structure yourself, the Drupal 7 to Drupal 11 Migration Workbook is the version we use: connected tabs for the inventory, field map, module plan, redirect map, and QA register from this checklist, with a formula-driven overview that shows what is still unresolved. One Excel file, no macros, no site connection.
See what is in the workbook — $29The checklist on this page is free and complete on its own. The workbook is the same structure as a working file, for teams who would rather not build it from scratch.