PostgreSQL on DigitalOcean vs ServersCamp. Droplets, volumes and a managed database

Consumer benchmark with vanilla PostgreSQL 18.6 on DigitalOcean FRA1 and ServersCamp. September 2026.

We keep testing our storage with PostgreSQL, and this time we compared it with DigitalOcean. It is the cloud a lot of small teams start on, and the first place many of them put a PostgreSQL database. So we took the droplet people buy for that job, gave it the same database we give ours, and ran the same pgbench battery on both.

On the DigitalOcean side that is a Basic Regular droplet with 4 vCPU and 8 GB for $48 a month. On ours it is a bs-l with the same 4 vCPU and 8 GB and a 160 GB W2 disk, our entry database class, for EUR 39.97 a month including the IPv4 address. Both are the budget line of their cloud and the size you pick when the database stops being a side project. At current exchange rates the two monthly checks are a few dollars apart.

We ran two scenarios, because DigitalOcean gives you two ways to host that database.

The first is a plain instance. You rent a server and install PostgreSQL yourself. On DigitalOcean the data can live on the droplet's own disk or on a Volume, their network block storage, so we tested both. On our side the data lives on W2 in both cases.

The second is managed. DigitalOcean sells Managed PostgreSQL, and we put its 4 vCPU / 8 GB node against what you can build on ServersCamp by hand, one VM with the database and one VM with the application, talking over our private network. We do not sell managed PostgreSQL yet. Quietly, it is already in internal testing, and until it ships the fair comparison is the setup a customer can build today.

ScenarioDigitalOceanServersCamp
1a. Instance, local diskDroplet s-4vcpu-8gb with 160 GB local SSD, $48bs-l with 160 GB W2, EUR 39.97
1b. Instance, network diskSame droplet plus a 160 GB Volume, $64bs-l with 160 GB W2, EUR 39.97
2. Managed vs by handManaged PostgreSQL 4 vCPU / 8 GB, 160 GiB, no standby, $126.40Database VM bs-l with W2, EUR 39.97, application VM next to it

The contenderstop

 DigitalOceanServersCamp
InstanceBasic Regular s-4vcpu-8gb, 4 vCPU / 8 GB, shared CPUbs-l Sustained, 4 vCPU / 8 GB
CPUIntel, model hidden by the provider (Broadwell family, 2016)Intel Xeon Skylake (2017)
Local disk160 GB SSDno
Network diskVolume 160 GB, $16, replicatedW2 160 GB, EUR 15.47, replicated across nodes, power-loss protected, 25k IOPS and 500 MiB/s
Managed database4 vCPU / 8 GB node, 160 GiB SSD, $126.40, one standby adds $126.40not sold yet
RegionFRA1eu-east-ro-1
OS / PostgreSQLUbuntu 26.04, PostgreSQL 18.6 from the Ubuntu archive, same pgtune formula on every server

Methodtop

PostgreSQL 18.6 from the Ubuntu 26.04 archive, the same pgtune formula on every server (shared buffers at a quarter of RAM, work memory and parallel workers derived from the box itself). fsync and synchronous_commit stayed on everywhere, including the managed database, where we had to turn synchronous_commit back on. Each database size was loaded with pgbench -i using server-side generation, then measured with three 120 second TPC-B write runs and three 120 second select-only read runs at 32 connections and 4 threads. The ladder was scale 100, 1000 and 6500, which is 1.5, 15 and 95 GB against 8 GB of RAM. In scenario 1 pgbench ran on the database server. In scenario 2 it ran on a separate VM in the same private network, the way an application talks to its database. Every run is reported as it came out. The commands are at the bottom of the page.

Scenario 1. A plain instancetop

Writes

TPC-B, three UPDATEs, one SELECT and one INSERT per transaction, 32 connections.

DatabaseDO local diskDO VolumeServersCamp W2
1.5 GB2 477 tps (12.3 to 13.6 ms)2 254 tps (13 to 16 ms)6 289 tps (5.1 ms)
15 GB2 427 tps (12.6 to 13.5 ms)1 154 tps (27 to 29 ms)4 892 tps (6.5 ms)
95 GB1 950 tps (14.9 to 17.8 ms)824 tps (32 to 47 ms)4 377 tps (7.2 to 7.4 ms)
TPC-B writes, tps at 32 connections (higher is better) 0 2000 4000 6000 1.5 GB 2477 2254 6289 15 GB 2427 1154 4892 95 GB 1950 824 4377 DO droplet, local disk DO droplet, Volume ServersCamp bs-l, W2
The local disk keeps the droplet's write rate flat as the database grows, the Volume halves it past 15 GB. W2 stays about twice as fast as the local disk at every size.

The worst 10 seconds of each run show the same. At 15 GB the Volume dropped to 484 tps, the local disk to 1 985 and W2 to 4 608. At 95 GB the numbers were 455, 1 478 and 4 180.

The local disk doubles DigitalOcean's write rate compared with its Volume on the larger databases, and the droplet is still about half as fast as W2. On the small database both DigitalOcean disks land in the same place, because the database fits in RAM and the CPU sets the pace. Ours got two and a half times more transactions out of the same four cores, with no steal.

Reads

Select-only point lookups, 32 connections.

DatabaseDO local diskDO VolumeServersCamp W2
1.5 GB26 429 tps (1.2 ms)25 642 tps (1.2 to 1.4 ms)50 980 tps (0.6 ms)
15 GB13 385 tps (2.3 to 2.5 ms)2 752 tps (9.7 to 15.7 ms)17 891 tps (1.7 to 1.9 ms)
95 GB10 655 tps (2.8 to 3.3 ms)2 107 tps (13 to 17 ms)13 521 tps (2.4 ms)

Here the droplet's local disk helps. It reads the 95 GB database five times faster than the Volume, and the gap to W2 shrinks to 1.3x. W2 is capped at 25k IOPS and the 95 GB read runs used about 22k of them, so a larger class of ours would read faster, at a higher price. On the Volume the droplet's CPU spent more than half its time waiting for the disk during large reads (iowait 52 to 60%).

A single commit

One client inserting one row per transaction for 60 seconds. Every commit waits until the WAL is on durable media, so this is the time your application waits on every write.

 DO local diskDO VolumeServersCamp W2
Commits per second5335733 185
Latency per commit1.87 ms1.74 ms0.31 ms
pg_test_fsync, fdatasync of 8 kB1 529 µs1 977 µs249 µs

The local disk commits no faster than the Volume. A synchronous write costs about 1.5 ms on DigitalOcean whichever disk you choose, and money does not buy it lower, since neither disk has performance tiers. W2 confirms a write in a quarter of a millisecond after it has landed on independent storage nodes.

Loading and restoring the database

95 GB databaseDO local diskDO VolumeServersCamp W2
Initial load (pgbench -i)41 min 20 s1 h 6 min27 min 11 s
pg_dump -Fd -j 411 min 34 s13 min 1 s6 min 31 s
pg_restore -j 439 min 13 s47 min 59 s21 min 57 s
Dump plus restore50 min 47 s1 h 1 min28 min 28 s

Both the dump and the restore of one large table are mostly single-threaded work, one core compressing or one core running COPY and building the primary key, so the faster core decides the result and the local disk barely helps.

Scenario 2. Managed PostgreSQL vs a database you build by handtop

Here pgbench runs on a separate VM and reaches the database over the private network, which is how an application sees it. On DigitalOcean the client was a droplet in the same VPC as the managed cluster, connected with the VPC hostname from their control panel on the direct port. On ServersCamp the client was a second bs-l on a different host in the same private network. Both sides used TLS 1.3.

This scenario compares the SQL speed an application gets. It does not compare the services. DigitalOcean Managed PostgreSQL includes daily backups, point-in-time recovery, minor version updates and node replacement, and with a database you build by hand those are your job. Prices in this article are the price of the database server alone. The client VM is left out on both sides.

DatabaseDO Managed PostgreSQLServersCamp, by hand
1.5 GB load34.6 s18.2 s
1.5 GB writes1 215 tps (24.7 to 28.1 ms)5 340 tps (5.9 to 6.1 ms)
1.5 GB reads9 538 tps (2.9 to 3.7 ms)37 848 tps (0.84 ms)
15 GB load9 min 30 s4 min 38 s
15 GB writes858 tps (34.7 to 38.9 ms)4 377 tps (7.3 ms)
15 GB reads4 904 tps (6.3 to 6.8 ms)18 527 tps (1.7 to 1.8 ms)
95 GB load1 h 5 min28 min 46 s
95 GB writes684 tps (40.9 to 53.4 ms)3 914 tps (8.1 to 8.3 ms)
95 GB reads5 038 tps (5.5 to 7.3 ms)13 468 tps (2.4 ms)
TPC-B writes over the network, tps at 32 connections (higher is better) 0 2000 4000 6000 1.5 GB 1215 5340 15 GB 858 4377 95 GB 684 3914 DO Managed PostgreSQL, $126.40 ServersCamp by hand, EUR 39.97
Both sides over the private network with TLS. The managed database writes four to six times slower than a bs-l with W2 at every size.

The managed database was also the least steady system in the whole test. In one 15 GB write run it fell to 94 transactions per second for ten seconds, and its latency standard deviation ran higher than its average. On our side the worst 10 seconds of any write run stayed within 7% of the average.

DigitalOcean's own monitoring shows that the managed node was not fully busy. During the 1.5 GB runs its CPU stayed between 55 and 74%, while our database server had 3% idle. At 32 clients and about 3 ms per read, 32 connections can complete roughly 10 thousand reads per second, which is close to what we measured. That fits the network between the client and the managed node being the limit. We did not measure where inside their service the time went, so this is our reading of the numbers and not a measured cause.

Going over the network cost our setup 11 to 16% on writes and 26% on small reads compared with running pgbench on the database server itself. On large reads it came out even, since the database server no longer shared its CPU with the client.

synchronous_commit was off on our DigitalOcean Managed PostgreSQL clustertop

The cluster we created in FRA1 on 27 September 2026 answered SHOW synchronous_commit with off right after creation. We did not change it, and no setting on the database or the role set it. We checked one cluster, so treat this as what we saw there, and check your own.

pg_settings on our new cluster, FRA1, 27 September 2026
SELECT name, setting, source, boot_val, reset_val
  FROM pg_settings WHERE name = 'synchronous_commit';

 synchronous_commit | off | configuration file | on | off

boot_val is PostgreSQL's own default, which is on. On this cluster the off came from the configuration file. With off, the server tells the client that a transaction is committed before its WAL reaches the disk. If the node crashes or restarts in that window, transactions the application already considers saved are gone, and nothing reports an error. With the default wal_writer_delay of 200 ms the window is up to about 600 ms of writes.

We ran every managed test with ALTER DATABASE bench SET synchronous_commit = on, so both sides pay for durability. Then we measured what the default buys.

One client, one INSERT per transaction, over the networkCommits per secondLatency
DO Managed, synchronous_commit on4762.10 ms
DO Managed, synchronous_commit off (as the cluster came)1 5080.66 ms
ServersCamp, synchronous_commit on2 4610.41 ms

With off their single-client commits were three times faster, and a durable commit on ServersCamp was still faster than a non-durable one on DigitalOcean. If you run DigitalOcean Managed PostgreSQL, check this setting on your cluster. It can be changed per database with the statement above.

What the monitoring and fio showedtop

We watched both sides while the tests ran. On our VM the platform records CPU, disk throughput and IOPS every 2 seconds. On every machine vmstat wrote a line every 5 seconds. DigitalOcean shows its own graphs in the control panel.

ServersCamp VM, platform metrics per phase

W2 limits are 25k IOPS and 500 MiB/s. CPU is the share of all four vCPU.

PhaseCPU avg / p95Disk read MB/s, avg / p95Disk write MB/s, avg / p95IOPS read / writeLimited by
Load 1.5 GB11 / 27%096 / 4090 / 152writing
Writes 1.5 GB80 / 100%012 to 23 / 580 / 950CPU
Reads 1.5 GB90 / 100%050 / 23 to 79CPU
Load 15 GB27 / 52%87 / 499299 / 458239 / 238W2 bandwidth
Writes 15 GB81 / 100%46 to 51 / 8972 to 79 / 1645 000 / 5 400CPU, disk at 20 to 25% of IOPS
Reads 15 GB90 / 100%193 to 216 / 25512 to 8020 000 to 22 000 / 900 to 6 800CPU and 88% of W2 IOPS
Load 95 GB29 / 47%120 / 501296 / 408131 / 233W2 bandwidth
Writes 95 GB81 / 100%51 to 53 / 7296 to 105 / 1906 400 / 6 500CPU
Reads 95 GB90 / 100%175 to 178 / 20718 to 3322 000 / 1 200 to 3 000CPU and 88% of W2 IOPS
Single-client commits32 / 41%021 to 250 / 2 600 to 3 300one synchronous write at a time
pg_dump 95 GB55 / 65%210 / 22641 / 89115 / 2 129one core compressing

Writes stayed on the CPU at every size, with the disk at a quarter to a third of its limits. Large reads used 22k of the 25k IOPS, so this is the one place where a higher class would change the result.

vmstat per phase, scenario 1

Averages of user, system, iowait and steal in percent, over each run and the pause after it.

PhaseDO local diskDO VolumeServersCamp W2
Writes 1.5 GB37 to 40 / 21 to 22 / 3 to 4 / 5 to 731 to 39 / 19 to 21 / 3 to 4 / 6 to 1445 / 33 / 0 / 0
Reads 1.5 GB62 / 25 to 26 / 0 / 160 to 63 / 26 to 27 / 0 / 0 to 457 to 61 / 28 to 32 / 0 / 0
Writes 15 GB39 to 40 / 25 to 26 / 6 to 7 / 1 to 320 to 23 / 14 to 15 / 27 to 28 / 9 to 1340 / 33 to 34 / 4 / 0
Reads 15 GB45 to 47 / 37 to 38 / 4 to 5 / 1 to 211 to 17 / 11 to 14 / 52 to 56 / 6 to 933 to 35 / 36 to 37 / 18 to 19 / 0
Writes 95 GB32 to 37 / 23 to 25 / 9 to 13 / 3 to 614 to 19 / 10 to 13 / 30 to 39 / 12 to 1538 to 39 / 33 to 34 / 5 to 6 / 0
Reads 95 GB38 to 42 / 36 to 38 / 7 to 12 / 1 to 311 to 15 / 11 to 14 / 55 to 60 / 6 to 1128 / 31 / 30 / 0

Steal on the droplet reached 25% in single samples during write runs and 35% during Geekbench. On our VM it was zero in every sample of every test.

DigitalOcean control panel graphs

On the droplet with local disk, loading the 15 GB database peaked at 230 MB/s of writes and 175 MB/s of reads, while fio measured 1 348 MB/s of sequential writes on the same disk. During the 15 GB write runs the disk moved 40 to 55 MB/s and the CPU climbed to 80%. During the 1.5 GB read runs the CPU was at 88 to 100% with a load average of 30 on four vCPU.

On the managed cluster the CPU stayed between 55 and 74% through the 1.5 GB runs, with a load average of 12 to 19. The client droplet was 9 to 16% busy. On our side the database server sat at 3% idle during the same tests and the client used 11 to 28%.

fio

fio 3.41, 8 GiB file, direct IO, 30 seconds per test after 5 seconds of warm-up.

TestDO local diskDO VolumeServersCamp W2
4k random read, one at a time298 µs1 229 µs223 µs
4k random write, one at a time, p991 237 µs897 µs257 µs
4k write with fsync, one at a time350 per second217 per second2 920 per second
4k random read, 8 jobs x 64 deep49 221 IOPS4 869 IOPS24 986 IOPS (class limit)
4k random write, 8 jobs x 64 deep34 353 IOPS7 450 IOPS25 007 IOPS (class limit)
1 MB sequential read, 4 jobs3 507 MB/s420 MB/s501 MB/s (class limit)
1 MB sequential write, 4 jobs1 348 MB/s301 MB/s500 MB/s (class limit)

The droplet's local disk has more raw throughput than W2 and its large sequential streams are seven times faster. W2 stops at the limits of its class by design, and higher classes raise them. In the PostgreSQL tests above that throughput did not turn into speed, because the database waited on single synchronous writes and on the CPU.

Where the time goestop

  • CPU. The droplet's vCPU is shared, and steal showed up in most samples, up to 25% during write runs and 35% during Geekbench. Our vCPU showed zero steal in every phase of every test. On an in-memory database both servers were CPU-bound and ours did more work per second.
  • Synchronous writes. Every commit waits for one synchronous write. On DigitalOcean that write costs about 1.5 ms on the local disk and about 2 ms on the Volume. On W2 it costs 0.25 ms. With 32 clients the database batches commits and the difference shrinks, which is why the write gap is two to five times while the single-client gap is six.
  • Disk under load. The Volume is the slowest part of the DigitalOcean stack. At 15 and 95 GB it kept the droplet's CPU waiting more than half the time on reads. W2 held 22k of its 25k IOPS with the CPU still busy.
  • The network to the managed database. A TPC-B transaction is several round trips, and on the managed side each one cost about 0.66 ms against 0.2 to 0.5 ms on our private network. With 32 clients those round trips could be enough to cap the throughput on their side. We did not isolate it.

Per eurotop

ScenarioDigitalOceanServersCamp95 GB writes, DO vs ServersCamp
Instance, local disk$48EUR 39.971 950 vs 4 377 tps
Instance, Volume$64EUR 39.97824 vs 4 377 tps
Managed vs by hand$126.40EUR 39.97684 vs 3 914 tps

Prices are for the database server alone. Managed PostgreSQL also includes backups, point-in-time recovery, updates and node replacement, which are not in our column.

Summarytop

Averages of three runs per point. The multiplier is how many times ServersCamp is faster.

 Writes (TPC-B)Reads (select-only)Single-client commitDatabase server, per month
vs droplet on local diskx2.0 to x2.5x1.3 to x1.9x6.0EUR 39.97 vs $48
vs droplet on a Volumex2.8 to x5.3x2.0 to x6.5x5.6EUR 39.97 vs $64
vs Managed PostgreSQL, both over the networkx4.4 to x5.7x2.7 to x4.0x5.2EUR 39.97 vs $126.40
For the same money or less, our database was at least twice as fast on writes at every size and faster on reads, and a single commit took a fifth to a sixth of the time. The managed cluster we created had synchronous_commit = off, which means a confirmed transaction can be lost when the node fails. We turned it on for our measurements. The managed price also covers backups, point-in-time recovery and updates.

Conclusiontop

We are not writing a conclusion.

Reproduce ittop

Install PostgreSQL and apply the tuning on the database server.

server setup
apt-get install -y postgresql-18
RAM_MB=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo); CORES=$(nproc)
SB=$((RAM_MB/4)); EC=$((RAM_MB*3/4)); MW=$((RAM_MB/16)); [ $MW -gt 2048 ] && MW=2048
WM=$(( (RAM_MB-SB)*1024/(200*3) ))
cat > /etc/postgresql/18/main/conf.d/bench.conf <<EOF
max_connections = 200
shared_buffers = ${SB}MB
effective_cache_size = ${EC}MB
maintenance_work_mem = ${MW}MB
work_mem = ${WM}kB
wal_buffers = 16MB
min_wal_size = 2GB
max_wal_size = 16GB
checkpoint_completion_target = 0.9
random_page_cost = 1.1
effective_io_concurrency = 256
max_worker_processes = ${CORES}
max_parallel_workers = ${CORES}
max_parallel_workers_per_gather = $((CORES/2))
EOF
systemctl restart postgresql@18-main
sudo -u postgres createdb bench

Run the ladder. Add -h, -p and -U to every command when the database is on another server.

the ladder
for N in 100 1000 6500; do
  pgbench -i -I dtGvp -s $N bench
  psql -c "CHECKPOINT;" bench; sleep 30
  for r in 1 2 3; do pgbench -c 32 -j 4 -T 120 -P 10 bench; sleep 30; done
  for r in 1 2 3; do pgbench --select-only -c 32 -j 4 -T 120 -P 10 bench; sleep 15; done
done

Single-client commits and the fsync test.

commits
psql -c "CREATE TABLE fsync_t (id bigserial, v int);" bench
echo "INSERT INTO fsync_t (v) VALUES (1);" > /tmp/fsync_insert.sql
pgbench -n -c 1 -j 1 -T 60 -P 10 -f /tmp/fsync_insert.sql bench
/usr/lib/postgresql/18/bin/pg_test_fsync -f /path/on/the/data/disk/testfile -s 5

Dump and restore.

dump and restore
pg_dump -Fd -j 4 -f /path/on/the/data/disk/dump bench
dropdb bench && createdb bench
pg_restore -j 4 -d bench /path/on/the/data/disk/dump

On DigitalOcean Managed PostgreSQL, check the commit setting and turn it on for the test database.

managed database
psql "$MANAGED_URL" -c "SELECT name, setting, source, boot_val FROM pg_settings WHERE name = 'synchronous_commit';"
psql "$MANAGED_URL" -c "ALTER DATABASE bench SET synchronous_commit = on;"

If your numbers on a ServersCamp VM come out below what this page shows, that is a bug. Tell us in a ticket.

Appendix. CPUtop

Other numbers from the same machines. Geekbench 6 gave 623 vs 1 081 single core and 1 809 vs 3 353 multi core. sysbench CPU gave 780 vs 1 045 events per second on one thread and 2 710 vs 4 174 on four. Raw output of every run is available on request.