1. Home
  2. Tools
  3. Storage calculator
Tool · sizing

Storage calculator

How much disk and memory will your embeddings need? This calculator models how PostgreSQL and pgvector lay vectors out on 8 KB pages, and the table at the bottom shows how close it gets to 22 tables and indexes we actually built.

Tested on pgvector 0.8.7 · PostgreSQL 18.6Updated 2026-10-27

All four layouts for these rows and dimensions

Table and index size, MB. Bars missing = can't be indexed at this dimension.

  • Table
  • Index

    How the estimate works#

    PostgreSQL stores everything in 8 KB pages, and pgvector index tuples can't span pages. That makes size a packing problem, not just "rows × bytes":

    • Value size: vector = 4 × dims + 8 bytes; halfvec = 2 × dims + 8; binary-quantized bit = dims / 8 + 8.
    • HNSW: each row is an element tuple (72-byte header + value) plus a neighbour tuple of 6-byte pointers — 2 × m slots on layer 0 and m more per extra layer. pgvector keeps the two together on one page when they fit. A 1,536-dim vector element is ~6.4 KB with its neighbours, so only one fits per page and ~1.7 KB per page is unused. Halving to halfvec fits two per page.
    • IVFFlat: one index tuple per row (8-byte header + value), grouped by list, plus one list tuple holding each centre.
    • Table: values over ~2 KB are moved out of line into TOAST in 1,996-byte chunks, four chunks per page; the heap row keeps an 18-byte pointer.

    Accuracy against real indexes#

    The model is the same JavaScript file this page runs. At build time we execute it against every table and index from our benchmarks and print the difference:

    RelationMeasuredModelError
    HNSW, vector(1536), m 8, 100K rows781 MB781 MB+0.0%
    HNSW, vector(1536), m 16, 100K rows781 MB781 MB+0.0%
    HNSW, vector(1536), m 32, 100K rows781 MB781 MB+0.0%
    HNSW, halfvec(1536), m 16, 100K rows391 MB391 MB+0.0%
    HNSW, bit(1536), m 16, 100K rows48 MB47 MB-2.7%
    HNSW, halfvec(3072), m 16, 99K rows773 MB773 MB+0.0%
    HNSW, bit(3072), m 16, 99K rows66 MB65 MB-0.4%
    HNSW, vector(1536), m 16, 100,000 rows781 MB781 MB+0.0%
    HNSW, vector(1536), m 16, 250,000 rows1.91 GB1.91 GB+0.0%
    HNSW, vector(1536), m 16, 500,000 rows3.81 GB3.81 GB+0.0%
    IVFFlat, vector(1536), 100 lists, 100,000 rows782 MB783 MB+0.1%
    IVFFlat, vector(1536), 316 lists, 100,000 rows784 MB786 MB+0.3%
    IVFFlat, vector(1536), 1000 lists, 100,000 rows789 MB797 MB+1.0%
    IVFFlat, vector(1536), 500 lists, 500,000 rows3.82 GB3.82 GB+0.1%
    IVFFlat, vector(1536), 707 lists, 500,000 rows3.82 GB3.83 GB+0.1%
    IVFFlat, vector(1536), 2000 lists, 500,000 rows3.83 GB3.85 GB+0.4%
    Table, vector(1536), 100K rows795 MB795 MB-0.1%
    Table, halfvec(1536), 100K rows400 MB400 MB-0.1%
    Table, vector(3072), 99K rows1.28 GB1.34 GB+4.9%
    Table, vector(1536), 100,000 rows795 MB795 MB-0.1%
    Table, vector(1536), 250,000 rows1.94 GB1.94 GB-0.1%
    Table, vector(1536), 500,000 rows3.88 GB3.88 GB-0.1%

    Measured with pg_relation_size() (indexes) and pg_table_size() (tables, including TOAST) on PostgreSQL 18.6 + pgvector 0.8.7. Real tables also carry your other columns, dead tuples between vacuums, and fill-factor slack — budget 10–20% on top.

    Memory: building and serving#

    Building. HNSW builds fastest when the whole graph fits in maintenance_work_mem. When it doesn't, pgvector logs a notice and continues much more slowly — 2.9× slower in our test. The calculator's memory figure is scaled from that measurement: at 64 MB the graph held 9,753 1,536-dim elements, about 6,881 bytes each.

    Serving. HNSW search hops between random pages, so latency stays low only while the index is cached in memory. Plan RAM for the index size (via shared_buffers plus the OS page cache), and more if queries also read full vectors for re-ranking.

    Frequently asked questions

    How big is an HNSW index in pgvector?

    For 1,536-dimension vector embeddings with default settings, about 8 KB per row: each element tuple (~6.2 KB) plus its neighbour list fills most of a page on its own. We measured 781 MB for 100,000 rows. A halfvec index fits two elements per page (391 MB), and a binary-quantized index many more (48 MB).

    How much maintenance_work_mem do I need to build an HNSW index?

    Enough to hold the whole graph in memory. Our build with the 64 MB default reported the graph no longer fit after 9,753 of 100,000 tuples — roughly 6,881 bytes per 1,536-dim element. The calculator scales that to your row count and dimensions.