Starting Strapi API on read-only DB replica

Hi all,
Is it possible to start the Strapi API on a read-only DB? It appears that when starting Strapi API, it attempts to update strapi_core_store_settings table and since the DB is read-only, the startup fails.

Here is our scenario:
We have a primary Maria DB that our Strapi admin panel connects to for content editing. This primary Maria DB has a secondary DB as a read-only replica located in another region. This secondary DB is to help with performance since the front-end site is also in that same region (and meet redundancy requirements).

We are trying to start up the Strapi API and connect to the secondary DB read-only replica but are not sure how to do this. Are we missing a certain configuration setting? We have serveAdminPanel set to false on the secondary Strapi API server (thinking this might solve this, but didn’t).

If possible this allows our front-end site (also located in that secondary region) to hit its Strapi API which would connect to its read-only DB (where that DB gets near real-time updates via DB replication from the primary DB). Our front-end site requests are all read requests (no other creates, updates, or deletes).

We researched all the documentation but no luck. Any advice is much appreciated.

Thanks in advance.

This will be the first I’m hearing of it if it’s supported out of the box.

I would suggest allowing Strapi to write to the database and allowing read-only requests via the API (through User Permissions).

Thanks for the suggestion. Our Strapi API permissions are setup as read-only; we just want to keep all its requests from that region to that region’s Strapi API, which would connect to that region’s DB (which is read only). We can’t even start the Strapi API (using npm run start), since it attempts to make some updates in the DB on startup.

If we could route the DB write requests (updates, inserts, deletes) to the primary DB, and keep all read requests (selects) at the secondary (read-only) DB, that would accomplish our goal. But, I think in order to do that, we’d have to change the core Strapi code (of which we are trying to avoid) - unless someone knows of workaround or approach we have not thought of.

Or is there another way to access the Strapi APIs without using ‘npm run start’ to start those APIs?

Did you find any solutions about read-replicas?

Unfortunately, no. :unamused:

We will most likely be submitting a new feature request with the Strapi team to see if they would implement something like this. Both AWS and Azure support read-replicas for DB failover and regional performance, so I would assume this would be a good feature to add.

Ideally, if they could add a property to the connection object in the config/database.js that indicates the DB connection was read only, implement the necessary logic to check for this property, the “npm run start” command could then execute successfully.

Hi anthonyj,
I also come up with this issue, I guess we have the similar condition, I have aws rds which have a read only replica, and I want my websit panosupplier to use it for better performance, because it only rend content from the database.

After tried a few hours, I found this quick solution:
In your file:
node_modules/@strapi/strapi/lib/Strapi.js

first: add try catch to bootstrap fuction, this is the reason that crashs the application

then: comment out function
await this.store.set

you can have a try, I am sure it can start the app now, it will also show some read only error, but it doesnot matter as you don’t want to write or change something in database.
below is the code for your reference

 async bootstrap() {
    try {
      const contentTypes = [
        coreStoreModel,
        webhookModel,
        ...Object.values(strapi.contentTypes),
        ...Object.values(strapi.components),
      ];

      this.db = await Database.init({
        ...this.config.get('database'),
        models: Database.transformContentTypes(contentTypes),
      });

      this.store = createCoreStore({ db: this.db });
      this.webhookStore = createWebhookStore({ db: this.db });

      this.entityValidator = entityValidator;
      this.entityService = createEntityService({
        strapi: this,
        db: this.db,
        eventHub: this.eventHub,
        entityValidator: this.entityValidator,
      });

      const cronTasks = this.config.get('server.cron.tasks', {});
      this.cron.add(cronTasks);

      this.telemetry.bootstrap();

      let oldContentTypes;
      if (await this.db.getSchemaConnection().hasTable(coreStoreModel.collectionName)) {
        oldContentTypes = await this.store.get({
          type: 'strapi',
          name: 'content_types',
          key: 'schema',
        });
      }

      await this.hook('strapi::content-types.beforeSync').call({
        oldContentTypes,
        contentTypes: strapi.contentTypes,
      });

      await this.db.schema.sync();

      await this.hook('strapi::content-types.afterSync').call({
        oldContentTypes,
        contentTypes: strapi.contentTypes,
      });

      // await this.store.set({
      //   type: 'strapi',
      //   name: 'content_types',
      //   key: 'schema',
      //   value: strapi.contentTypes,
      // });

      await this.startWebhooks();

      await this.server.initMiddlewares();
      await this.server.initRouting();

      await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP);

      this.cron.start();

      return this;
    } catch (error) {
      console.log(error)
    }

  }