Nearly everything in the planner’s model of parallel query is a discount: the rows are divided among the processes, and the CPU cost is divided along with them. parallel_setup_cost and parallel_tuple_cost are the two settings that charge something for going parallel. The first is a flat fee for each Gather node; the second is a fee for each row that comes up through one. The defaults are 1000 and 0.1, in the same arbitrary units as every other planner cost (a sequential page read is 1, a row is 0.01). Both are floating point with a floor of zero, and the context is user, so they can be set per session, per role, per database or per function. Both defaults were committed on September 30, 2015, and neither has changed since.

You can read both off any parallel plan. This is 18.6, on a 10-million-row table:

1=> EXPLAIN SELECT * FROM t WHERE k < 10000;
2 Gather (cost=1000.00..196150.34 rows=97339 width=73)
3 Workers Planned: 2
4 -> Parallel Seq Scan on t (cost=0.00..185416.44 rows=40558 width=73)
5 Filter: (k < 10000)

The 1000.00 is the setup fee, due before the first row. The total is the scan’s 185,416.44, plus 1,000, plus 0.1 for each of 97,339 rows. (Gather Merge pays 5% more per row, since it has to wait on every worker, and the source comment is candid about the size: “For lack of a better idea, charge an extra 5%.”) The setup fee is per Gather and not per worker. The same plan with one worker planned starts at 1000.00 too, and a query with two Gather nodes pays twice.

What the fees are weighed against is smaller than you might expect. The serial version of that scan costs 258,332: 133,334 for the pages and 124,998 for the rows. With two workers the planner divides the row cost by 2.4 (the leader counts as 0.4 of a worker) and does not divide the page cost at all, on the reasoning that the operating system’s readahead had already done what could be done. The most that two workers can save on this scan is therefore 72,915, about 28% of it, and both fees have to fit inside that. Neither parameter has any say in how many workers a plan asks for. They decide only whether a parallel plan wins.

What 1000 buys

parallel_setup_cost sets how small a query can be and still get workers. With a filtered count(*) over tables of 75 rows to the page, the plan went parallel at about 185,000 rows. That is a 19MB table (the 8MB minimum had stopped mattering long before) and a query that takes about 11 ms serially. Launching one worker and shutting it down again took 4 to 5 ms on the same machine. By the stopwatch, the breakeven was between 125,000 and 150,000 rows, so on an idle machine the default is close to right. It is also not buying much at that size: at 200,000 rows, 12.2 ms became 9.9.

The idle machine is the assumption to look at. A parallel plan’s cost is an estimate of elapsed time with a free core for every process. Nothing in it accounts for the extra CPU the plan burns, or for who else wanted those cores. I ran the same query against a 300,000-row table, where the default plan uses two workers, from pgbench on a two-core machine:

Clients Default plan (two workers) Serial plan
1 72 to 77 tps 65 to 72 tps
2 77 to 79 tps 140 to 147 tps
4 78 to 84 tps 134 to 140 tps

With one client, the parallel plan wins by a few percent. With two, it has nowhere to go, and the serial plan gets nearly twice the work out of the same hardware. I have not measured it on a larger machine, but more cores should move the point at which that happens and not remove it. In a 2025 thread proposing that parallel query be off by default, Tom Lane rejected the proposal and conceded the premise in the same message: the default costs are “over-optimistic and allow us to choose PQ when we shouldn’t.”

If the server’s job is a great many small queries, raise it. At 10000, the 300,000-row table went back to a serial plan and a 1GB table kept its two workers. To see where a value draws the line on your hardware, divide a serial query’s EXPLAIN ANALYZE time by its estimated cost to get milliseconds per cost unit; on tables shaped like these, a two-worker scan-and-aggregate goes parallel once its serial cost is about three and a half times parallel_setup_cost (wider rows raise that ratio, because more of the cost is pages). On my test machine that put 10000 at queries of roughly 80 ms. It is a blunt instrument, and Scott Mead, who started that thread, said he had not found a value that made good decisions. It is still less blunt than max_parallel_workers_per_gather = 0, because the ten-minute report keeps its workers.

A row for the price of ten

parallel_tuple_cost = 0.1 says that handing a row from a worker to the leader costs ten times what it cost to process the row in the first place. As far as I can find, nobody argued for that number when it was chosen. It appears in Amit Kapila’s first proposal for parallel cost parameters, in January 2015, under the name cpu_tuple_comm_cost. When Jeff Janes asked on pgsql-performance in 2019 how anyone was supposed to set it, Laurenz Albe went and looked, and reported that the default “seems not to have been the subject of discussion.”

Its job is to keep parallel plans away from queries that return a lot of rows. On the table above, the two-worker scan stops being chosen once the filter passes about 7% of the rows, because 693,000 rows at 0.1 each, plus the setup fee, leave the parallel plan less than 1% cheaper, and the planner calls that a tie and takes the serial plan.

The thing it is pricing has since become much cheaper. Rows travel from worker to leader through a shared-memory queue, and through PostgreSQL 14 the worker updated a shared counter and set the leader’s latch for every one of them. In 15, Dilip Kumar’s patch made it do that in batches, and his first message on the subject listed being able to reduce parallel_tuple_cost as the first reason to try. The queue got faster. The number stayed where it was.

I ran the same scan on the same two-core VM under three versions, with one worker and under EXPLAIN ANALYZE, so that no client was involved. Each row returned cost about 145 ns more than it did in the serial plan on 14.24, and under 30 ns on 15.19 and 18.6. (About half the rows cross the queue; the leader scans the rest itself.) With half the table coming back, the parallel plan was 37% slower than the serial one on 14, and 21% to 24% faster on 15 and 18. On 18.6 it was still slightly ahead at half the table when the leader had real work to do with each row (formatting it for COPY), and a CREATE TABLE AS that kept 20% of the rows took 1.76 seconds with the serial plan the defaults chose and 1.31 once parallel_tuple_cost = 0.02 let it have workers.

Janes measured this himself in that 2019 thread, before the queue was changed, and got 0.011 on an eight-CPU machine. (He was answering Tom Lane, whose instincts said ten times was if anything too low, and Andres Freund, who had measured the queue and agreed.) Andrew Dunstan posted a patch in March 2026 to scale the charge by row width. It is not in 19 beta 4, where both parameters behave exactly as they do in 18.

A common piece of advice for these two is to set them both to 0. The documentation suggests it too, as a way to find out whether a query can be parallelized at all, and for that it is the right tool. In postgresql.conf, it sends workers after almost every sequential scan over 8MB, including a SELECT * of an entire table, which on my two-core machine took 20% to 30% longer to COPY out for the help.

Leave parallel_setup_cost at 1000 on a machine with cores to spare. On one that is busy with small queries, set it to 10000 or more, and give the default back to whoever runs the big ones with ALTER ROLE reporting SET parallel_setup_cost = 1000 (which applies to sessions that log in as that role, and not to SET ROLE). On 15 and later I would set parallel_tuple_cost to 0.02, on the roles that build tables and feed reports and not on the application, whose queries should not be returning 7% of a table in the first place.