PostgreSQL VARCHAR vs TEXT: Real-World Performance Benchmarks

#Engineering#Architecture#Performance
PostgreSQL VARCHAR vs TEXT: Real-World Performance Benchmarks

PostgreSQL treats VARCHAR and TEXT identically at the storage layer, so there is no meaningful speed or space difference between them. VARCHAR(n) adds one thing: a length check on write. Pick TEXT by default, and enforce limits explicitly where the business rule demands it.

The official documentation says the same: there is no performance difference among the character types, apart from the extra storage of blank-padded CHAR(n) and a few CPU cycles for the length check. The expensive mistakes here are not type choice. They are table-rewriting migrations, unbounded input, oversized indexes, and ORMs that silently pick a type for you. This article covers the internals, gives you a benchmark harness you can run on your own hardware, and shows where the real costs hide.

Table of Contents

How PostgreSQL Stores VARCHAR and TEXT

Both types are variable-length ("varlena") types. They share the same on-disk representation, the same comparison functions, the same collation behavior, and the same TOAST handling. VARCHAR(n) is TEXT plus a type modifier that triggers a length check during input.

The mechanics that matter:

  • ▹Header overhead: short values carry a 1-byte header, longer values a 4-byte header. This is identical for both types.
  • ▹TOAST: when a row exceeds roughly 2 kB, PostgreSQL compresses and/or moves large values out of line into a TOAST table. Again, identical for both types. Read the TOAST docs once; it explains most "why is my table bloated" tickets.
  • ▹Length semantics: n counts characters, not bytes. Multi-byte UTF-8 is handled correctly.
  • ▹Maximum size: a single value tops out around 1 GB regardless of type.
  • ▹CHAR(n): the outlier. It blank-pads to n, wastes space, and has surprising comparison semantics. Avoid it.

You can verify the storage claim in thirty seconds:

SELECT
  pg_column_size('hello world'::varchar) AS varchar_bytes,
  pg_column_size('hello world'::text)    AS text_bytes,
  pg_column_size('hello world'::char(20)) AS char_bytes;

The first two match. The CHAR(20) result is larger because of padding.

The Benchmark Harness

Most online "benchmarks

Engineering Consultation

Build With Precision.

Partner with ByteForth to architect autonomous AI agents, enterprise SaaS platforms, and cloud infrastructure. Transparent roadmaps, senior engineering pods, and weekly production releases.

✓

24-Hour Architect Response

Direct technical scoping with senior engineers, zero sales fluff.

✓

100% Client-Owned IP & Strict NDA

Enterprise security standards, clean VPC deployments, and complete code ownership.

✓

US, UK & European Timezone Overlap

Synchronous communication, sprint reviews, and direct Slack integration.