AWS RDS for PostgreSQL vs ServersCamp managed PostgreSQL: pgbench TPC-B

Comparative benchmark of managed PostgreSQL across two providers. May 2026.

TL;DR

We compared managed PostgreSQL 18.3 on comparable hardware (2 vCPU / 8 GB / 500 GB) across two providers: AWS RDS db.m6i.large and ServersCamp db-m-8g. One workload - pgbench TPC-B-like, three database sizes (in-cache, partial cache, disk-bound), three runs each. No tuning - both out of the box.

About the CPUs. AWS m6i.large runs on Intel Xeon Platinum 8375C (Ice Lake, 2021, 2.9 / 4.0 GHz base / max turbo, per AWS). ServersCamp runs on Intel Xeon Gold 6132 (Skylake-SP, 2017, 2.6 / 3.7 GHz base / max turbo, per Intel ARK) - that's four years and one or two architecture generations of difference. This gives AWS a theoretical ~10-15% IPC advantage plus +8-11% clock headroom on CPU-bound workloads. We ran the test with that gap on the table on purpose, to see whether AWS's managed-PG overhead eats into that lead on disk-bound scenarios. For a clean CPU pairing we'd want AWS db.m5.large (also Skylake) - that's planned for the next round, see the end of the article.

ServersCamp was tested in two storage QoS configurations (everything else - vCPU, RAM, PG version - identical):

  • Test 1 - 5,000 IOPS / 225 MB/s. Deliberately constrained, well below the current default (10,000 IOPS / 500 MB/s). The point of running it: probe how much storage QoS contributes to the disk-bound result by halving both IOPS and throughput vs Test 2.
  • Test 2 - 12,000 IOPS / 500 MB/s, manually aligned with AWS RDS gp3's free baseline for 400+ GiB volumes. The point: isolate the storage tier's contribution and evaluate managed-PG as a product on its own at storage parity.

Why 12,000 IOPS / 500 MB/s. AWS RDS gp3 ships with 3,000 IOPS / 125 MB/s baseline on volumes up to 400 GiB. From 400 GiB upward the baseline auto-scales to 12,000 IOPS / 500 MB/s included in the price - and stays there with no extra payment. So we sized the AWS RDS volume at 500 GiB on purpose: that's the typical, sensible move for a customer who wants the maximum "free" storage baseline. ServersCamp Test 2 is calibrated to that exact baseline - a fair pairing against what AWS gives away without provisioned IOPS upcharges.

The numbers (median TPS, Δ ServersCamp T2 vs AWS):

ScenarioAWS m6iScamp T2ΔLead
bench100 (in-cache, CPU-bound)3,9763,745-5.8%AWS
bench1000 (partial cache, mixed)2,7622,818+2.0%ServersCamp
bench10000 (disk-bound, ~150 GB)2,2762,481+9.0%ServersCamp

Key findings:

  1. On CPU-bound, AWS is ahead ~6% thanks to the newer CPU (Ice Lake 8375C vs Skylake 6132).
  2. On disk-bound, ServersCamp T2 leads by 9% TPS and p95 latency is roughly half of AWS (13.5 vs 26 ms).
  3. On stability, ServersCamp T2 dramatically outperforms AWS: CoV TPS 4% vs 23%, max dip -15% vs -68%.
  4. ServersCamp Test 1 (5k/225, sub-default) trails AWS on disk-bound - that's the storage QoS we deliberately constrained, not managed-PG. Going back to default-or-above (10k/500 or 12k/500) inverts the result.

Raw data. Run scripts, every run.txt summary, and per-interval aggregate logs from all three scenarios are published at github.com/serverscamp/pgbench-aws-vs-serverscamp. Every number in this article is verifiable - clone the repo and run the same scripts on your own AWS RDS and ServersCamp accounts.

The same numbers as bars below. AWS leads on bench100 (in-cache, where only CPU matters); ServersCamp T2 leads on bench1000 and bench10000 (where storage starts to dominate). The grouping at bench100 - where all three bars are within ~6% of each other - is essentially CPU-generation noise.

Median TPS by scenario (higher is better) 0 1000 2000 3000 4000 TPS bench100 in-cache (~1.5 GB) 3976 3729 3745 bench1000 partial cache (~15 GB) 2762 2265 2818 bench10000 disk-bound (~150 GB) 2276 1957 2481 AWS RDS m6i ServersCamp T1 (5k IOPS) ServersCamp T2 (12k IOPS)
Median TPS across three runs per scenario. On bench100 (CPU-bound, DB fits in cache) AWS m6i leads. On bench1000 and bench10000 (disk-touching workloads) ServersCamp T2 overtakes AWS at storage parity.

Latency tracks TPS in mirror image - more transactions per second means lower per-transaction latency. The gap on bench10000 (disk-bound) is what later in the article shows up as p95 latency cut roughly in half for ServersCamp T2.

Average latency by scenario (lower is better) 0 5 10 15 latency, ms bench100 in-cache 8.07 8.58 8.73 bench1000 partial cache 11.59 14.13 11.36 bench10000 disk-bound 14.06 16.35 12.90 AWS RDS m6i ServersCamp T1 (5k IOPS) ServersCamp T2 (12k IOPS)
Average latency from pgbench summary, Run 2 of each scenario. On bench1000/10000 the gap mirrors TPS: where TPS is higher, latency is lower.

Disclaimer

What this is. A reproducible comparison of two managed PostgreSQL services: AWS RDS (db.m6i.large) and ServersCamp (db-m-8g). One workload - standard pgbench TPC-B-like, three database sizes, three runs each. The test was run by one engineer in one day. It does not claim to be the industry's last word.

What this is NOT.

  • Not a production-grade SLA test. Three runs per scenario is statistically thin for final calls.
  • Not a read-only / point-lookup / bulk-load workload. Only TPC-B (write-heavy mixed).
  • Not a fault-tolerance / replica / failover test.
  • Not a comprehensive cost analysis. The Cost section shows on-demand list prices for the configurations we tested, but doesn't model backup, data transfer, support tier upgrades, or volume-discount agreements.

About latency and percentile metrics. pgbench was run with --aggregate-interval=10, which records average, min, and max latency per 10-second window - but not the per-transaction distribution. Every "p95 latency" mentioned in this article is the p95 of the per-window average across 60-120 windows per run, not p95/p99/p999 across individual transactions (which number in the millions). We made that trade-off deliberately for log volume and simplicity. It's a weaker signal than a "real" p99. For production SLA claims on tail latency you'd want a per-transaction logging run - that's a separate iteration, not in this article.

Principle. We test managed PG as the product, as it ships. No postgresql.conf tuning, no custom parameter groups. The provider's default is part of the product.

About the two ServersCamp configurations. Test 1 (5k IOPS / 225 MB/s) is below the current default tier (10k IOPS / 500 MB/s) - we ran it deliberately constrained to probe how strongly storage QoS drives the result. Test 2 (12k / 500) is calibrated to AWS gp3's free baseline at 400+ GiB volumes, so the comparison there is at storage parity. Neither test represents a "default product"; they're two points along the storage-QoS axis chosen to bracket the question.

What's left out of frame. AWS db.m5.large (Skylake-equivalent for a clean CPU pairing with ServersCamp) is planned, but not in this article.

Test standtop

Equipment overview

 AWSServersCamp
Instance classRDS PostgreSQL db.m6i.largeManaged PostgreSQL db-m-8g
vCPU / RAM2 vCPU / 8 GiB2 vCPU / 8 GB
CPU generationIntel Xeon Platinum 8375C - Ice Lake (3rd gen Xeon Scalable, 2021)Intel Xeon Gold 6132 - Skylake-SP (1st gen Xeon Scalable, 2017)
Clock (base / max turbo, per Intel ARK)2.9 / 4.0 GHz2.6 / 3.7 GHz
Disk size500 GiB500 GB
Storage techEBS gp3 - networked SDSManaged-volume - networked SDS
Storage IOPS12,000 (RDS gp3 baseline for 400+ GiB volumes, no upcharge)5,000 (Test 1) / 12,000 (Test 2 - manually aligned)
Storage throughput500 MB/s (RDS gp3 baseline for 400+ GiB)225 MB/s (Test 1) / 500 MB/s (Test 2)
PostgreSQL version18.3-R1 (Amazon-build)18.3 (PGDG-build)
pgbench version (client)18.318.3
SSL in testsrequirerequire
max_connections200200
Topologysingle instance (Single-AZ, no replicas)single instance, no replicas
Backupdefaults, untoucheddefaults, untouched
RegionFrankfurt (eu-central-1)Bucharest (eu-east-ro-1)

Client VM (pgbench generator)

ParameterAWS EC2 c6i.xlargeServersCamp burst-l
Hostnameip-172-31-25-222vm-ff4fd7714b235241
vCPU / RAM4 / 8 GiB4 / 8 GB (burst limits removed)
OSUbuntu 26.04 LTS (kernel 7.0.0-1004-aws)Ubuntu 25.04
Disk100 GiB gp3 (SDS)100 GB standard SDS
Networkup to 12.5 Gbps1 Gbit (initial), later upped to 10 Gbit
pgbench version18.318.3
Placementsame AZ as RDS (eu-central-1a)same network as the DB
The architectural asymmetry to keep in mind: AWS m6i runs on Ice Lake (2021) CPUs, ServersCamp on Skylake (2017). Roughly +10-15% IPC and +8-11% clock headroom in AWS's favour (per Intel ARK base/turbo specs), which we should see playing out on CPU-bound runs.

Methodologytop

Principles

  • Out of the box - no ALTER SYSTEM, no custom parameter groups, no tuning. The provider's default is the product.
  • Identical pgbench parameters on both sides: -c 32 -j 4 -M prepared --no-vacuum.
  • Three database sizes, covering different regimes:
    • scale=100 (~1.5 GB) - fits entirely in page cache, measures CPU and locks.
    • scale=1000 (~15 GB) - partial cache, mixed CPU + disk.
    • scale=10000 (~150 GB) - disk-bound, random IO dominates.
  • Three runs per size. Before each run: CHECKPOINT + wait for autovacuum to finish.
  • Warmup before the series: 5 min for bench100/1000, 10 min for bench10000.
  • Measurement: 10 min for bench100/1000, 20 min for bench10000.
  • --log --aggregate-interval=10 - TPS/latency captured every 10 seconds for stability analysis.

1. Environment

Set once per shell session on each pgbench client VM. Connection details below are anonymised; substitute your own RDS endpoint and ServersCamp managed-PG endpoint from the respective control panels.

AWS EC2 client

export PGHOST="<your-instance>.eu-central-1.rds.amazonaws.com"
export PGPORT="5432"
export PGUSER="postgres"
export PGPASSWORD='<your-password>'
export PGSSLMODE="require"

ServersCamp VM client

export PGHOST="postgres-<id>.apps.serverscamp.com"
export PGPORT="5432"
export PGUSER="<your-user>"
export PGPASSWORD='<your-password>'
export PGSSLMODE="require"

Both sides use PGSSLMODE=require so encryption overhead is comparable. Default libpq settings otherwise - no statement timeouts, no client-side pooling, no application_name games.

2. Creating the test databases and loading data

One-time setup before the run scripts. pgbench -i -s N creates the four standard pgbench tables and loads N×100k accounts. Wrap the bench10000 init in screen or tmux - it takes ~26 minutes on AWS, ~38 minutes on the slowest ServersCamp config.

AWS

psql -d postgres <<'SQL'
CREATE DATABASE bench100;
CREATE DATABASE bench1000;
CREATE DATABASE bench10000;
SQL

time pgbench -d bench100   -i -s 100
time pgbench -d bench1000  -i -s 1000
time pgbench -d bench10000 -i -s 10000

ServersCamp

Same shape. Note that on ServersCamp the per-tenant user does not have access to the maintenance postgres database; the platform provisions a default database per user (here db_<id>) which we connect to for DDL.

psql -d db_<id> <<'SQL'
CREATE DATABASE bench100;
CREATE DATABASE bench1000;
CREATE DATABASE bench10000;
SQL

time pgbench -d bench100   -i -s 100
time pgbench -d bench1000  -i -s 1000
time pgbench -d bench10000 -i -s 10000

Before Test 2 on ServersCamp (after raising storage QoS from 5k/225 to 12k/500), drop and recreate to measure init from a clean slate:

psql -d db_<id> <<'SQL'
DROP DATABASE IF EXISTS bench100;
DROP DATABASE IF EXISTS bench1000;
DROP DATABASE IF EXISTS bench10000;

CREATE DATABASE bench100;
CREATE DATABASE bench1000;
CREATE DATABASE bench10000;
SQL

time pgbench -d bench100   -i -s 100
time pgbench -d bench1000  -i -s 1000
time pgbench -d bench10000 -i -s 10000

3. Run script

Same structure for all three scenarios. Only the database name, the warmup duration, and the measurement duration change.

bench100 - AWS variant

settle() {
  echo "[$(date +%H:%M:%S)] CHECKPOINT + waiting for autovacuum..."
  psql -d postgres -c "CHECKPOINT;" >/dev/null
  while [ "$(psql -d postgres -tAc "SELECT count(*) FROM pg_stat_activity WHERE backend_type='autovacuum worker'")" != "0" ]; do
    echo "  autovacuum still working..."
    sleep 10
  done
  echo "[$(date +%H:%M:%S)] ready"
}

mkdir -p ~/results/aws/bench100 && cd ~/results/aws/bench100

settle
echo "=== WARMUP ==="
pgbench -d bench100 -c 32 -j 4 -T 300 -M prepared --no-vacuum

for i in 1 2 3; do
  settle
  echo "=== RUN $i ==="
  pgbench -d bench100 -c 32 -j 4 -T 600 -M prepared --no-vacuum \
    --log --log-prefix=run${i} --aggregate-interval=10 \
    2>&1 | tee run${i}.txt
done

echo "=== DONE bench100 ==="

bench100 - ServersCamp variant

Identical to the AWS version, except settle() uses psql -d bench100 (the per-tenant user does not have access to postgres), and results live under ~/results/serverscamp/bench100:

settle() {
  echo "[$(date +%H:%M:%S)] CHECKPOINT + waiting for autovacuum..."
  psql -d bench100 -c "CHECKPOINT;" >/dev/null
  while [ "$(psql -d bench100 -tAc "SELECT count(*) FROM pg_stat_activity WHERE backend_type='autovacuum worker'")" != "0" ]; do
    echo "  autovacuum still working..."
    sleep 10
  done
  echo "[$(date +%H:%M:%S)] ready"
}

mkdir -p ~/results/serverscamp/bench100 && cd ~/results/serverscamp/bench100

settle
echo "=== WARMUP ==="
pgbench -d bench100 -c 32 -j 4 -T 300 -M prepared --no-vacuum

for i in 1 2 3; do
  settle
  echo "=== RUN $i ==="
  pgbench -d bench100 -c 32 -j 4 -T 600 -M prepared --no-vacuum \
    --log --log-prefix=run${i} --aggregate-interval=10 \
    2>&1 | tee run${i}.txt
done

echo "=== DONE bench100 ==="

bench1000

Same structure. Only changes:

  • Database name: bench100bench1000 everywhere (including in settle() on the ServersCamp side).
  • Output folder: ~/results/{aws,serverscamp}/bench1000.
  • Same warmup (5 min, -T 300) and same measurement (10 min, -T 600) as bench100.

bench10000

Same structure, but longer runs because cold-cache disk-bound TPS takes longer to stabilise:

  • Database name: bench10000.
  • Output folder: ~/results/{aws,serverscamp}/bench10000.
  • Warmup: 10 min (-T 600) instead of 5.
  • Measurement: 20 min (-T 1200) instead of 10.
# settle() identical, paste from above

mkdir -p ~/results/aws/bench10000 && cd ~/results/aws/bench10000   # or /serverscamp/

settle
echo "=== WARMUP ==="
pgbench -d bench10000 -c 32 -j 4 -T 600 -M prepared --no-vacuum

for i in 1 2 3; do
  settle
  echo "=== RUN $i ==="
  pgbench -d bench10000 -c 32 -j 4 -T 1200 -M prepared --no-vacuum \
    --log --log-prefix=run${i} --aggregate-interval=10 \
    2>&1 | tee run${i}.txt
done

echo "=== DONE bench10000 ==="

4. What settle() does

Called before warmup and before every measurement run - never skipped, never reused. Two steps:

  1. CHECKPOINT - forces the server to flush all dirty shared buffers to disk. Without this, an in-flight checkpoint started by the previous run can leak IO contention into the current measurement.
  2. Poll pg_stat_activity every 10 seconds until no autovacuum worker backends are running. Autovacuum competes for the same CPU and IO as pgbench - measuring while it's working would understate steady-state TPS.

Note: on the ServersCamp side, settle() connects to the bench database itself (e.g. psql -d bench100) rather than to a maintenance database, because the per-tenant user does not have access to the postgres DB. pg_stat_activity is server-wide, so the count of autovacuum workers is the same regardless of which DB you connect from.

5. pgbench parameters - identical on both sides

FlagValueWhy this value
-c 3232 concurrent clientsOLTP-grade concurrency. High enough to saturate a 2-vCPU instance and surface lock contention; not so high that connection overhead dominates.
-j 44 worker threadsMatches the 4 vCPUs on the pgbench client VM. One worker per core avoids client-side scheduling becoming the bottleneck before the server is.
-M preparedprepared statementsRealistic for any application using a connection pool (PgBouncer, JDBC, asyncpg) - the parse/plan cost is paid once. Simple-protocol mode would mostly measure parser overhead, not the database engine.
--no-vacuumdo not VACUUM before runThe pre-run VACUUM warms the cache and skews the first minute of the measurement. Skipping it lets autovacuum behave naturally and the measurement starts from the actual steady state we just settled into.
--logper-thread logEnables post-hoc analysis: TPS over time, latency variance, dip detection. Without this, only the summary line at the end of the run is available.
--log-prefix=runNfilename prefixKeeps three runs' logs distinct in the same folder (otherwise pgbench overwrites pgbench_log.PID).
--aggregate-interval=1010-second aggregatesTrade-off between log volume and resolution. 10 s is dense enough to see checkpoint stalls and dips; per-transaction logging would multiply the log size by ~30,000 per run with no analytical gain at this stage.

6. Output files

After each scenario, ~/results/{provider}/{benchN}/ contains:

  • runN.txt - the pgbench summary printed at the end of run N: TPS, latency average, transactions processed, failed transactions. This is what we copy into the article body and the comparison tables.
  • runN.<PID> and runN.<PID>.1, runN.<PID>.2, runN.<PID>.3 - per-thread aggregate logs (4 files per run, one per -j worker). Each line is a 10-second window with timestamp, transaction count, latency mean/min/max. These feed the stability analysis later in the article.

All of the above - run scripts, raw runN.txt summaries, and per-interval aggregate logs from every run - is checked in at github.com/serverscamp/pgbench-aws-vs-serverscamp. Clone it, run the same scripts on your own AWS RDS and ServersCamp accounts, and verify any number in this article.

Two ServersCamp configurations - read this

ServersCamp goes through the full benchmark cycle twice with different storage QoS, deliberately, to isolate how much of the disk-bound result is storage-driven vs database-driven. The current ServersCamp default storage tier is 10,000 IOPS / 500 MB/s; both Test configurations bracket that.

Test 1Test 2
Storage IOPS5,00012,000
Storage throughput225 MB/s500 MB/s
Position vs default~½ IOPS, ~½ throughput - deliberately constrained~20% above default IOPS, default throughput - matched to AWS gp3 free baseline
Why this pointprobe how much storage QoS alone drives the disk-bound result"at storage parity with AWS, how does managed-PG itself stack up?"

Everything else - vCPU, RAM, PG version, pg_hba - is the same across both. CPU and RAM on both ServersCamp runs are identical.

Reading only Test 1, you might conclude ServersCamp loses to AWS on disk-bound. That's not what's losing - what's losing is the deliberately constrained 5k/225 storage probe, not managed-PG. Test 2 shows the inversion: at storage parity, the picture flips. The current default tier (10k/500) sits between the two points - much closer to Test 2's behaviour than Test 1's.

Init phase: loading datatop

pgbench -i -s N loads the test tables. It's not part of the TPS run, but it gives a baseline sense of disk and CPU throughput under sequential pressure.

Init results

DatabasescaleSizeAWS m6iScamp T1 (5k/225)Scamp T2 (12k/500)
bench100100~1.5 GB12.69 s20.57 s14.97 s
bench10001000~15 GB157.74 s241.85 s175.61 s
bench1000010000~150 GB1561.67 s (26m1s)2280.92 s (38m1s)1479.85 s (24m40s)
Surprise observation. On the bench10000 init, ServersCamp Test 2 beats AWS m6i by 5%. This is genuinely unexpected: Intel ARK gives 8375C a 4.0 GHz max turbo vs 6132's 3.7 GHz, so PK build (single-threaded sort over ~1B rows) should favour AWS on raw CPU. Most likely it's not CPU at all - the postgres process on RDS runs niced (visible in OS metrics later in the article), which de-prioritises it under contention, and the EBS write path adds overhead on long sequential PK builds. Reproduce-it-yourself caveat: this is one observation, not a robust finding.

Excerpts from init logs (bench10000)

AWS RDS

done in 1561.67 s (drop tables 0.00 s, create tables 0.01 s,
  client-side generate 876.29 s, vacuum 1.59 s, primary keys 683.78 s).
real    26m1.711s

ServersCamp T1 (5k IOPS)

done in 2280.92 s (drop tables 0.00 s, create tables 0.02 s,
  client-side generate 1337.61 s, vacuum 5.95 s, primary keys 937.32 s).
real    38m1.080s

ServersCamp T2 (12k IOPS)

done in 1479.85 s (drop tables 0.00 s, create tables 0.01 s,
  client-side generate 941.12 s, vacuum 3.33 s, primary keys 535.40 s).
real    24m39.970s

Storage bottleneck, demonstrated

Disk Throughput readings from the ServersCamp panel during PK build:

ConfigurationReadWriteLimit utilization
ServersCamp Test 1 (limit 225 MB/s)224.6 MB/s30.2 MB/s99.8%
ServersCamp Test 2 (limit 500 MB/s)498.3 MB/s64.8 MB/s99.7%

In both cases, the disk hits its ceiling - at 225 MB/s on Test 1, at 500 MB/s on Test 2. So Test 1's storage QoS really is the bottleneck, and ServersCamp's managed-PG is capable of consuming the same 500 MB/s as AWS gp3.

pgbench runs: TPS and latencytop

bench100 (~1.5 GB, in-cache, CPU-bound)

Database fits in shared_buffers + page cache entirely. We're measuring CPU and locks. Storage shouldn't matter.

What this measures in practice. This is not a typical production scenario - real databases rarely fit entirely in 8 GB of RAM. What we're effectively isolating here is the gap between two CPU generations (Ice Lake vs Skylake), not anything about how the database is managed or how storage is provisioned. AWS's lead is silicon, not architecture. Useful as a control measurement, but if you're choosing a managed-PG provider for a real workload, this row tells you almost nothing about what you'll actually get.

MetricAWS m6iScamp T1 (5k/225)Scamp T2 (12k/500)
Run 1 TPS3,976.443,713.253,744.75
Run 2 TPS3,967.673,729.283,665.94
Run 3 TPS4,020.643,782.453,758.20
Median TPS3,976.443,729.283,744.75
Latency avg (Run 2)8.065 ms8.581 ms8.729 ms
Variance (across runs)±0.7%±0.9%±1.2%
Δ TPS vs AWS--6.2%-5.8%
Winner: AWS m6i (+5.8 to 6.2%). On in-cache CPU-bound work, Ice Lake wins on IPC and clock (4.0 GHz vs 3.7 GHz max turbo per Intel ARK, plus generational IPC gains). Storage QoS doesn't matter: T1 and T2 deliver indistinguishable TPS (3729 vs 3744 - within noise).

The raw pgbench output below is included for transparency - the table summarises it, but if you want to verify the numbers yourself, here it is. The two lines that actually matter are tps = ... (transactions per second, the headline number) and latency average = ... (mean per-transaction latency). Everything else describes the run setup. Full run.txt for every run of every scenario, plus the per-interval aggregate logs, lives in the repo: github.com/serverscamp/pgbench-aws-vs-serverscamp.

pgbench summary, AWS Run 2

pgbench (18.3)
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 100
query mode: prepared
number of clients: 32
number of threads: 4
duration: 600 s
number of transactions actually processed: 2380108
number of failed transactions: 0 (0.000%)
latency average = 8.065 ms
initial connection time = 143.238 ms
tps = 3967.670593 (without initial connection time)

ServersCamp Test 2 Run 1

pgbench (18.3 (Ubuntu 18.3-1.pgdg24.04+1))
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 100
query mode: prepared
number of clients: 32
number of threads: 4
duration: 600 s
number of transactions actually processed: 2246325
number of failed transactions: 0 (0.000%)
latency average = 8.545 ms
initial connection time = 183.938 ms
tps = 3744.748138 (without initial connection time)

bench1000 (~15 GB, partial cache)

Database ~15 GB on ~8 GB available cache. Some pages get read from disk - storage starts to matter.

What this measures in practice. This is the regime smaller production systems actually live in: DB a few times bigger than RAM, hot pages cached, cold pages on disk. Both CPU and storage influence the result, which makes this the noisiest scenario - run-to-run variance jumps to 5-8% as cache warmth and autovacuum timing shift between runs. Useful as a transition view between "all in cache" and "all on disk", but for buying decisions the bench10000 row below carries more weight.

MetricAWS m6iScamp T1 (5k/225)Scamp T2 (12k/500)
Run 1 TPS2,577.802,060.842,683.57
Run 2 TPS2,761.722,265.012,817.91
Run 3 TPS2,883.512,447.142,884.13
Median TPS2,761.722,265.012,817.91
Latency avg (Run 2)11.587 ms14.128 ms11.356 ms
Variance (across runs)±5.6%±8.4%±3.6%
Settle between runs2-18 s40-47 s4-20 s
Δ TPS vs AWS--18.0%+2.0%
Winner: ServersCamp Test 2 (+2.0%). At storage parity, ServersCamp pulls ahead of AWS even on the mixed cache+disk workload. Test 1 (5k/225) trails by 18% - clean evidence that 225 MB/s is a real bottleneck, not the database itself.

Especially telling: Run 3 (with cache fully warm):

  • AWS Run 3: 2,883.51 TPS
  • ServersCamp T2 Run 3: 2,884.13 TPS

Difference: 0.02% - statistical noise. With a warm cache the two providers run identically.

Raw pgbench output for that warm-cache run, side by side. Compare the tps and latency average lines - the rest is metadata.

AWS Run 3

scaling factor: 1000
duration: 600 s
number of transactions actually processed: 1729727
latency average = 11.098 ms
tps = 2883.514321 (without initial connection time)

ServersCamp Test 2 Run 3

scaling factor: 1000
duration: 600 s
number of transactions actually processed: 1730230
latency average = 11.095 ms
tps = 2884.131092 (without initial connection time)

bench10000 (~150 GB, disk-bound)

Database ~150 GB on 8 GB RAM - most of it cold. Every transaction takes a page miss and goes to disk. The scenario closest to a real production workload.

What this measures in practice. This is what production looks like for any database past the small-side: tens or hundreds of GB on a host with 8-16 GB RAM, hot set bigger than cache, page miss on most transactions. Storage characteristics and managed-PG overhead dominate the result - the CPU advantage AWS had on bench100 gets neutralised because the workload spends most of its time waiting for disk anyway. If you're picking between providers, this row is the one to weight most heavily. It's also where the stability difference (CoV, latency tails) shows up clearly.

MetricAWS m6iScamp T1 (5k/225)Scamp T2 (12k/500)
Run 1 TPS2,196.371,896.812,409.97
Run 2 TPS2,275.581,956.722,481.38
Run 3 TPS2,339.691,983.082,561.34
Median TPS2,275.581,956.722,481.38
Latency avg (Run 2)14.062 ms16.354 ms12.896 ms
Variance (across runs)±3.1%±2.2%±3.0%
Settle between runs19-20 s41-53 s19-23 s
Δ TPS vs AWS--14.0%+9.0%
Winner: ServersCamp Test 2 (+9.0%). On the real disk-bound scenario, ServersCamp pulls ahead of AWS by nearly 10%. The run-by-run gap is stable (+9.7% / +9.0% / +9.5%) - not a coincidence.

Raw pgbench output, mid-series run on each side. Look at tps: ServersCamp processed ~265,000 more transactions in the same 1200-second window, with lower average latency. Same workload, same client, same connection settings - the difference is on the database side.

AWS Run 3

scaling factor: 10000
duration: 1200 s
number of transactions actually processed: 2807282
latency average = 13.677 ms
tps = 2339.689575 (without initial connection time)

ServersCamp Test 2 Run 3

scaling factor: 10000
duration: 1200 s
number of transactions actually processed: 3073180
latency average = 12.493 ms
tps = 2561.339411 (without initial connection time)

Stability and latency tails: the real findingtop

Median TPS is only part of the picture. Reading the per-interval logs (TPS/latency every 10 s via --aggregate-interval=10) revealed a qualitative difference between the providers that the summary numbers hide.

Within-run TPS variation, CoV (lower = more stable) 0% 5% 10% 15% 20% 25% CoV TPS, % bench100 avg run 1-3 6.5% 5.1% 5.2% bench1000 avg run 1-3 13.9% 15.4% 4.8% bench10000 avg run 1-3 21.0% 11.5% 4.2% AWS RDS m6i ServersCamp T1 (5k IOPS) ServersCamp T2 (12k IOPS)
CoV TPS = stdev of TPS across 10-second windows ÷ mean TPS, in %. Smaller bars = flatter performance over the run. On disk-bound: AWS swings ±21%, ServersCamp T2 stays around ±4%. Trend is consistent across three runs, but a categorical claim about tails would need 10+ runs at different times of day.

1. Within-run stability (CoV TPS)

Coefficient of Variation = stdev(TPS_per_interval) / mean(TPS) × 100%. Smaller = more uniform performance during the run.

ScenarioAWS m6iScamp T1 (5k/225)Scamp T2 (12k/500)
bench100 run1/2/35.5 / 6.9 / 7.0%5.3 / 5.8 / 4.2%5.3 / 5.1 / 5.1%
bench1000 run1/2/316.3 / 11.9 / 13.5%15.4 / 15.9 / 14.8%4.4 / 5.8 / 4.3%
bench10000 run1/2/323.3 / 20.1 / 19.7%12.3 / 11.2 / 11.1%3.8 / 4.4 / 4.4%
On disk-bound bench10000, across three runs the trend is unmistakable: ServersCamp T2's CoV TPS is markedly lower than AWS (~4% vs ~20%). At similar means, AWS shows large dips and peaks; ServersCamp holds a flat line. This is a strong directional signal in our sample, but a categorical claim would need 10+ runs at different times of day to clear multi-tenant noise.

2. TPS dips - minimum over a 10-second window

ScenarioMean TPSAWS minScamp T2 min
bench1000 run1~25761505 (-42%)2444 (-5%)
bench10000 run1~2300828 (-63%)2190 (-5%)
bench10000 run2~2300718 (-68%)2129 (-14%)
bench10000 run3~23001006 (-57%)2188 (-15%)

Across three bench10000 runs, AWS dropped to 700-1000 TPS in individual 10-second windows (nearly -70% from the mean). Without direct EBS/RDS telemetry we can't pin the mechanism - candidates: checkpoint stalls flushing dirty pages, multi-tenant noisy neighbours on EBS, periodic internal RDS-agent activity. ServersCamp T2 didn't show comparable dips in the same runs - TPS stayed in a narrow band.

Note on gp3: unlike gp2, gp3 has no burst credits - it's a steady provisioned baseline by spec. So calling these dips "gp3 burst exhaustion" would be technically wrong. Whatever's causing them is on the AWS side, but without EBS CloudWatch metrics we won't claim more than that.

3. Latency tails: p95 of average latency over 10-second windows

Important note on the metric. This is not p95 latency over individual transactions. This is p95 of "average latency in a 10-second window" across 60-120 windows per run. With --aggregate-interval=10, the per-transaction distribution wasn't recorded. For real per-transaction p99/p999 you'd need a separate run with --log and no aggregation.

ScenarioAWS p95 latencyScamp T2 p95 latencyAWS max single-txScamp T2 max single-tx
bench1000 run314.50 ms11.86 ms293 ms225 ms
bench10000 run126.12 ms14.08 ms518 ms243 ms
bench10000 run220.48 ms14.13 ms522 ms201 ms
bench10000 run321.43 ms13.53 ms508 ms267 ms
On bench10000, ServersCamp T2 has p95 latency roughly half of AWS (13.5 vs 26 ms), and max single-transaction latency twice as low (200-267 vs 508-522 ms). For production with a p99 latency SLA, that matters more than mean TPS.
p95 latency on bench10000 (lower is better) p95 of average latency over 10-second windows, not per-transaction p95 0 5 10 15 20 25 30 latency, ms Run 1 26.12 14.08 Run 2 20.48 14.13 Run 3 21.43 13.53 AWS RDS m6i ServersCamp T2 (12k IOPS)
p95 of average latency over 10-second windows in each of the three bench10000 runs. AWS tail stays at 20-26 ms, ServersCamp T2 at 13-14 ms. T1 omitted - on bench10000 it's storage-bound, which we already covered.

4. bench10000 summary (real-world, disk-bound)

AWS m6iScamp T2
Avg latency14.4 - 15.6 ms12.5 - 13.3 ms
p95 latency20.5 - 26.1 ms13.5 - 14.1 ms
Max single-tx508 - 522 ms200 - 267 ms
TPS dips (min vs mean)down to -68%down to -15%
CoV TPS20-23%3.8-4.4%

ServersCamp T2 isn't just 9% faster on the median - it's qualitatively more stable on disk-bound work. Vendors don't usually publish numbers like these because they spoil the sleekness of marketing-grade benchmarks.

Sidenote: top and Performance Insightstop

You can skip this section - it's loose observations from the server side without strong interpretation. Including it because the numbers are interesting; if anyone has a sharper read on the mechanism, we'd love to hear it.

Top waits on AWS RDS during bench1000/bench10000 (Performance Insights)

LWLock:WALWrite           5.82  ████████████████
IO:DataFileRead           3.61  ██████████
IO:WalSync                0.70  ██
Client:ClientRead         0.57  █
CPU                       0.54  █
Lock:transactionid        0.33  █
Timeout:VacuumDelay       0.04
IPC:ProcarrayGroupUpdate  0.03
IO:DataFileWrite          0.03
Lock:tuple                0.02

OS CPU breakdown under the same workload (bench10000)

AWS RDS

%Cpu(s):  7.2 us, 22.8 sy, 48.6 ni, 9.2 id, 10.6 wa,  0.0 hi,  1.6 si,  0.0 st

ServersCamp

%Cpu(s): 51.8 us, 32.3 sy,  0.0 ni,  0.7 id,  2.5 wa,  0.0 hi, 12.7 si,  0.0 st

What jumped out:

  • On AWS, user CPU ~7%, nice ~49%. On ServersCamp, user ~52%, nice 0%.
  • iowait: AWS ~10.6%, ServersCamp ~2.5%.

Adding user+nice, total CPU usage looks comparable on both sides (~52-55%). But how the kernel scheduler accounts for that work in each case - we won't try to interpret. If anyone with deeper knowledge of either provider's managed internals can explain, we'd take the comment.

Summary tabletop

TPS gap: ServersCamp Test 2 vs AWS m6i across scenarios

ScenarioAWS m6i TPSScamp T2 TPSΔLead
bench100 (CPU-bound, in-cache)3,976.443,744.75-5.8%AWS
bench1000 (mixed)2,761.722,817.91+2.0%ServersCamp T2
bench10000 (disk-bound)2,275.582,481.38+9.0%ServersCamp T2

Storage QoS contribution (T1 → T2)

ScenarioT1 (5k/225)T2 (12k/500)Lift from storage upgrade
bench1003,729.283,744.75+0.4%
bench10002,265.012,817.91+24.4%
bench100001,956.722,481.38+26.8%

On bench100 the storage upgrade does nothing (DB in cache). On bench1000/10000 the lift is 24-27% - direct effect of higher IOPS/throughput.

Cost at this configurationtop

Same hardware spec on both sides (2 vCPU / 8 GB / 500 GiB), same EU region, on-demand pricing, no commitments. Excludes backup storage, data transfer, and support tier on both sides.

AWS prices are billed in USD; converted to EUR using the ECB reference rate of 1 USD = €0.92 as of 2026-05-05 for like-for-like comparison. Original USD figures shown in parentheses.

AWS RDS for PostgreSQL db.m6i.large

Line itemMonthly
DB instance (db.m6i.large, 2 vCPU / 8 GiB)€142.38 ($154.76)
Storage (500 GiB gp3, 12k IOPS / 500 MB/s baseline included)€63.02 ($68.50)
Total€205.40 ($223.26)

ServersCamp managed PostgreSQL db-m-8g

Line itemMonthly
DB instance (db-m-8g, 2 vCPU / 8 GB)€36.36
Storage (500 GB at €0.15/GB)€75.00
Total€111.36

AWS at €205.40 is roughly 1.85× more expensive than ServersCamp at €111.36 for the same configuration on-demand.

Price per TPS (disk-bound, the production-realistic scenario)

Dividing monthly cost by median TPS on bench10000 - the scenario closest to a real production workload:

TPSMonthly costCost per TPS / month
AWS RDS m6i2,276€205.40~€0.090
ServersCamp T2 (12k IOPS)2,481€111.36~€0.045

ServersCamp delivers ~2× more transactions per euro at the most production-relevant scenario. On the upcoming price update we mention in the roadmap, that gap widens further.

The honest caveat: AWS Savings Plans / Reserved Instances

AWS pricing can come down if you commit money up front. Approximate Reserved Instance pricing for db.m6i.large in EU regions, converted at the same ECB rate:

  • 1-year, No Upfront: roughly €89/mo instance (~37% off, ~$97)
  • 1-year, All Upfront: roughly €76/mo instance (~46% off, ~$83)
  • 3-year, All Upfront: roughly €57/mo instance (~60% off, ~$62)

Add the same €63/mo of storage and the cheapest plan (3-year all-upfront) gets AWS RDS down to ~€120/mo total. That's still ~8% more than ServersCamp's on-demand price (€111.36), and you've committed 36 months of spend up front.

Why we benchmark on-demand

The whole point of cloud is "scale up when you grow, scale down when you don't." Reserved Instances and Savings Plans break that promise - in exchange for a discount, you commit to a fixed amount of compute for 12 or 36 months.

We approached this benchmark from the pay-as-you-grow perspective because that's the model most cloud customers actually live in - startups, small SaaS teams, indie devs, and frankly the majority of teams whose next quarter's load isn't already locked in. For that reality, fixed-commitment pricing is the wrong shape:

  • You don't know if next month brings 200 users or 2,000. A 1-year RI assumes you do.
  • If traffic doubles tomorrow, on-demand absorbs it. A reserved instance sized for last year's load doesn't.
  • If traffic dies, on-demand stops billing the moment you stop the instance. An all-upfront RI is already paid.
  • "Save 60% with 3 years upfront" is a discount that applies to teams who already know exactly how much they'll need for 36 months. Most don't.

So the comparison that's relevant to our reader is on-demand vs on-demand. That's the €111.36 vs €205.40 above. RIs are an option AWS gives you, not a price you'd pay if you came in cold today.

Conclusionstop

What we proved

  1. Managed PG as a product is equivalent across both providers. At identical vCPU/RAM/storage QoS, both deliver close or identical TPS on the same workload. Run 3 of bench1000 (TPS 2883.51 AWS vs 2884.13 ServersCamp T2) - 0.02% difference, perfect parity.
  2. On CPU-bound workload, AWS m6i wins thanks to the newer CPU. Ice Lake 8375C edges Skylake 6132 by ~6% TPS on bench100 - within the theoretical IPC + clock advantage.
  3. On disk-bound workload, ServersCamp T2 wins thanks to less managed-PG overhead. On bench10000, at storage parity ServersCamp leads by 9% TPS and cuts p95 latency roughly in half. Likely contributors: nice-priority cost on RDS, Nitro+EBS overhead, WAL contention.
  4. The stability trend on disk-bound favours ServersCamp T2. Across three runs, CoV TPS ~4% vs ~23% for AWS, and we didn't see the kind of large TPS dips on T2 that AWS produced (min -15% from mean vs -68% on AWS). It's a directional signal in our sample - for a categorical claim, 10+ runs at different times of day are needed.
  5. Storage QoS is the dominant lever for disk-bound work. When we ran a deliberately constrained probe (Test 1, 5k IOPS / 225 MB/s, well below the current 10k/500 default), TPS dropped 14-18% on bench1000/bench10000. Same managed-PG, same CPU, same RAM - just half the IOPS and half the throughput. Doubling them back inverts the result. This is the underlying reason AWS's "free 12k/500 baseline at 400+ GiB" is such a load-bearing detail of the comparison.
  6. On the price-performance dimension, the gap is wider than the raw TPS gap. On-demand, same hardware: €111.36 (ServersCamp) vs €205.40 (AWS). Cost per TPS at disk-bound: ~€0.045 vs ~€0.090. ServersCamp delivers ~2× more transactions per euro, no commitment needed to access that price.

The pattern

The more disk-bound the workload, the more AWS's CPU advantage gets neutralised, and the more its managed-PG overhead shows up. Linear relationship:

Disk weight in workloadΔ ServersCamp T2 vs AWS
0% (in-cache, bench100)-5.8% (AWS ahead)
~50% (mixed, bench1000)+2.0% (ServersCamp ahead)
~95% (disk-bound, bench10000)+9.0% (ServersCamp ahead)

Practical takeaway

If your DB fits in RAM - AWS m6i is ~6% faster on CPU. You pay roughly 2× the price for that ~6%. Most teams won't make that trade.

If your DB is bigger than RAM (typical production) - ServersCamp at storage parity delivers +9% TPS, half the p95 latency, ~5× lower TPS variance, and ~half the bill. That's not a marginal advantage on any single axis - it's a stack of them.

Storage QoS matters - more than CPU or memory for disk-bound work. ServersCamp's current default tier (10,000 IOPS / 500 MB/s) sits ~17% below AWS gp3's free 12k/500 baseline on IOPS, identical on throughput. In practice that lands much closer to Test 2's behaviour than Test 1's. For very large databases or read-heavy production you may want a higher tier - but the math still ends up cheaper than RDS at parity.

A note on AWS sizing - this matters. Our 150 GB database sits on a 500 GiB RDS volume on purpose. AWS gp3 starts at only 3,000 IOPS / 125 MB/s baseline; the 12,000 IOPS / 500 MB/s tier we benchmarked against kicks in for free only at 400+ GiB volumes. We sized up so the comparison would land against AWS at its free best. If you provision a smaller disk (say 200 GiB) and don't pay extra for provisioned IOPS on top, your disk-bound TPS on RDS will be substantially worse than what we measured here - and the gap to ServersCamp gets bigger, not smaller.

Bottom line

Under real production load - database bigger than RAM, hot pages on disk - ServersCamp is faster, dramatically more stable on tails, and roughly half the price.

The full scorecard, on-demand pricing, no commitments either side:

Dimension (vs AWS RDS m6i, same hardware)ServersCamp T2
TPS on disk-bound (production-realistic)+9.0%
p95 latency-48% (~half)
TPS variance (CoV)~5x lower (4% vs 23%)
Worst TPS dip in a run-15% vs -68%
Monthly price on-demand-46% (€111 vs €205)
Cost per TPS (disk-bound)~½ (€0.045 vs €0.090)
TPS on small in-memory DBs-5.8% (newer AWS CPU)

The one column AWS wins is the one most production workloads don't even hit - small DB, fits entirely in RAM, CPU-bound. Everywhere else, ServersCamp is faster, cheaper, more predictable on tails, and you can leave next month with no penalty.

If the goal is performance per euro without locking in spend - this isn't a hard call.

On the roadmaptop

  • The same pgbench across the other major managed Postgres providers - DigitalOcean, Render, Supabase, Neon, Aiven, Crunchy Bridge. One workload, identical settings, the same disclaimer template. The point isn't to crown a winner, it's to give you a like-for-like grid you can read across.
  • Price update on our managed PostgreSQL. We've already done the deeper price-per-TPS and price-per-p95 numbers on the back of this benchmark. The honest takeaway: our PostgreSQL pricing has room to come down. Expect an adjustment before the next benchmark drops - if we ask people to compare on numbers, we don't get to hide behind soft pricing.

What's next

  • Try ServersCamp - spin up a managed Postgres on the free tier and run pgbench yourself
  • Migrating from AWS RDS? - see the migration page; we'll handle the move for you
  • Next benchmark: same workload against DigitalOcean, Render, Supabase, Neon, Aiven, and the other major managed Postgres providers

Raw data, including per-run run.txt summaries, per-interval aggregate logs, and the run scripts, is published on GitHub: github.com/serverscamp/pgbench-aws-vs-serverscamp. The test is reproducible; if you can't reproduce it on your account, that's a writeup bug - tell us via a ticket.