If you run a Drupal site in the UK public sector, you have almost certainly lived through one painful major-version upgrade and can see the next one coming. Drupal 7’s long goodbye, the architectural break to Drupal 8, and the steady churn of contrib modules have made “staying current” a recurring capital cost rather than routine maintenance. At some point the question stops being “how do we upgrade Drupal?” and becomes “is Drupal still the right platform for us?”

For a growing number of government departments, NHS bodies, charities and universities, the answer is Wagtail. This is not a fashion choice — it is a return to home turf. Wagtail was created at Torchbox specifically for public-sector content, and the same agencies that have delivered GDS-standard services for years are the ones who maintain it. This guide is the practical migration playbook, written for a digital lead who has to justify the move and a developer who has to do it.

01Why the public sector is moving

The reasons cluster differently from a commercial WordPress migration. In public-sector conversations we hear four recurring drivers:

  • Upgrade cost & end-of-life pressure. Major Drupal upgrades are effectively re-platforming exercises. Teams ask, reasonably, whether that budget is better spent moving to a platform with a gentler upgrade path. Wagtail follows Django’s predictable, well-supported release cadence.
  • Security surface. Like WordPress, much of Drupal’s risk lives in contrib modules. Wagtail’s footprint is smaller and the dependency provenance is easier to evidence for Cyber Essentials Plus and supplier assurance.
  • Editor experience. Drupal’s admin is powerful but notoriously steep. Wagtail’s admin was shaped by a decade of editor research at public-sector clients and consistently scores higher with non-technical content teams.
  • Python, data & AI. Public-sector digital teams increasingly do data and AI work in Python. A Django-based CMS keeps the content platform in the same ecosystem as everything else they build.

Crucially, Drupal and Wagtail are closer peers than WordPress and Wagtail. Both are serious, structured-content platforms with a real field system. That makes the migration cleaner — you are mapping structured data to structured data — even though the day-to-day experience changes a lot.

The honest framing

We respect Drupal — it is a credible enterprise CMS and for some estates it remains the right answer. The case for Wagtail is strongest when your next Drupal upgrade looks like a re-platform anyway, your editors struggle with the admin, and your team is heading toward Python. If none of those are true, a Drupal-to-Drupal upgrade may be the cheaper path.

02Nodes & fields vs pages & StreamField

The mental-model shift is the thing to get right first, because it shapes the entire migration. Drupal and Wagtail think about content differently:

the conceptual mapping drupal → wagtail
DRUPAL                            WAGTAIL
--------------------------------  --------------------------------
Node (content entity)          →  Page (tree node)
Content type (e.g. Article)    →  Page subclass (ArticlePage)
Field (field_*)                →  Model field OR StreamField block
Paragraphs (entity ref)        →  StreamField blocks (very natural fit)
Taxonomy term                  →  Snippet + tag/category model
Menu                           →  Wagtail menu snippet / Site settings
URL alias (path auto)          →  Page slug + tree path
Media entity                   →  Wagtail Image / Document
View (listing)                 →  Page QuerySet on an IndexPage
Block / region                 →  StreamField, snippets, or template
Webform                        →  Wagtail FormPage (or external service)

The happiest mapping in the whole table is Paragraphs → StreamField. If your Drupal site already uses the Paragraphs module for flexible body content, your editors are already thinking in typed, ordered blocks — which is exactly Wagtail’s model. Those sites migrate beautifully, because the content shape barely changes; only the implementation does.

The judgement call, as with any migration, is which Drupal fields become first-class Page model fields (structured, queried — a publication date, a service category, an ISBN) and which collapse into StreamField blocks (free-form body content). Decide this per content type, with the editorial team in the room, before importing anything.

03Step 1 · Getting data out of Drupal

Drupal gives you better extraction options than WordPress. Choose based on your Drupal version:

  • JSON:API (Drupal 8/9/10) — the cleanest route. The core JSON:API module exposes every content type and field as well-structured, paginated JSON. This is our default.
  • Migrate API — Drupal’s own ETL framework. Useful for staging or normalising data before it leaves Drupal, especially on complex Drupal 7 → later jumps.
  • Direct database read — for Drupal 7, where JSON:API isn’t available out of the box, query the node, field_data_* and file_managed tables directly.

With JSON:API, the importer is a re-runnable Django management command that pages through each content type and creates Wagtail pages. The idempotency (keying on the Drupal node UUID) means you can run it repeatedly against staging as you refine the mapping:

articles/management/commands/import_drupal.py python
import requests
from django.core.management.base import BaseCommand
from django.utils.html import strip_tags
from articles.models import ArticleIndexPage, ArticlePage
from .drupal_html import body_to_streamfield

BASE = "https://old-site.gov.uk/jsonapi/node/article"


class Command(BaseCommand):
    help = "Import Drupal articles via JSON:API into Wagtail"

    def handle(self, *args, **opts):
        index = ArticleIndexPage.objects.first()
        url = f"{BASE}?page[limit]=50&include=field_image"

        while url:
            data = requests.get(url, timeout=30).json()
            for node in data["data"]:
                attrs = node["attributes"]
                # idempotent on the Drupal UUID
                if ArticlePage.objects.filter(drupal_uuid=node["id"]).exists():
                    continue

                page = ArticlePage(
                    title=attrs["title"],
                    slug=attrs["path"]["alias"].rstrip("/").split("/")[-1],
                    drupal_uuid=node["id"],
                    intro=strip_tags(attrs.get("field_summary") or "")[:255],
                    body=body_to_streamfield(attrs["body"]["value"]),
                )
                index.add_child(instance=page)
                page.save_revision().publish()

            url = data["links"].get("next", {}).get("href")   # paginate

Store the original Drupal UUID and path alias on every imported page (a couple of extra model fields). The UUID makes re-imports safe; the path alias is what you’ll turn into redirects in Step 4.

04Step 2 · Mapping content types

Define one Wagtail Page subclass per Drupal content type you’re keeping, and review the set with the content team before writing the importers. A typical public-sector estate maps to something like:

articles/models.py · a public-sector content type python
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail.admin.panels import FieldPanel
from wagtail import blocks


class ArticlePage(Page):
    # provenance — keep these for safe re-imports + redirects
    drupal_uuid = models.UUIDField(null=True, editable=False, db_index=True)

    intro = models.CharField(max_length=255, blank=True)
    body = StreamField([
        ("paragraph", blocks.RichTextBlock(features=["h2", "h3", "bold", "italic", "link", "ol", "ul"])),
        ("detail",    DetailDisclosureBlock()),   # GDS-style accordion
        ("warning",   WarningCalloutBlock()),     # GDS warning text
        ("document",  DocumentChooserBlock()),
        ("table",     TypedTableBlock()),
    ], use_json_field=True)

    content_panels = Page.content_panels + [
        FieldPanel("intro"),
        FieldPanel("body"),
    ]
    parent_page_types = ["articles.ArticleIndexPage"]

Notice the blocks named after the GOV.UK Design System components — detail/disclosure, warning text, typed tables. Building your StreamField vocabulary around the components your editors already know keeps the migration familiar and bakes accessibility into the content model itself. That leads straight to the part that matters most in government.

05Step 3 · GDS & WCAG 2.2

For a public-sector body this is not a “nice to have” — the Public Sector Bodies (Websites and Mobile Applications) Accessibility Regulations 2018 make WCAG 2.2 AA a legal requirement, and anything going through a service assessment is held to the GDS Service Standard. Wagtail is unusually well placed here:

  • Built-in accessibility checker. Wagtail ships an integrated accessibility checker (powered by Axe) right in the page editor, so content authors catch issues — missing alt text, bad heading order, low contrast — before they publish, not in an annual audit.
  • Component-driven front-ends. Because you build the front-end yourself in templates, you can implement the GOV.UK Design System (or your own accessible component library) properly, rather than fighting a theme. WCAG 2.2 AA is very achievable when every component is accessible by construction.
  • Semantic, structured content. StreamField produces clean, semantic HTML with a sensible heading hierarchy — a quiet but real accessibility advantage over free-form WYSIWYG soup.
  • Service Standard fit. Wagtail’s open-source licence, Python/Django foundation, and testability map directly onto the Standard’s expectations around open source, iteration, and not being locked to a single supplier.

Treat accessibility as a migration acceptance criterion, not a post-launch fix. Run automated checks (Axe, Lighthouse, pa11y) in CI against every template, and budget for a manual audit with assistive-technology users before go-live. The migration is the ideal moment to clear an accessibility backlog that built up over years on the old platform.

06Step 4 · URLs & redirects

Public-sector URLs are often cited in legislation, printed in leaflets, and linked from other government services — so URL continuity is even more important here than on a commercial site. The path aliases you captured during extraction become 301 redirects:

turning Drupal path aliases into 301s python
from wagtail.contrib.redirects.models import Redirect

def redirect_from_drupal_alias(alias: str, page):
    # e.g. alias = "/services/apply-for-a-blue-badge"
    Redirect.objects.get_or_create(
        old_path=Redirect.normalise_path(alias),
        defaults={"redirect_page": page, "is_permanent": True},
    )

Crawl the live Drupal site (Screaming Frog or its XML sitemap) to get the authoritative list of public URLs, then add a CI check that asserts every one returns 200 or 301 on the new site. Carry across seo_title/search_description from the Drupal meta fields, regenerate the XML sitemap with wagtail.contrib.sitemaps, and resubmit in Search Console at launch. The mechanics are the same as a WordPress migration — our WordPress to Wagtail guide covers the redirect-and-SEO pipeline in more depth, and the Wagtail SEO guide covers the metadata models.

07Cost, timeline & procurement

Public-sector migrations carry overheads a commercial project doesn’t — service assessments, accessibility audits, security assurance, and a formal procurement route. Plan for them. A representative shape:

representative Drupal → Wagtail migration (public sector) £ gbp
PROJECT SHAPE                                   | TYPICAL RANGE   | TIMELINE
------------------------------------------------+-----------------+-----------
Single site, <500 nodes, few content types      |  £25k – 50k     |  8–12 wks
Departmental site, structured + Paragraphs      |  £50k – 100k    |  12–20 wks
Large multi-site / many content types           |  £100k – 250k+  |  20–36 wks
Add: full GDS service assessment + a11y audit   |  +£10k – 30k    |  +4–8 wks

PROCUREMENT ROUTES:
  • Digital Marketplace — G-Cloud (off-the-shelf, fastest)
  • Digital Marketplace — Digital Outcomes (bespoke build)
  • below-threshold direct award for discovery / audit phases

The most reliable way to de-risk procurement and budget is to run a short, separately-procured discovery first — usually a below-threshold direct award. Discovery produces the content-type inventory, the redirect map, an accessibility baseline, and a costed delivery plan you can then take to a Digital Outcomes competition with confidence. It also satisfies the GDS Service Standard’s expectation that you understand the problem before you build.

08FAQ

We’re on Drupal 7. Is the migration harder?

Slightly — Drupal 7 lacks JSON:API, so extraction is via the database or the Migrate API rather than a clean API. But Drupal 7’s end of life makes the case for moving off it (rather than the painful jump to Drupal 10) all the stronger, and the Wagtail target schema is identical regardless of source version.

Do we have to use the GOV.UK Design System?

No — only central-government services on the service.gov.uk domain are obliged to. Local authorities, NHS bodies, universities and charities often run their own brand and design system. Wagtail is agnostic; you implement whichever accessible component library you’re standardised on. (The NHS, for example, has its own.)

Can Wagtail handle Welsh / bilingual content?

Yes. Wagtail’s internationalisation supports per-locale page trees with translation workflow — well suited to Welsh-language duty obligations and other bilingual public-sector requirements, with editorial review per locale built in.

How do we keep the site live during migration?

Run Wagtail and Drupal side by side behind a reverse proxy, migrating section by section and cutting over a path at a time. For most public-sector estates a phased cutover is lower-risk than a big-bang launch and easier to assure.

Is Wagtail genuinely used in UK government?

Yes — it originated at Torchbox for the British Council and is used across government, the NHS, the third sector and higher education. You are not pioneering an unproven platform; you are adopting one with a deep public-sector track record.

If you’re scoping a move off Drupal, the sensible first step is a discovery that inventories your content types, maps your redirects, and baselines your accessibility — a costed plan you can take to procurement. That’s precisely what our Discovery Sprint delivers, structured around the GDS Service Standard.