Hardware Sizing for Self-Hosted DefectDojo Pro (Pro)

Sizing a DefectDojo deployment comes down to two questions. How much data are you holding, and how many people are working in it at once. This page gives starting points for both.

Treat what follows as general guidance rather than a specification. The figures lean deliberately conservative, and they assume a deployment doing everyday triage alongside regular scan imports. Your own numbers will move depending on how you use the product, so read the notes under the table before you provision anything.

Specs are given as generic vCPU and memory figures so they apply to any cloud provider or on-premise hardware. The application node guidance assumes Kubernetes. If you run Docker Compose on a single host, use the same totals.

Sizing table

FindingsConcurrent usersDatabaseApplication nodes
Up to 100KUp to ~252–4 vCPU / 16–32 GB2 × (2–4 vCPU / 8–16 GB)
100K–500K~25–504–8 vCPU / 32–64 GB2–3 × (4 vCPU / 16 GB)
500K–1M~50–1008 vCPU / 64–96 GB2–3 × (8 vCPU / 32 GB)
1M–5M~100–2508–16 vCPU / 96–128 GB5–6 × (8 vCPU / 32 GB)
5M–10M~250–50016–32 vCPU / 128–192 GB9–10 × (8 vCPU / 32 GB)
500M500+192 vCPU / 768 GB+50+ × (8 vCPU / 32 GB)

Where you land inside a range depends on your workload. Start at the upper end of a range if anything in What pushes you up a tier applies to you.

The 500M row is a reference point at the far end rather than a continuation of the pattern above it, so do not interpolate between it and the 10M tier. A deployment sitting between those two needs to be sized individually. It also assumes work that hardware alone will not do for you, covered in Very large deployments.

How to read these numbers

Database memory matters more than database CPU

DefectDojo runs aggregation-heavy queries across your findings. Those stay fast while the working set and its indexes are served from memory, and they degrade quickly once the database starts reaching for disk. When you have to choose, buy memory before you buy cores. The table reflects that. Memory roughly doubles from tier to tier while CPU counts move much more slowly.

Application nodes track users, not findings

The concurrent user figures in the table assume smaller datasets belong to smaller teams. That assumption breaks often. If you hold 200K findings but have 100 people in the UI at once, size the application layer for the users and leave the database where your finding count puts it. The two scale independently.

There is one exception, at the far end of the table. Import and deduplication run on the application tier rather than in the database, so once a data set is large enough for that work to dominate, node count follows ingest volume instead of user count. That is why the 500M row sits well above what its user figure on its own would suggest.

Node shape is flexible

Kubernetes will spread the load whether you give it a few large nodes or more small ones, so the node counts above are one workable arrangement rather than a requirement. Two things are worth holding to. Keep at least two nodes so losing one doesn’t take the application down, and avoid nodes smaller than 2 vCPU / 8 GB so individual pods schedule comfortably.

Storage

Plan on 20–30 GB of database storage per million findings. Where you fall in that spread depends on how much you hang off each finding. Long descriptions and large endpoint counts push you toward the top of it. The finding rows themselves are a small part of this. Most of the space goes to indexes and to the related tables that hang off each finding, so sizing from row data alone will leave you well short.

Every tier through 10M fits inside a few hundred GB of general-purpose SSD. Storage is cheap next to the cost of running out, so provision for where you expect to be in a year rather than where you are now. If your provider offers storage autoscaling, turn it on.

The 500M row is sized at 2.5 TB. That figure assumes the live data set is actively managed, with older findings archived out of the hot path rather than accumulating indefinitely. Applied naively, the per-million rate above would put an unmanaged 500M deployment several times higher. If you are heading toward this scale, treat the archiving strategy as part of the sizing exercise rather than something to sort out later.

Storage at this scale also needs attention to throughput, not only capacity. Once the working set stops fitting in memory, default baseline IOPS on general-purpose volumes becomes the limit well before capacity does.

Media storage is separate and usually much smaller. It holds uploaded artifacts such as screenshots and risk acceptance documents, so size it from your own upload habits.

What pushes you up a tier

Finding count is the headline number, but several things will have you sizing up sooner than the count alone suggests.

  • Import volume and frequency. Large scans arriving often, especially several at the same time, put sustained load on both the database and the async workers. CI pipelines that import on every build are the usual cause.
  • Deduplication. Deduplication compares incoming findings against what you already hold. The more findings you have and the broader your deduplication configuration, the more work every import does.
  • Reporting and dashboards. Metrics views and large report generation are read-heavy, and they hit the database harder than day-to-day triage does.
  • API traffic. Integrations that poll or pull large result sets add concurrent load that never shows up in your interactive user count.
  • Retention. Deployments that keep everything forever grow into the next tier on schedule. Archiving or deleting old data keeps you where you are for longer.

Very large deployments

Past the 10M tier, hardware stops being the whole answer. Two things change.

The binding constraint moves from reading to writing. Deduplication compares each incoming finding against what you already hold, so the cost of an import grows with the size of the data set behind it. At the top of the table this is usually what you hit first, ahead of anything users notice in the UI. Whatever import volume built a data set that large is generally still running, so you pay that cost continuously rather than once.

The memory figures assume the hot set stays small. A deployment works recent findings and leaves older ones largely untouched, which is what lets a database hold far more data than it has memory and still perform well. If your access pattern is genuinely spread across the whole data set, you will need more memory than the table lists, and past a point no single instance will have enough.

Both of those point at the same work. Partitioning and archiving cold findings out of the live data set matter more at this scale than another increment of vCPU, and heavy reporting belongs on a read replica rather than on the primary. Plan for that alongside the hardware rather than after it, and talk to us before you provision.

When in doubt, round up

The figures here already lean conservative, and being one size too large costs far less than being one size too small. Database memory pressure in particular does not degrade gracefully. Performance holds up fine until it doesn’t.

Adding application capacity later is straightforward, since you add nodes. Resizing a database typically means downtime, so that is the one worth getting right up front.

Questions or support

These are starting points, not limits. If your deployment sits at the top of the table, or your workload doesn’t resemble the assumptions here, talk to us before you provision. Contact your account representative or .