Skip to content

Strapi 5 upgrade guide: migrating from Strapi 4 step‑by‑step

A complete, practical guide to moving a Strapi 4 site to Strapi 5 – backup, dependency updates, config changes, plugin compatibility, and testing with Next.js.

By the Revisionary team · · 5 min read

man in black t-shirt using laptop computer
Photo by Anthony Riera on Unsplash

Upgrading a Strapi 4 project to Strapi 5 is a straightforward process if you follow a disciplined checklist: back up your data, align the runtime, update core and plugins, adjust configuration, migrate custom code, and verify the front‑end integration. When each step is completed, you keep the content history, edit‑locks, and audit logs intact, and your Next.js site continues to fetch the same API shape.

1. Prerequisites

RequirementWhy it matters
Node 18‑24Strapi 5 runs on these versions; older Node can cause build failures.
Supported database (PostgreSQL, MySQL, SQLite)No schema changes are required for the core upgrade, but the driver must match the Node version.
Git branch for the migrationKeep the original code on a separate branch to allow roll‑back if needed.
Access to the server’s filesystemNeeded for backup files and to edit config/plugins.ts.

Make sure you have a clean working copy and that all local changes are committed.

monitor showing Java programming
Photo by Ilya Pavlov on Unsplash

2. Back up everything

Even though the migration does not touch the database schema, a full backup protects you from unexpected data loss.

bash
# PostgreSQL example
pg_dump -U <user> -h <host> -Fc -f strapi_backup.dump <db_name>

# MySQL example
mysqldump -u <user> -p<password> <db_name> > strapi_backup.sql

If you are using the Revisionary plugin, its audit log and trash are stored in the same database, so they are covered by the dump. Keep the dump file in a secure location and, if possible, test a restore on a staging server.

3. Update the project’s dependencies

3.1. Change the Strapi version in package.json

Replace the Strapi 4 packages with the Strapi 5 equivalents. Most core packages keep the same name, but the major version changes.

json
// package.json (excerpt)
"dependencies": {
  "@strapi/strapi": "^5.0.0",
  "@strapi/plugin-i18n": "^5.0.0",
  "@strapi/plugin-users-permissions": "^5.0.0",
  // other plugins …
}

If you use community plugins, check their npm pages for a Strapi 5 compatible release. If none exists, you may need to replace the plugin or defer the upgrade.

3.2. Install the new dependencies

bash
npm install   # or yarn install

During installation you may see peer‑dependency warnings. Resolve them by updating the indicated packages to their Strapi 5 versions.

4. Adjust configuration files

Strapi 5 introduces a new layout for config/plugins.ts. The old config/plugins.js format still works after a simple rename, but you should adopt the typed format to avoid runtime errors.

ts
// config/plugins.ts (new format)
export default ({ env }) => ({
  // Example: keep the Revisionary plugin configuration
  revisionary: {
    enabled: true,
    // No breaking changes for the plugin yet, but keep an eye on future releases.
  },
  // other plugins …
});

If you have custom environment variables, move them to the new config/env/{environment}.js structure as described in the Configuration guide (/docs/reference/configuration).

5. Review breaking changes in core APIs

5.1. Document Service API

The Document Service API now requires explicit locale handling for localized queries. If you previously called strapi.services.article.find(params), update the call to include the locale when needed.

ts
// Before (Strapi 4)
const articles = await strapi.services.article.find({ _limit: 10 });

// After (Strapi 5)
const articles = await strapi.documents('api::article.article').find({
  locale: 'en',
  limit: 10,
});

Read the full migration notes in the official Strapi documentation for the Document Service.

5.2. i18n changes

The i18n plugin now stores the default locale in config/i18n.js and expects the locale field on each entry. Adjust any custom queries that relied on the old default‑locale fallback.

ts
// Example of a locale‑aware query
const post = await strapi.documents('api::post.post').findOne(id, {
  locale: 'fr',
});

5.3. Routes, Controllers, and Services

The auto‑generated route files moved from api/<name>/config/routes.json to src/api/<name>/routes/<name>.js. If you have custom routes, copy them to the new folder structure and rename the export to the new format:

ts
// src/api/article/routes/article.ts
export default {
  routes: [
    {
      method: 'GET',
      path: '/articles',
      handler: 'api::article.article.find',
    },
  ],
};

Controllers and services follow the same folder shift (src/api/<name>/controllers and src/api/<name>/services). Update any relative imports accordingly.

6. Migrate the Revisionary plugin data

Revisionary stores versioned entries in dedicated tables that are compatible with Strapi 5. After the core upgrade, run the plugin’s migration command (if the plugin provides one). At the time of writing, the plugin automatically detects the Strapi version and runs a silent migration on first start.

bash
npm run build   # builds the admin UI
npm run start   # starts Strapi; the plugin migrates its tables

Verify that the Version history panel appears in the Content Manager and that the Audit log shows recent actions (/docs/everyday-use/history). If the panel is missing, consult the troubleshooting guide (/docs/troubleshooting).

7. Test the back‑end locally

  1. Start Strapi in development mode:
    bash
    npm run develop
  2. Open the admin UI (http://localhost:1337/admin).
  3. Check that all content types load, the Revisionary panel shows entries, and you can create a draft, publish, and restore a previous version.
  4. Run the built‑in health check endpoint (/admin/health) to confirm the server reports no errors.

If any custom code throws ReferenceError: strapi.services is not defined, replace it with the new Document Service call pattern.

8. Verify the Next.js front‑end

Most Next.js projects consume Strapi via the REST or GraphQL endpoints. The REST shape is unchanged for basic fields, but the locale handling and the Document Service paths differ.

8.1. Update the API client

If you use fetch('/api/articles'), you may need to add the locale query parameter for multilingual sites.

js
// pages/api/articles.js (Next.js API route)
export default async function handler(req, res) {
  const locale = req.query.locale || 'en';
  const response = await fetch(`${process.env.STRAPI_URL}/api/articles?locale=${locale}`);
  const data = await response.json();
  res.status(200).json(data);
}

For GraphQL, the endpoint stays /graphql, but the schema now includes the locale argument on each query field.

8.2. Run the front‑end locally

bash
cd next-app
npm run dev

Visit the site and confirm that content loads, draft preview works (if you use the Draft & Publish preview mode), and navigation is unaffected.

9. Deploy to production

  1. Build the Strapi admin UI:
    bash
    npm run build
  2. Copy the built build/ folder to your production server or container image.
  3. Ensure the environment variables (DATABASE_URL, HOST, STRAPI_ADMIN_JWT_SECRET, etc.) match the production values.
  4. Restart the Strapi process.
  5. Monitor the logs for any migration warnings.

If you deploy with Docker, update the Dockerfile to use a Node 18‑based base image and reinstall dependencies with the new version.

Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/build ./build
COPY package*.json ./
RUN npm ci --only=production
EXPOSE 1337
CMD ["node", "./build"]

10. Post‑migration checklist

  • All content types appear in the admin UI.
  • Revisionary version history, trash, and audit log are functional.
  • Custom plugins are either updated or disabled.
  • Next.js front‑end displays content for every locale used.
  • Automated tests (if any) pass against the new API.
  • Backup files are stored safely, and a restore test has been performed.

Following this checklist gives you confidence that the migration will not disrupt editors or end users.

11. Common pitfalls and how to avoid them

SymptomLikely causeFix
Cannot find module '@strapi/plugin-…'Plugin not yet released for Strapi 5Either upgrade to the plugin’s 5.x version or temporarily disable it.
Missing fields in the admin UIContent‑type JSON schema still uses the old attributes formatRun strapi generate:model again or manually migrate the schema to the new format described in the migration guide.
Locale not applied in API responsesCalls still use the old find service without localeSwitch to the Document Service API and pass the locale option.
Audit log empty after migrationRevisionary migration failed silentlyRestart Strapi with DEBUG=revisionary:* npm run develop to see detailed logs and contact support via the Support page (/docs/reference/support).

12. When to consider a fresh install instead of an in‑place upgrade

If your project relies heavily on plugins that have no Strapi 5 equivalents, or if the custom codebase is tightly coupled to Strapi 4 internals, a fresh Strapi 5 installation with a data import (using the Content Export/Import feature) may be faster. The export can be performed from the admin UI (Settings → Export data) and then imported into the new instance.

a bunch of wires that are connected to a server
Photo by Lightsaber Collection on Unsplash

Conclusion

Migrating from Strapi 4 to Strapi 5 does not require a complete rewrite; it is a series of well‑defined steps: back up, update dependencies, adjust configuration, migrate custom code, verify the Revisionary plugin, and test the Next.js front‑end. By following the checklist above, you preserve content history, keep edit‑locks and audit logs intact, and take advantage of the newer Document Service API, improved i18n handling, and performance gains of Strapi 5.

Tagsstrapiupgrademigrationnextjscontent‑management

The Revisionary team

Developers who build Revisionary, a version history plugin for Strapi 5. We write about Strapi, web development, AI and application security.

This article was written with the help of AI.

All articles