Strapi slow under load? What one instance can take

Your Strapi API gets slow when traffic goes up, but the server looks mostly idle, the database is quiet and nothing is erroring. We saw exactly that in a load test. One Strapi process can only use one CPU core, and once that core is full, Strapi does not fail. It makes every request wait.

Strapi 5 + Postgres 17, one instance, default config

Simulated usersWhat Strapi did
Under 100206 requests a second, p95 240 ms
About 250287 requests a second, p95 1.1 s
About 500290 requests a second, p95 2.0 s
About 1,000286 requests a second, p95 4.1 s
About 1,500286 requests a second, p95 6.8 s
2,400 to 2,500279 requests a second, p95 9.9 s
Whole run133,269 requests answered, 0 errors

The short answer

Strapi runs as one Node.js process, and Node runs your JavaScript on one thread. However many cores the server has, one Strapi instance can only keep one of them busy. In our run it reached about 290 requests a second by 250 simulated users and never went higher. Every user after that just waited longer, until p95 was close to 10 seconds.

The server as a whole was about 39% busy the entire time. So if your Strapi is slow and your CPU graph says there is plenty left, this is the first thing to check.

What we saw in a load test

We ran the stock create-strapi-app project, built in production mode, with Postgres 17 in one docker compose stack on a fresh 4 vCPU / 8 GB server (shared vCPU). It got 10 posts, and 2,500 simulated users ramped up over 10 minutes against the list posts API. The table above is what came back.

What ran out was one Node thread at 100% of a core. Postgres used about 0.3 cores and Strapi about 1 GB of memory, so neither the database nor memory was close to the limit.

Why zero errors is not good news

Strapi answered every single request, which looks great on an error rate dashboard. But by the end most of them took close to 10 seconds, and to someone waiting on a page that is about as bad as an error.

This is a design choice, not a bug. Strapi has no load limiter by default, so under overload everything gets slow instead of some requests failing fast. Directus, which ran on the same server, does the opposite and answers 503 once it is full. Either way the ceiling is the same, so watch p95 latency, not just errors.

How to check your own server

Run top on the server and press 1 to see each core. One core near 100% with the rest mostly idle, at the moment Strapi is slow, is this problem. In docker stats, 100% means one full core, so a Strapi container stuck around 100% on a 4 core box points the same way. The Node.js guide linked below has more ways to check.

Run more than one instance

Strapi has no built-in cluster mode, so you run several copies yourself. The simplest way on one server is several containers from the same image behind a reverse proxy such as nginx, Caddy or Traefik, for example with replicas in docker compose. The Strapi docs recommend PM2 as the process manager in production, and PM2 can also start several processes.

From what one instance did here, 4 instances on this server were estimated at about 430 requests a second. That is an estimate, not a measurement, so measure again after the change.

What to sort out before running several instances

Same environment everywhere. Start every instance with the same .env, so APP_KEYS and the JWT secrets match. Otherwise a token issued by one instance is rejected by the next.

Uploads. The default upload provider writes files into public/uploads inside the container that received them. With several containers, mount one shared volume for that folder or use an upload provider such as S3, or files will appear on one instance and be missing on the others.

Cron. cron.enabled is false by default. If you turned it on, every instance runs every task, because nothing elects one instance to do it. Enable it on one instance only.

Database connections. Each instance opens its own pool, which defaults to min 2 and max 10. Four instances can use 40 connections, and Postgres allows 100 by default. The docs also suggest a pool min of 0 when running in Docker.

Schema changes. Each instance checks and syncs the database schema when it boots. After a deploy that changes content types, start one instance first and the rest once it is up, so they do not race each other.

Take load off the one thread

Cache public reads. If the same published content goes to everyone, a CDN or reverse proxy cache with even a few seconds of TTL turns a crowd of identical requests into one request to Strapi.

Ask for less. Use the fields and populate parameters to request only the fields and relations a page actually needs. Turning less data into JSON is less work for the thread that is full.

Buy a bigger server last. A single instance will not use the extra cores. More CPU only helps once you run more instances and every core is busy.

Check your own Strapi app

Your content types, relations and populate settings change the number, so the run above is a reference, not your answer. Stresix starts your Strapi repository or compose stack, database included, on a fresh server, ramps up simulated users, and measures CPU per core and per thread inside each container. The report says when one thread was the limit and whether more instances or a bigger server would help. Your first run is free.

Frequently asked questions

Why is Strapi slow under load?

Usually because one Strapi instance runs on one Node thread. In our run that thread hit 100% of a core at about 290 requests a second while the server was 39% busy, and every extra request waited in line.

Does Strapi use multiple CPU cores?

One instance uses one core for your requests. To use more, run several instances behind a reverse proxy or with PM2, and share uploads, secrets and the database between them.

How many requests a second can Strapi handle?

In our run, one Strapi 5 instance with default config on a 4 vCPU / 8 GB server held about 290 requests a second on the list API with 10 posts, with zero errors but p95 near 10 seconds at 2,500 users. Your content types and queries will change that.

Will a bigger server make Strapi faster?

Not with one instance, because it leaves the extra cores idle. Run more instances first, and pay for more cores once every core is busy.

Related guides

Stop guessing your specs. Start knowing them.

Point Stresix at a repo or Docker image and get a number you can deploy on, in one run. Your first run is free, no card.

Start a free load test