Directus returning 503 "Under pressure"
If your Directus API starts answering 503 and the error message ends in "Under pressure", nothing crashed. Directus is turning requests away on purpose because the one Node thread doing the work is full. We hit exactly this in a load test, so here is what it means, when it kicks in, and what to change.
Directus 11 + Postgres 17, one instance on a 4 vCPU / 8 GB server, default config, list items API
| Simulated users | What Directus did |
|---|---|
| Under 100 | 159 requests a second, p95 320 ms |
| About 250 | 217 requests a second, p95 1.5 s, about as many as it would ever answer |
| About 500 | 208 requests a second, p95 3.4 s, extra users now waiting in line |
| About 1,000 | 208 requests a second, p95 6.8 s |
| About 1,300 | First 503s, just under 4 minutes into the run |
| About 1,500 to 2,500 | Every request answered with a 503, in about 0.3 s |
| Whole run | 49,928 requests answered before the 503s took over |
What the 503 means
Directus ships with a pressure limiter, and it is on by default (PRESSURE_LIMITER_ENABLED=true). Every 250 ms it checks how busy the Node event loop is. If the loop is more than 99% busy, or work is waiting more than 500 ms to run, new requests get a 503 straight away until it recovers. The full error message is: Service "api" is unavailable. Under pressure.
So the 503 is a guard, not a crash. It keeps the server responsive and the failure obvious, instead of letting every request pile up until it times out. The database is usually fine. The thing that is full is the Directus process.
What we saw in a load test
We ran the official Directus image with Postgres 17 in one docker compose stack on a fresh 4 vCPU / 8 GB server, gave it 10 posts, and ramped 2,500 simulated users at the list items API over 10 minutes. The table above is what came back.
Directus stopped getting faster at about 250 users and held around 210 requests a second. Past that, latency climbed while extra users waited, and at about 1,300 users the limiter tripped. From there almost everything got a fast 503. That is also why the raw peak of the run reads about 3,400 requests a second: 503s are cheap to send, and the useful number is the 210.
What ran out was one Node thread at 100% of a core. The server as a whole was about 34% busy, Postgres used at most 0.3 cores, and Directus used about 1.2 GB of memory.
Why turning it off does not fix it
Setting PRESSURE_LIMITER_ENABLED=false makes the 503s go away, but not the limit behind them. The same full thread now has to queue every request instead.
We saw what that looks like in the same test. Strapi has no limiter by default. It answered every request, but by the end its p95 was close to 10 seconds. Fast failures or slow everything: the ceiling is the same, and only how it fails changes.
What raises the limit
Run more Directus instances. One instance runs on one Node thread, so on a 4 core server three cores sit mostly idle. Run several Directus containers behind a reverse proxy such as nginx, Caddy or Traefik. The Directus docs recommend Redis for multi-container setups so synchronization (SYNCHRONIZATION_STORE=redis), caching and rate limiting work across all instances. From what one instance did here, 4 instances on this server were estimated at about 380 requests a second. That is an estimate, so measure again after the change.
Turn on the data cache. CACHE_ENABLED is false by default. For public reads that many people request, turning it on with a short CACHE_TTL lets repeated requests skip the database query and most of the work. With several instances, set CACHE_STORE=redis so they share it.
Cache in front of it. If the same public content goes to everyone, a CDN or a reverse proxy cache with even a few seconds of TTL turns a crowd of identical requests into one request to Directus.
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.
Tuning the limiter itself
PRESSURE_LIMITER_RETRY_AFTER is off by default. Setting it adds a Retry-After header to the 503, so clients and proxies know when to try again.
PRESSURE_LIMITER_MAX_EVENT_LOOP_DELAY (500 ms) and PRESSURE_LIMITER_MAX_EVENT_LOOP_UTILIZATION (0.99) decide when it trips. Raising them lets more requests in, at the cost of slower answers for everyone. The memory checks, PRESSURE_LIMITER_MAX_MEMORY_RSS and PRESSURE_LIMITER_MAX_MEMORY_HEAP_USED, are off by default.
Find your number before your users do
The point where the 503s start depends on your collections, permissions and payload sizes, so the run above is a reference, not your answer. Stresix starts your Directus repository or compose stack on a fresh server, ramps up simulated users, and the report shows when errors began, which thread or service ran out, and whether more instances or a bigger server would help. Your first run is free.
Frequently asked questions
Why does Directus return 503 "Under pressure"?
Its pressure limiter is on by default. When the Node event loop is over 99% busy or work waits more than 500 ms, Directus answers new requests with a 503 straight away instead of queueing them.
Should I disable the Directus pressure limiter?
Usually not. Turning it off swaps fast 503s for slow requests that may time out, and the limit stays the same. Run more instances or add caching to raise the limit itself.
How many requests a second can Directus handle?
In our run, one instance with default config on a 4 vCPU / 8 GB server held about 210 requests a second on the list items API with 10 posts. Your collections, permissions and payloads will change that, so test your own setup.
Does Directus need Redis to run several instances?
The Directus docs recommend Redis for multi-container deployments, so synchronization, caching, rate limiting and WebSockets work across all instances.
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