Skip to content

pgmem

Real PostgreSQL 18, in memory, for tests. No Docker, no disk. Prepare the schema once, share read-only tests, and give writing tests isolated copies in milliseconds.

A small shop’s data layer with 70 tests: 50 that only read, 20 that write. On a shared PostgreSQL server, here one started with Testcontainers, the writing tests have to wait for each other, each behind a reset of the shared data. With pgmem the suite takes one snapshot after the migrations and seed data; the read-only tests share that database and every writing test forks its own copy, so everything after the snapshot runs in parallel.

The same 70 tests (50 read-only, 20 writing) on the same schema, migrations and seed data, run with go test at parallelism 8. Each timeline is the run whose total was the median of 5 runs on Apple M3 (8 cores). Bars are the nodes of each execution model; lower and to the left is faster.

Whole suite, wall clock

  • pgmem204 ms
    196 ms–219 ms over 5 runs
  • Testcontainers, one shared server1.73 s
    1.70 s–1.81 s over 5 runs
  • PostgreSQL, one shared server646 ms
    633 ms–671 ms over 5 runs
  • docker run, one shared server2.48 s
    2.26 s–3.59 s over 5 runs

Tests are drawn in the 8 slots of go test -parallel; a slot shows the tests that ran in it one after another. All timelines share one time axis.

  • Boot database
  • Import schema + seed master data
  • Snapshot
  • Fork (a writing test's own copy)
  • Input sample data
  • Reset data
  • Test that queries data
  • Test that modifies a table
pgmem 204 ms totalin the test process; read tests share the template, each writing test forks the snapshot, all in parallel20 forks, one per writing test (20); the 50 read tests use the template database without a fork.
pgmem: Timeline of the representative run, 204 ms050010001500total 204 msTestMainTestMain · Boot database: 39 ms (0.0 → 39 ms)TestMain · Import schema + seed master data: 47 ms (39 → 86 ms)TestMain · Snapshot: 3.2 ms (86 → 89 ms)Shared database (read tests)Shared database (read tests) · Input sample data: 13 ms (122 → 135 ms)parallel slot 1CreateOrder/two_lines · Fork (a writing test's own copy): 13 ms (89 → 102 ms)CreateOrder/two_lines · Input sample data: 18 ms (103 → 121 ms)CreateOrder/two_lines · Test that modifies a table: 1.2 ms (121 → 122 ms)TopProducts/top_1 · Test that queries data: 0.5 ms (135 → 135 ms)TopProducts/top_30 · Test that queries data: 0.6 ms (135 → 136 ms)TopProducts/top_8 · Test that queries data: 1.1 ms (136 → 137 ms)TopProducts/top_2 · Test that queries data: 1.0 ms (137 → 138 ms)ApplyDiscount/games_0 · Fork (a writing test's own copy): 10 ms (138 → 148 ms)ApplyDiscount/games_0 · Input sample data: 13 ms (148 → 161 ms)ApplyDiscount/games_0 · Test that modifies a table: 0.4 ms (161 → 161 ms)ListOrders/user_71 · Test that queries data: 0.1 ms (161 → 161 ms)ListOrders/user_64 · Test that queries data: 0.1 ms (161 → 161 ms)ListOrders/user_57 · Test that queries data: 0.5 ms (161 → 162 ms)ListOrders/user_22 · Test that queries data: 0.8 ms (162 → 163 ms)ListOrders/user_29 · Test that queries data: 0.5 ms (163 → 163 ms)SearchProducts/product_1 · Test that queries data: 1.0 ms (163 → 164 ms)SearchProducts/product_8 · Test that queries data: 0.8 ms (164 → 165 ms)parallel slot 2CreateOrder/five_lines · Fork (a writing test's own copy): 14 ms (89 → 103 ms)CreateOrder/five_lines · Input sample data: 12 ms (103 → 116 ms)CreateOrder/five_lines · Test that modifies a table: 0.9 ms (116 → 116 ms)ShipOrder/one_paid · Fork (a writing test's own copy): 8.8 ms (117 → 126 ms)ShipOrder/one_paid · Input sample data: 18 ms (126 → 144 ms)ShipOrder/one_paid · Test that modifies a table: 0.4 ms (144 → 144 ms)GetOrder/orders_6_16_26_36 · Test that queries data: 3.5 ms (145 → 148 ms)RevenueByCategory/Garden · Test that queries data: 0.8 ms (148 → 149 ms)RevenueByCategory/Toys · Test that queries data: 0.3 ms (149 → 150 ms)RevenueByCategory/Games · Test that queries data: 0.3 ms (150 → 150 ms)RevenueByCategory/Music · Test that queries data: 0.4 ms (150 → 150 ms)GetOrder/orders_10_20_30_40 · Test that queries data: 2.0 ms (150 → 152 ms)SearchProducts/product_0 · Test that queries data: 0.8 ms (152 → 153 ms)DeleteUser/user_36 · Fork (a writing test's own copy): 11 ms (153 → 164 ms)DeleteUser/user_36 · Input sample data: 9.6 ms (164 → 173 ms)DeleteUser/user_36 · Test that modifies a table: 0.3 ms (173 → 174 ms)parallel slot 3CreateOrder/out_of_stock · Fork (a writing test's own copy): 13 ms (89 → 102 ms)CreateOrder/out_of_stock · Input sample data: 20 ms (102 → 122 ms)CreateOrder/out_of_stock · Test that modifies a table: 0.2 ms (122 → 122 ms)TopProducts/top_10 · Test that queries data: 1.5 ms (135 → 136 ms)TopProducts/top_20 · Test that queries data: 1.4 ms (136 → 138 ms)ApplyDiscount/movies_50 · Fork (a writing test's own copy): 9.3 ms (138 → 147 ms)ApplyDiscount/movies_50 · Input sample data: 13 ms (147 → 160 ms)ApplyDiscount/movies_50 · Test that modifies a table: 0.6 ms (160 → 161 ms)ListOrders/user_43 · Test that queries data: 0.2 ms (161 → 161 ms)SearchProducts/product_4 · Test that queries data: 0.3 ms (161 → 162 ms)ListOrders/user_36 · Test that queries data: 0.4 ms (162 → 162 ms)ListOrders/user_15 · Test that queries data: 0.7 ms (162 → 163 ms)SearchProducts/product_2 · Test that queries data: 0.9 ms (163 → 164 ms)SearchProducts/product_6 · Test that queries data: 0.6 ms (164 → 164 ms)SearchProducts/product_5 · Test that queries data: 0.7 ms (164 → 165 ms)parallel slot 4CreateOrder/one_line · Fork (a writing test's own copy): 16 ms (89 → 106 ms)CreateOrder/one_line · Input sample data: 14 ms (106 → 119 ms)CreateOrder/one_line · Test that modifies a table: 0.5 ms (119 → 120 ms)ShipOrder/twice · Fork (a writing test's own copy): 10 ms (120 → 130 ms)ShipOrder/twice · Input sample data: 10 ms (130 → 141 ms)ShipOrder/twice · Test that modifies a table: 3.5 ms (141 → 144 ms)GetOrder/orders_5_15_25_35 · Test that queries data: 3.1 ms (145 → 148 ms)GetOrder/orders_2_12_22_32 · Test that queries data: 2.3 ms (148 → 151 ms)RevenueByCategory/Outdoors · Test that queries data: 0.6 ms (151 → 151 ms)RevenueByCategory/Sports · Test that queries data: 0.5 ms (151 → 152 ms)GetOrder/orders_8_18_28_38 · Test that queries data: 1.8 ms (152 → 154 ms)DeleteUser/user_22 · Fork (a writing test's own copy): 8.6 ms (154 → 162 ms)DeleteUser/user_22 · Input sample data: 9.8 ms (162 → 172 ms)DeleteUser/user_22 · Test that modifies a table: 0.3 ms (172 → 172 ms)parallel slot 5CancelOrder/paid · Fork (a writing test's own copy): 12 ms (89 → 102 ms)CancelOrder/paid · Input sample data: 17 ms (102 → 119 ms)CancelOrder/paid · Test that modifies a table: 0.7 ms (119 → 119 ms)ShipOrder/pending_rejected · Fork (a writing test's own copy): 8.3 ms (121 → 129 ms)ShipOrder/pending_rejected · Input sample data: 18 ms (129 → 146 ms)ShipOrder/pending_rejected · Test that modifies a table: 0.2 ms (146 → 147 ms)GetOrder/orders_4_14_24_34 · Test that queries data: 2.3 ms (147 → 150 ms)RevenueByCategory/Movies · Test that queries data: 0.4 ms (150 → 150 ms)GetOrder/orders_9_19_29_39 · Test that queries data: 2.1 ms (150 → 152 ms)DeleteUser/user_15 · Fork (a writing test's own copy): 5.8 ms (152 → 158 ms)DeleteUser/user_15 · Input sample data: 14 ms (158 → 172 ms)DeleteUser/user_15 · Test that modifies a table: 0.3 ms (172 → 172 ms)parallel slot 6CancelOrder/another_paid · Fork (a writing test's own copy): 15 ms (89 → 105 ms)CancelOrder/another_paid · Input sample data: 12 ms (105 → 116 ms)CancelOrder/another_paid · Test that modifies a table: 2.8 ms (116 → 119 ms)TopProducts/top_50 · Test that queries data: 1.7 ms (135 → 136 ms)TopProducts/top_15 · Test that queries data: 2.1 ms (136 → 139 ms)ApplyDiscount/music_25 · Fork (a writing test's own copy): 11 ms (139 → 149 ms)ApplyDiscount/music_25 · Input sample data: 9.7 ms (149 → 159 ms)ApplyDiscount/music_25 · Test that modifies a table: 0.5 ms (159 → 160 ms)ListOrders/user_50 · Test that queries data: 1.4 ms (162 → 163 ms)SearchProducts/product_3 · Test that queries data: 1.8 ms (163 → 165 ms)SearchProducts/product_9 · Test that queries data: 0.3 ms (165 → 165 ms)parallel slot 7CancelOrder/shipped · Fork (a writing test's own copy): 14 ms (90 → 104 ms)CancelOrder/shipped · Input sample data: 11 ms (104 → 115 ms)CancelOrder/shipped · Test that modifies a table: 0.5 ms (115 → 116 ms)ShipOrder/all_paid · Fork (a writing test's own copy): 6.0 ms (118 → 124 ms)ShipOrder/all_paid · Input sample data: 12 ms (124 → 136 ms)ShipOrder/all_paid · Test that modifies a table: 0.9 ms (136 → 136 ms)TopProducts/top_3 · Test that queries data: 2.3 ms (137 → 139 ms)GetOrder/orders_1_11_21_31 · Test that queries data: 0.9 ms (139 → 140 ms)GetOrder/orders_7_17_27_37 · Test that queries data: 0.5 ms (140 → 141 ms)RevenueByCategory/Books · Test that queries data: 6.7 ms (141 → 147 ms)GetOrder/orders_3_13_23_33 · Test that queries data: 3.4 ms (147 → 151 ms)RevenueByCategory/Kitchen · Test that queries data: 0.9 ms (151 → 152 ms)RevenueByCategory/Tools · Test that queries data: 0.4 ms (152 → 152 ms)ListOrders/user_8 · Test that queries data: 0.5 ms (152 → 152 ms)SearchProducts/product_7 · Test that queries data: 0.8 ms (153 → 153 ms)DeleteUser/user_29 · Fork (a writing test's own copy): 10 ms (153 → 164 ms)DeleteUser/user_29 · Input sample data: 9.6 ms (164 → 173 ms)DeleteUser/user_29 · Test that modifies a table: 0.3 ms (173 → 174 ms)parallel slot 8CancelOrder/pending · Fork (a writing test's own copy): 13 ms (90 → 103 ms)CancelOrder/pending · Input sample data: 16 ms (103 → 119 ms)CancelOrder/pending · Test that modifies a table: 0.7 ms (119 → 120 ms)TopProducts/top_5 · Test that queries data: 2.0 ms (135 → 137 ms)ApplyDiscount/books_10 · Fork (a writing test's own copy): 7.6 ms (137 → 145 ms)ApplyDiscount/books_10 · Input sample data: 19 ms (145 → 165 ms)ApplyDiscount/books_10 · Test that modifies a table: 0.4 ms (165 → 165 ms)ms since the suite started
Testcontainers, one shared server 1.73 s totaltestcontainers-go postgres module on the official image, in memory (tmpfs, no fsync); writing tests take turns after a resetno copies: the 50 read tests and the 20 writing tests share one database.
Testcontainers, one shared server: Timeline of the representative run, 1.73 s050010001500total 1.73 sTestMainTestMain · Boot database: 761 ms (0.0 → 761 ms)761TestMain · Import schema + seed master data: 108 ms (761 → 868 ms)108Shared database (read tests)Shared database (read tests) · Reset data: 5.1 ms (1.62 s → 1.63 s ms)Shared database (read tests) · Input sample data: 29 ms (1.63 s → 1.65 s ms)parallel slot 1CreateOrder/two_lines · Reset data: 9.2 ms (869 → 878 ms)CreateOrder/two_lines · Input sample data: 36 ms (878 → 913 ms)CreateOrder/two_lines · Test that modifies a table: 2.0 ms (913 → 915 ms)CreateOrder/one_line · Reset data: 8.1 ms (915 → 923 ms)CreateOrder/one_line · Input sample data: 28 ms (923 → 951 ms)CreateOrder/one_line · Test that modifies a table: 0.9 ms (951 → 952 ms)CreateOrder/five_lines · Reset data: 7.6 ms (952 → 960 ms)CreateOrder/five_lines · Input sample data: 27 ms (960 → 987 ms)CreateOrder/five_lines · Test that modifies a table: 1.9 ms (987 → 989 ms)CreateOrder/out_of_stock · Reset data: 7.5 ms (989 → 996 ms)CreateOrder/out_of_stock · Input sample data: 27 ms (996 → 1.02 s ms)CreateOrder/out_of_stock · Test that modifies a table: 0.4 ms (1.02 s → 1.02 s ms)CancelOrder/paid · Reset data: 7.0 ms (1.02 s → 1.03 s ms)CancelOrder/paid · Input sample data: 26 ms (1.03 s → 1.06 s ms)CancelOrder/paid · Test that modifies a table: 1.7 ms (1.06 s → 1.06 s ms)CancelOrder/pending · Reset data: 6.8 ms (1.06 s → 1.07 s ms)CancelOrder/pending · Input sample data: 27 ms (1.07 s → 1.09 s ms)CancelOrder/pending · Test that modifies a table: 1.3 ms (1.09 s → 1.09 s ms)CancelOrder/another_paid · Reset data: 6.8 ms (1.09 s → 1.1 s ms)CancelOrder/another_paid · Input sample data: 28 ms (1.1 s → 1.13 s ms)CancelOrder/another_paid · Test that modifies a table: 1.4 ms (1.13 s → 1.13 s ms)CancelOrder/shipped · Reset data: 6.9 ms (1.13 s → 1.14 s ms)CancelOrder/shipped · Input sample data: 28 ms (1.14 s → 1.17 s ms)CancelOrder/shipped · Test that modifies a table: 0.4 ms (1.17 s → 1.17 s ms)ShipOrder/one_paid · Reset data: 6.6 ms (1.17 s → 1.17 s ms)ShipOrder/one_paid · Input sample data: 28 ms (1.17 s → 1.2 s ms)ShipOrder/one_paid · Test that modifies a table: 0.5 ms (1.2 s → 1.2 s ms)ShipOrder/pending_rejected · Reset data: 6.1 ms (1.2 s → 1.21 s ms)ShipOrder/pending_rejected · Input sample data: 30 ms (1.21 s → 1.24 s ms)ShipOrder/pending_rejected · Test that modifies a table: 0.2 ms (1.24 s → 1.24 s ms)ShipOrder/all_paid · Reset data: 7.7 ms (1.24 s → 1.24 s ms)ShipOrder/all_paid · Input sample data: 40 ms (1.24 s → 1.28 s ms)ShipOrder/all_paid · Test that modifies a table: 4.2 ms (1.28 s → 1.29 s ms)ShipOrder/twice · Reset data: 8.4 ms (1.29 s → 1.3 s ms)ShipOrder/twice · Input sample data: 37 ms (1.3 s → 1.33 s ms)ShipOrder/twice · Test that modifies a table: 0.5 ms (1.33 s → 1.34 s ms)DeleteUser/user_15 · Reset data: 4.9 ms (1.34 s → 1.34 s ms)DeleteUser/user_15 · Input sample data: 32 ms (1.34 s → 1.37 s ms)DeleteUser/user_15 · Test that modifies a table: 0.8 ms (1.37 s → 1.37 s ms)DeleteUser/user_22 · Reset data: 5.3 ms (1.37 s → 1.38 s ms)DeleteUser/user_22 · Input sample data: 30 ms (1.38 s → 1.41 s ms)DeleteUser/user_22 · Test that modifies a table: 0.6 ms (1.41 s → 1.41 s ms)DeleteUser/user_29 · Reset data: 5.9 ms (1.41 s → 1.41 s ms)DeleteUser/user_29 · Input sample data: 29 ms (1.41 s → 1.44 s ms)DeleteUser/user_29 · Test that modifies a table: 0.6 ms (1.44 s → 1.44 s ms)DeleteUser/user_36 · Reset data: 6.0 ms (1.44 s → 1.45 s ms)DeleteUser/user_36 · Input sample data: 28 ms (1.45 s → 1.48 s ms)DeleteUser/user_36 · Test that modifies a table: 0.6 ms (1.48 s → 1.48 s ms)ApplyDiscount/books_10 · Reset data: 5.7 ms (1.48 s → 1.48 s ms)ApplyDiscount/books_10 · Input sample data: 27 ms (1.48 s → 1.51 s ms)ApplyDiscount/books_10 · Test that modifies a table: 1.0 ms (1.51 s → 1.51 s ms)ApplyDiscount/music_25 · Reset data: 6.0 ms (1.51 s → 1.52 s ms)ApplyDiscount/music_25 · Input sample data: 30 ms (1.52 s → 1.55 s ms)ApplyDiscount/music_25 · Test that modifies a table: 0.6 ms (1.55 s → 1.55 s ms)ApplyDiscount/movies_50 · Reset data: 5.3 ms (1.55 s → 1.55 s ms)ApplyDiscount/movies_50 · Input sample data: 29 ms (1.55 s → 1.58 s ms)ApplyDiscount/movies_50 · Test that modifies a table: 0.6 ms (1.58 s → 1.58 s ms)ApplyDiscount/games_0 · Reset data: 7.1 ms (1.58 s → 1.59 s ms)ApplyDiscount/games_0 · Input sample data: 29 ms (1.59 s → 1.62 s ms)ApplyDiscount/games_0 · Test that modifies a table: 0.6 ms (1.62 s → 1.62 s ms)TopProducts/top_1 · Test that queries data: 1.0 ms (1.65 s → 1.66 s ms)TopProducts/top_3 · Test that queries data: 0.8 ms (1.66 s → 1.66 s ms)TopProducts/top_2 · Test that queries data: 1.6 ms (1.66 s → 1.66 s ms)RevenueByCategory/Books · Test that queries data: 3.5 ms (1.66 s → 1.66 s ms)RevenueByCategory/Outdoors · Test that queries data: 1.9 ms (1.66 s → 1.66 s ms)RevenueByCategory/Sports · Test that queries data: 0.5 ms (1.66 s → 1.66 s ms)RevenueByCategory/Tools · Test that queries data: 0.8 ms (1.66 s → 1.66 s ms)RevenueByCategory/Kitchen · Test that queries data: 0.6 ms (1.66 s → 1.67 s ms)RevenueByCategory/Garden · Test that queries data: 0.8 ms (1.67 s → 1.67 s ms)RevenueByCategory/Games · Test that queries data: 0.6 ms (1.67 s → 1.67 s ms)RevenueByCategory/Movies · Test that queries data: 0.4 ms (1.67 s → 1.67 s ms)ListOrders/user_8 · Test that queries data: 1.0 ms (1.67 s → 1.67 s ms)ListOrders/user_57 · Test that queries data: 0.6 ms (1.67 s → 1.67 s ms)ListOrders/user_43 · Test that queries data: 1.2 ms (1.67 s → 1.67 s ms)GetOrder/orders_7_17_27_37 · Test that queries data: 2.9 ms (1.67 s → 1.67 s ms)SearchProducts/product_7 · Test that queries data: 1.9 ms (1.67 s → 1.67 s ms)parallel slot 2TopProducts/top_10 · Test that queries data: 14 ms (1.65 s → 1.67 s ms)ListOrders/user_50 · Test that queries data: 1.6 ms (1.67 s → 1.67 s ms)GetOrder/orders_5_15_25_35 · Test that queries data: 2.7 ms (1.67 s → 1.67 s ms)SearchProducts/product_6 · Test that queries data: 0.8 ms (1.67 s → 1.67 s ms)SearchProducts/product_2 · Test that queries data: 1.9 ms (1.67 s → 1.68 s ms)parallel slot 3TopProducts/top_50 · Test that queries data: 13 ms (1.65 s → 1.67 s ms)ListOrders/user_71 · Test that queries data: 2.0 ms (1.67 s → 1.67 s ms)GetOrder/orders_9_19_29_39 · Test that queries data: 3.1 ms (1.67 s → 1.67 s ms)SearchProducts/product_8 · Test that queries data: 1.4 ms (1.67 s → 1.67 s ms)parallel slot 4TopProducts/top_30 · Test that queries data: 12 ms (1.65 s → 1.67 s ms)RevenueByCategory/Music · Test that queries data: 2.1 ms (1.67 s → 1.67 s ms)ListOrders/user_36 · Test that queries data: 0.3 ms (1.67 s → 1.67 s ms)GetOrder/orders_1_11_21_31 · Test that queries data: 2.4 ms (1.67 s → 1.67 s ms)GetOrder/orders_3_13_23_33 · Test that queries data: 1.5 ms (1.67 s → 1.67 s ms)SearchProducts/product_5 · Test that queries data: 0.9 ms (1.67 s → 1.67 s ms)SearchProducts/product_1 · Test that queries data: 0.5 ms (1.67 s → 1.67 s ms)parallel slot 5TopProducts/top_20 · Test that queries data: 14 ms (1.65 s → 1.67 s ms)ListOrders/user_22 · Test that queries data: 0.5 ms (1.67 s → 1.67 s ms)GetOrder/orders_10_20_30_40 · Test that queries data: 2.9 ms (1.67 s → 1.67 s ms)GetOrder/orders_2_12_22_32 · Test that queries data: 1.9 ms (1.67 s → 1.67 s ms)parallel slot 6TopProducts/top_15 · Test that queries data: 11 ms (1.65 s → 1.67 s ms)RevenueByCategory/Toys · Test that queries data: 3.4 ms (1.67 s → 1.67 s ms)ListOrders/user_29 · Test that queries data: 1.1 ms (1.67 s → 1.67 s ms)GetOrder/orders_6_16_26_36 · Test that queries data: 3.5 ms (1.67 s → 1.67 s ms)SearchProducts/product_4 · Test that queries data: 1.1 ms (1.67 s → 1.67 s ms)parallel slot 7TopProducts/top_8 · Test that queries data: 13 ms (1.66 s → 1.67 s ms)ListOrders/user_64 · Test that queries data: 1.2 ms (1.67 s → 1.67 s ms)ListOrders/user_15 · Test that queries data: 1.4 ms (1.67 s → 1.67 s ms)GetOrder/orders_4_14_24_34 · Test that queries data: 2.2 ms (1.67 s → 1.67 s ms)SearchProducts/product_0 · Test that queries data: 1.0 ms (1.67 s → 1.67 s ms)SearchProducts/product_3 · Test that queries data: 2.5 ms (1.67 s → 1.68 s ms)parallel slot 8TopProducts/top_5 · Test that queries data: 15 ms (1.66 s → 1.67 s ms)GetOrder/orders_8_18_28_38 · Test that queries data: 2.9 ms (1.67 s → 1.67 s ms)SearchProducts/product_9 · Test that queries data: 1.5 ms (1.67 s → 1.67 s ms)ms since the suite started

The suite, the harness and every node’s timing are described on the benchmarks page; the project is in bench/modelcase.

The real thing

PostgreSQL 18.3 compiled to WebAssembly and translated to Go. Your SQL, planner behaviour and error codes are PostgreSQL’s, not an emulator’s.

Nothing to install

Install the library and its platform binary. No Docker daemon, no container runtime on CI, no database files written to disk.

Share reads, isolate writes

Run migrations and seed data once, then share a fork across read-only tests and give writing tests isolated copies. Writing tests can run in parallel.

Any driver

pgmem listens on the PostgreSQL wire protocol, so pgx, psycopg, JDBC, node-postgres, ORMs and migration tools work unchanged.

s, err := pgmem.Start(ctx, pgmem.Options{Database: "app"})
if err != nil {
t.Fatal(err)
}
defer s.Close()
db, _ := sql.Open("pgx", s.DSN())

The same schema, queries and per-test isolation, measured on one machine against the official image with docker run, Testcontainers, and a devbox-installed PostgreSQL.

pgmem values are medians from 5 runs on 2026-09-18; the Docker, Testcontainers and devbox comparison baselines are from 2026-09-14. Docker and Testcontainers memory uses 3 fresh OrbStack runs. Bars scale to the largest value in each card; lower is better.

Start to first query

From command start to TCP SELECT 1; downloads are excluded and Devbox uses an initialized cluster.

  • pgmem · Go library70 ms
  • Docker · PostgreSQL 181.65 s
  • Testcontainers · Go1.09 s
  • devbox · services up411 ms

Fresh copy per test

Median time to create an isolated database copy and run a join.

  • pgmem · Go library15 ms
  • Docker · PostgreSQL 1861 ms
  • Testcontainers · Go21 ms
  • devbox · services up83 ms

Indexed SELECT

Warm indexed SELECT over TCP.

  • pgmem · Go library29 µs
  • Docker · PostgreSQL 1893 µs
  • Testcontainers · Go102 µs
  • devbox · services up28 µs

Sort 200k rows

Median time to sort and count 200k rows.

  • pgmem · Go library117 ms
  • Docker · PostgreSQL 18259 ms
  • Testcontainers · Go258 ms
  • devbox · services up228 ms

Backend + test-process memory

After setup, before queries. Docker and Testcontainers sum container usage (PostgreSQL, plus Ryuk for Testcontainers) and benchmark-process growth. OrbStack VM changes are reported separately.

  • pgmem · Go library103 MiB
  • Docker · PostgreSQL 1877 MiB
  • Testcontainers · Go137 MiB
  • devbox · services up36 MiB

Initial download footprint

Same MB scale; Go link growth, compressed image layers, and devbox cache download measure different things.

  • pgmem · Go library33.1 MB
  • Docker · PostgreSQL 18117.9 MB
  • Testcontainers · Go120.0 MB
  • devbox · services up112.7 MB

The current stripped darwin/arm64 pgmem binary is 36.7 MB, including the export-pruned AOT backend. The benchmark page records the latest pgmem-only rerun and keeps the comparison baselines reproducible.

Query latency, methodology and how to reproduce are on the benchmarks page.