Skip to main content

Server API: Lifecycle

Page summary:

The Server API has 3 lifecycle functions. Use register() to declare capabilities before the app is fully initialized, bootstrap() to run logic once Strapi is initialized, and destroy() to clean up resources on shutdown. Each function receives { strapi } as its argument.

Lifecycle functions control when your plugin's server-side logic runs during the Strapi application startup and shutdown sequence. They are exported from the server entry file alongside routes, controllers, services, and other server blocks.

Prerequisites

Before diving deeper into the concepts on this page, please ensure you have:

Startup sequence

Understanding when each lifecycle runs helps you put the right code in the right place:

Loading diagram...

PhaseWhat is available in your plugin
2. Registerstrapi object is available, but the database is not initialized yet and routing is not initialized yet
4. BootstrapFull runtime: database initialized, routes initialized, services and content-types loaded, other plugins available
5. ShutdownShutdown is in progress; use this hook to release resources before Strapi finishes stopping
Note

Each lifecycle function is called once per plugin instance. If a lifecycle is called a second time on the same plugin instance (for example in custom tests), Strapi throws an error. This does not occur under normal operation.

register()

Type: Function

register() runs early in startup, before database initialization and before route initialization.

Use register() to:

  • Register the server side of custom fields
  • Register database migrations
  • Register server middlewares on the Strapi HTTP server (e.g. strapi.server.use(...))
  • Extend another plugin's content-types or interface before bootstrap
/src/plugins/my-plugin/server/src/register.js
'use strict';

module.exports = ({ strapi }) => {
// Register a server-level middleware early in startup
strapi.server.use(async (ctx, next) => {
ctx.set('X-Plugin-Version', '1.0.0');
await next();
});
};

bootstrap()

Type: Function

bootstrap() runs after module lifecycle registration (plugins/APIs), database initialization, route initialization, and Content API action registration.

Use bootstrap() to:

  • Seed the database with initial data
  • Register admin RBAC actions using strapi.service('admin::permission').actionProvider.registerMany(...)
  • Register cron jobs
  • Subscribe to database lifecycle events
  • Call services from your plugin or other plugins
  • Set up cross-plugin integrations that require other plugins to be registered first
/src/plugins/my-plugin/server/src/bootstrap.js
'use strict';

module.exports = async ({ strapi }) => {
// Register admin RBAC actions for this plugin
await strapi.service('admin::permission').actionProvider.registerMany([
{
section: 'plugins',
displayName: 'Read',
uid: 'read',
pluginName: 'my-plugin',
},
{
section: 'plugins',
displayName: 'Settings',
uid: 'settings',
pluginName: 'my-plugin',
},
]);
};

destroy()

Type: Function

destroy() is called when the Strapi instance is shutting down. It is optional. Only implement it when your plugin holds resources that need explicit cleanup.

Use destroy() to:

  • Close external connections (databases, message queues, WebSocket servers)
  • Clear intervals or timeouts set in bootstrap()
  • Remove event listeners registered during the plugin's lifetime
/src/plugins/my-plugin/server/src/destroy.js
'use strict';

module.exports = ({ strapi }) => {
// Close an external connection opened in bootstrap()
strapi.plugin('my-plugin').service('queue').disconnect();
};

Best practices

  • Keep register() lightweight. It runs before full initialization.

  • Use bootstrap() for database reads/writes. The database is initialized during the bootstrap phase, not during register. Any call to strapi.documents() or a service that queries the database belongs in bootstrap().

  • Register admin RBAC actions in bootstrap(). Use strapi.service('admin::permission').actionProvider.registerMany(...) in bootstrap(). This is when the permission service is available. Content API actions are registered automatically by Strapi during the same phase.

  • Always pair resource creation with destroy(). If your plugin opens a connection, registers a global interval, or attaches a process listener in bootstrap(), implement destroy() to clean up those resources. This prevents resource leaks during testing and graceful restarts.

  • Avoid hard dependencies between plugins in register(). At registration time, the order in which other plugins have registered is not guaranteed. Cross-plugin calls that rely on another plugin being initialized belong in bootstrap().

  • Prefer services over inline logic. Move non-trivial bootstrap logic into a dedicated service method (e.g. strapi.plugin('my-plugin').service('setup').initialize()). This keeps lifecycle files readable and the logic testable.