← All playbooks
CMS migration

Strapi → Contentful

Export Strapi with strapi export, transform the JSONL entities into Contentful types, then load the space with the Contentful CLI. strapi export is built for another Strapi instance. Admin users and API tokens do not come along. Rebuild preview, roles, and webhooks.

Typical timeline15–40 business days70–160 hours active work
Statusneeds review
Source profileStrapi 5 data export docs reviewed 2026-10-31
Destination profileContentful CLI import and export docs reviewed 2026-10-31
Documentation date
Review window
Evidence4 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

Strapi

Reasons to stay
  • An open-source headless CMS with a writable admin, REST, GraphQL, and a readable export archive
  • Editors get a purpose-built publishing workflow instead of raw files
Reasons to leave
  • Self-hosting, schema code, and custom importers become your problem when you leave a SaaS CMS
  • Themes, extensions, and content models add maintenance and portability constraints
New platform

Contentful

What gets better
  • Mature structured content APIs, localization, roles, and ecosystem support enterprise publishing
  • Editors get a purpose-built publishing workflow instead of raw files
What gets worse
  • Content-model complexity, API usage, and pricing can create significant platform dependence
  • Themes, extensions, and content models add maintenance and portability constraints
Best of the move

Contentful: Mature structured content APIs, localization, roles, and ecosystem support enterprise publishing. This removes a major source-side concern: Self-hosting, schema code, and custom importers become your problem when you leave a SaaS CMS.

Worst of the move

What you lose: An open-source headless CMS with a writable admin, REST, GraphQL, and a readable export archive. What you inherit: Content-model complexity, API usage, and pricing can create significant platform dependence.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome12 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 6, Manual rebuild: 4, Not transferred: 2.
Clean0
Partial6
Manual4
Lost2
Mapping route9 of 10 fields have a destination path

This timeline assumes

  • Strapi 5. You can run strapi export --no-encrypt --no-compress, or --format dir --no-encrypt on 5.42 or later.
  • You have a Contentful space where you can create types and run contentful-import.
  • Strapi stays online until the frontend no longer loads Strapi media URLs.
  • The site already documents Contentful → Strapi and Strapi → Sanity as nearby routes.
  • strapi transfer only talks to another Strapi /admin endpoint. It cannot load Contentful.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Export archivepartialcriticalstrapi export writes metadata, configuration, entities, links, schemas, and assets. Default output is encrypted and gzipped. Admin users and API tokens are not exported.Export with --no-encrypt --no-compress or --format dir so you can read JSONL. Keep a separate admin user list.
Content typesmanualcriticalStrapi schemas do not become Contentful content types. Components and dynamic zones need a Contentful model you design.Create types first. Map __component values to Contentful content types or embedded entries.
EntriespartialcriticalEach entities_*.jsonl line is one record. You must transform it into Contentful entry JSON and preserve a stable id, preferably Strapi documentId.Write a repeatable script. Use create-or-replace semantics so reruns converge.
RelationspartialcriticalRelations live in links/*.jsonl as well as on entities. A single pass can point at entries that do not exist yet.Import entries, then patch links. Or import in dependency order.
AssetspartialcriticalExported files come from the uploads folder. --only content still keeps asset database rows and can leave broken links. --exclude files does the same from the other side.Export files and content together unless you have another store. Upload to Contentful. Replace media URLs.
Rich text and blockspartialhighStrapi Blocks, Markdown, HTML, or CKEditor JSON is not Contentful rich text.Convert a sample per type. Keep the raw JSONL.
i18nmanualcriticalStrapi locale variants must become Contentful locale fields or separate entries.Create locales in the space first. Import each locale on purpose.
Draft versus publishedpartialhighStrapi draft and published rows can both appear. Contentful has its own draft API.Map status. Do not publish drafts.
Roles, tokens, and admin userslostcriticalStrapi documents that admin users and API tokens are omitted from export.Recreate Contentful roles and tokens. Rotate anything that was in env files.
Webhooks and previewmanualhighStrapi webhooks and preview URLs do not import.Rebuild them in Contentful.
Frontend queriesmanualcriticalREST, GraphQL, and documentId assumptions change.Point a branch at Contentful Delivery or GraphQL before cutover.
Scheduled publisheslostmediumStrapi schedules do not become Contentful scheduled actions.Recreate future publishes in Contentful.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
Collection typeContentful content typemanualCreate the type first.
documentIdEntry id or a stored fieldtransformKeep a stable key.
entities JSONLContentful entrytransformOne line per record.
links JSONLEntry linkstransformApply after both sides exist.
uploads fileContentful assettransformUpload, then link.
Component or dynamic zoneEmbedded entry or rich texttransformMap every __component.
LocaleContentful localemanualCreate locales first.
Draft flagDraft or published entrytransformDo not publish drafts.
WebhookContentful webhookmanualNew secrets.
Admin user or API tokenNo destinationunsupportedNot in the export.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep a readable Strapi export and a running Strapi you can point at again.

  1. Run strapi export --no-encrypt --no-compress or --format dir --no-encrypt.
  2. Record type and entry counts per locale.
  3. Hash the archive or directory.
  4. List webhooks and preview URLs.
  5. Keep a Strapi backup you have restore-tested.

Proof to capture: Export hash, counts, and a named restore owner.

Transformation · A script you own plus contentful-import

JSONL to Contentful JSON

Turn Strapi entities and links into a contentful-import payload.

  1. Read schemas first.
  2. Create missing content types.
  3. Transform entities.
  4. Upload assets.
  5. Patch links.
  6. Rerun until counts settle.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · Contentful Delivery or GraphQL plus the app test suite

Frontend remap

Stop shipping Strapi URLs to production.

  1. Change space and token env vars on a branch.
  2. Map slugs and ids.
  3. Run the suite against Contentful.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Export

You import an encrypted archive you cannot read

highpossible likelihood

The default .tar.gz.enc needs a key you did not store.

Consequence
The transform never starts.
Mitigation
Export again with --no-encrypt. Store the key if you keep encryption.

Stop if: You cannot open entities JSONL.

Import

Asset rows without files

criticalpossible likelihood

You used --only content or --exclude files.

Consequence
Contentful entries point at missing media.
Mitigation
Export files with content. Fail on missing binaries.

Stop if: A required asset has no file.

Cutover

Frontend still reads Strapi

criticalpossible likelihood

A production build still has STRAPI_URL.

Consequence
Editors change Contentful. The site does not.
Mitigation
Search env and CI. Watch production network.

Stop if: A production request still hits Strapi.

06Precise timeline

Do the work in this order.

Estimate forOne Strapi 5 project, a few dozen types, and a frontend you can retarget
Total elapsed15–40 business days
Active work70–160 hours
BufferAdd time for dynamic zones, many locales, or a large uploads folder.
01
Days 1–5Inventory Strapi4–5 days
02
Days 5–8Readable export2–4 days
03
Days 8–28Space and rehearsal import12–20 days
04
Days 28–35Freeze and switch the API3–5 days
05
Days 35–40Observe and close5–7 days
  1. Days 1–5 · inventory

    Inventory Strapi

    12–20 hours active4–5 days elapsedOwner review waiting
    • List types, components, locales, and media providers.
    • Decide Contentful type shapes.

    Depends on: Strapi and Contentful admin access

    Stop / go checkpoint

    Export?

    Go when: The type map is signed.

    Stop when: A required dynamic zone has no target type.

  2. Days 5–8 · backup

    Readable export

    6–12 hours active2–4 days elapsedExport runtime waiting
    • Run a readable export.
    • Hash files.
    • Count entities and assets.

    Depends on: inventory

    Stop / go checkpoint

    Transform?

    Go when: JSONL opens and counts exist.

    Stop when: The archive is encrypted without a key.

  3. Days 8–28 · pilot

    Space and rehearsal import

    40–100 hours active12–20 days elapsedImport runtime waiting
    • Create Contentful types.
    • Transform and import a sample.
    • Upload assets.
    • Point a frontend branch at Contentful.

    Depends on: backup

    Stop / go checkpoint

    Cut over?

    Go when: Counts, assets, and the frontend pass.

    Stop when: A required relation or locale is wrong.

  4. Days 28–35 · cutover

    Freeze and switch the API

    8–18 hours active3–5 days elapsedFinal export waiting
    • Freeze Strapi publishes.
    • Re-export and rerun the import.
    • Point production at Contentful.

    Depends on: pilot

    Stop / go checkpoint

    Contentful is the CMS?

    Go when: Production reads Contentful only.

    Stop when: A production page still uses Strapi.

  5. Days 35–40 · observe

    Observe and close

    6–10 hours active5–7 days elapsedPublish cycle waiting
    • Watch previews and asset hosts.
    • Keep Strapi running.

    Depends on: cutover

    Stop / go checkpoint

    Close rollback?

    Go when: One publish cycle is clean.

    Stop when: Ids or assets regress.

07The point of change

Cut over with a way back.

Go live

Cutover

Make Contentful the only CMS the production frontend reads.

Recommended window: A weekday with CMS and frontend owners on call.

  1. Freeze Strapi publishing.
  2. Run a final readable export and import.
  3. Reconcile counts and asset hosts.
  4. Switch production environment variables.
  5. Publish one known entry and confirm the live page.

Proof to capture: Production reads Contentful. Counts match. Assets do not use Strapi URLs you will cancel.

Return to safety

Rollback

Return the frontend to Strapi without losing Contentful-era edits you still want.

Deadline: Before you delete the Strapi project or drop the export.

  1. Point production back at Strapi.
  2. Export Contentful entries changed after cutover.
  3. Apply only reviewed entries back to Strapi if needed.
  4. Keep the Strapi export.

Proof to capture: Production reads Strapi again.

Rollback immediately when
  • Count mismatch
  • Broken required asset
  • Frontend still mixed between APIs
  • Locale missing
  • Preview or webhook failure
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 8 checks passed
PassIDCheckMethodExpected resultEvidence
V-01BlockingReadable archiveOpen entities and links JSONL.Files are not encrypted. Admin users are absent as documented.Archive listing.
V-02BlockingEntry countsCompare Strapi and Contentful per type and locale.Counts match or variances are signed.Count sheet.
V-03BlockingFiles uploadedOpen a sample of media fields.Required assets exist in Contentful.Asset report.
V-04BlockingLinksOpen entries with several relations.Links resolve.Relation sample.
V-05BlockingProduction APIWatch network on a production build.No Strapi API requests.Network log.
V-06BlockingDraft versus publishedPublish one draft and unpublish one entry.The frontend follows Contentful status.Publish log.
V-07BlockingLocale pagesOpen each published locale for a sample type.Approved locale content.Locale sample.
V-08BlockingStrapi still servesKeep the project and a rollback env.You can point the frontend back.Rollback note.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: One publish cycle, no required Strapi media hosts, and seven stable days.

  1. Archive the export and importer log.
  2. Revoke Strapi tokens the frontend used.
  3. Keep Strapi running through the retention window.
  4. Rotate Contentful tokens created for the import.
  5. Document space backup.
  6. Schedule the next schema and token review.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. Strapi: data exportAccessed 2026-10-31
  2. Contentful: CLI import and exportAccessed 2026-10-31
  3. Contentful: data export helpAccessed 2026-10-31
  4. Strapi: CLI transfer and export optionsAccessed 2026-10-31