The calculator needs JavaScript and must be served over HTTP. If you opened this file from disk, run python3 -m http.server in the public folder and open http://localhost:8000.
In short
VAC is generally available from GitLab 19.5. Tracking a branch stores another copy of every finding on that branch. If each project tracks one more branch with the same findings as its default branch, the per-finding vulnerability tables roughly double. If only some projects track branches, they grow by those projects' share of your vulnerabilities. Each pipeline on a tracked branch writes about as much as one on the default branch.
To size it for your instance, fill in your plan below, then run the SQL script on your database for the rest. It only reads data.
Estimate
Enter your numbers to see an estimate.
Treat this as a starting point
These numbers come from measurements on GitLab.com and on a test install. GitLab's performance and stability vary a lot with workload and deployment, so your results may differ. Use the estimate to get an idea of the size of the change, and leave room for a margin of error. The "Plan for" figures add some headroom, but they can't cover every setup. How we measured.
Formula
For each tracked branch, VAC stores its own copy of every finding on that branch, plus its own reference to each dependency. GitLab calls each tracked branch a tracked context. A finding is one vulnerability on one branch, so the same vulnerability on two tracked branches is two findings. Bytes per vulnerability means the bytes for one finding. Shared tables (identifiers, scanners, SBOM components and occurrences, statistics) don't get new rows.
branches now = projects x tracked branches per project
branches stored = projects x (tracked branches + branches tracked and later untracked)
findings/branch = vulnerabilities per project x share
added findings = branches stored x findings/branch
dependency refs = branches now x dependencies per project x 1.1
added storage = added findings x bytes per vulnerability
+ dependency refs x bytes per dependency ref
pipelines/day = branches now x pipelines per day per branch
row updates/day = pipelines/day x (findings/branch x 5.5 + dependencies per project x 2)
WAL/day = pipelines/day x findings/branch x 14 to 17 KiB
first ingestion = added findings x 25 to 29 KiB of WAL (one time)
plan for storage = added storage (high end) x 1.5
plan for WAL/day = WAL/day (high end) x 2
1.1 is how many dependency refs a tracked branch has compared to the default branch on GitLab.com. 5.5 and 2 are row updates per finding and per dependency, measured on a GDK (see Method). Untracking removes a branch's dependency refs but keeps its findings, so branches you later untrack still count toward findings storage.
"Plan for" adds headroom on top of the high end. The two measured profiles differ by 1.83x, child rows per finding were 2.6x higher on GitLab.com than on the GDK (mostly finding links, which vary a lot by scanner), and bloat depends on your autovacuum settings, so storage gets 1.5x. WAL was only measured on lean payloads, without Sidekiq follow-up writes or continuous vulnerability scanning, so it gets 2x. If the "Plan for" figure lands in the next band, the verdict says so but keeps the lower band's colour.
Units: KiB = 1,024 bytes. GB on this page means 1,0243 bytes, the same as pg_size_pretty shows. The page rounds numbers to 2 significant figures.
Rule-of-thumb thresholds
We picked these as rules of thumb. The product doesn't enforce them, and your own hardware and backup setup matter more.
| Check | Likely fine | Plan ahead | Test first |
|---|---|---|---|
| Added storage, % of database | |||
| WAL added, % of your current WAL |
WAL (write-ahead log) is PostgreSQL's record of every change it writes. Backups, WAL archiving and replicas all have to copy it. For the presets, storage uses the GitLab.com size (the high end), and Custom uses your own number. WAL uses the high end and is checked only when you enter your current WAL per day from query 4. Until then it shows as Not checked. WAL archive space needed is WAL per day x days of retention.
Method and sources
GitLab.com production (2026-10-07)
Read-only measurements on postgres.ai thin clones of the GitLab.com production sec database, taken 2026-10-07 at about 11:00 UTC. Sizes are on disk and include table data (heap), large values stored out of line (TOAST), indexes and bloat. Row counts are planner estimates. Duplication counts are exact.
- 9,783 B per vulnerability per tracked branch across the per-branch tables (about 9.6 KiB, or 9,640 B without temporary backfill indexes).
- About 250 B per
sbom_occurrence_refsrow. - Tracked branches carry 11.5% to 18.5% of the default branch's findings, and about 1.1x its dependency refs.
| Table | Rows per finding | Bytes per finding |
|---|---|---|
vulnerability_occurrences | 1.00 | 3,922 |
vulnerability_finding_links | 11.07 | 2,965 |
vulnerability_reads | 1.00 | 1,429 |
vulnerabilities | 1.00 | 549 |
vulnerability_occurrence_identifiers | 1.94 | 418 |
security_finding_enrichments | 0.70 | 222 |
vulnerability_finding_risk_scores | 1.00 | 144 |
sbom_occurrences_vulnerabilities | 0.32 | 73 |
Controlled measurement on a GDK (2026-10-07)
GitLab master 03043cd4f1ee (committed 2026-10-07 11:00 UTC), PostgreSQL 17.8, separate sec database. One project, 2,000 findings (1,000 SAST, 1,000 dependency scanning) and 500 SBOM components, ingested on the default branch and on one tracked branch.
- About 5.2 KiB (5,336 B) per vulnerability per tracked branch, heap plus indexes, in freshly packed tables.
- About 244 B per dependency ref.
- Steady-state pipeline: 12,028 row updates and no inserts, about 6.0 per finding. 2.7 of those are HOT updates, the cheaper in-place kind that skips index writes. The estimator splits this into 5.5 per finding plus 2 per dependency.
- WAL: 14 to 17 KiB per finding per steady-state pipeline, typical for lean payloads. Real scanner payloads write more (not measured). First ingestion writes 25 to 29 KiB per finding.
- Each pipeline rewrites every tracked finding's rows, so autovacuum has more to clean on these tables. PostgreSQL keeps the old copy of each rewritten row (a dead tuple) until autovacuum removes it. Query 5 shows whether it keeps up today. More links per finding, bigger payloads and steady-state bloat are why the GitLab.com profile is the high end.
- A tracked-branch pipeline costs the same as a default-branch pipeline.
- Not measured: Sidekiq follow-up jobs (for example search indexing and statistics), so real write load is higher.
Reading the code
Data model and limits come from reading GitLab master on 2026-10-07: 54e51fb3 for the data model and the branch limit, and 05dd34b6f45 for what untracking leaves behind.
Child row counts
"Other per-finding rows" uses 5.4 per finding (GDK) to 14.2 per finding (GitLab.com). Finding links vary the most by scanner.
Things to know
- Untracking doesn't remove data. When you stop tracking a branch, its findings, vulnerabilities and vulnerability report rows stay in the database. Only the dependency refs are removed, and tracking the same branch again stores its findings again. Plan storage as if tracked branches are permanent.
- You can't get that storage back, so start with one tracked branch on a few projects, then re-run the script and compare with the estimate.
- With continuous vulnerability scanning on, new advisories create findings on every tracked branch between pipelines. This isn't in the estimate.
- If you use advanced search (Elasticsearch or OpenSearch) for the vulnerability report, each tracked branch's findings are indexed too, so plan index storage in proportion. Filtering by branch on Self-Managed needs advanced search with advanced vulnerability management.
- A project ingests one pipeline at a time. If several finish while one is being ingested, only the latest is ingested next, so fewer pipelines may be ingested than you enter.
- By default a project can track its default branch plus 3 more branches or tags.
- These estimates come from GitLab as of October 2026 (see Method), and later versions may differ. Even with the same version, results vary with workload and deployment. For the best number, run the SQL script and use Custom, then start with a few projects and compare.