Volume postgres-volume-T1Wd bills ~600 MB but holds ~50 MB — zvol not reclaiming discarded blocks
tryreppd
HOBBYOP

a month ago

Project: reppd (a0b44bf6-d144-4a1d-b3e8-d2d5612a4e89) · Environment: production

Service: Postgres-Ovgz (listmonk companion DB) · Volume: postgres-volume-T1Wd

The reported/billed usage on this volume sits around 590–619 MB and slowly climbs,

but the actual on-disk footprint is ~50 MB. Confirmed from inside the container via railway ssh:

  • df -h /var/lib/postgresql/data → 50.6 MB used on /dev/zd9136
  • du -sh /var/lib/postgresql/data → 49.4 MB
  • pg_database_size → 9.8 MB
  • Mount: ext4 rw,relatime,discard,stripe=8192 (continuous discard already enabled)
  • Device supports discard: discard_max_bytes=268435456, discard_granularity=16384
  • fstrim -v → ioctl FITRIM: Operation not permitted (container lacks CAP_SYS_ADMIN)

So online discard is active and the device advertises TRIM support, yet the zvol's

allocated-block metric never comes back down — consistent with known ZFS zvol behavior

where discards aren't reclaimed at the host layer (cf. openzfs/zfs#7722). The write

source is listmonk's timer-refreshed dashboard matviews.

Ask: Could you run a host-side zpool trim / enable autotrim on the pool backing this

volume (or otherwise reconcile the metric)? I can't run batch fstrim from the container.

If that's not possible, please confirm and I'll dump/restore into a fresh volume instead.

Happy to keep the service running for you to inspect live.

Solved

5 Replies

Railway
BOT

a month ago

Volume billing tracks the ZFS zvol's allocated blocks, not the logical data size, so the ~648 MB metric is what you're billed on even though df shows ~50 MB. Continuous discard is mounted but the gap persisting like this usually means leaked file descriptors are holding deleted files open, preventing reclamation. A redeploy of the service releases those handles and lets the zvol reclaim the freed blocks, which is the self-serve step most likely to close that 648-to-50 MB gap. On the Postgres side, VACUUM FULL or pg_repack can further compact the on-disk files from ~50 MB toward your 9.8 MB of live data (both take a table lock). Your dump/restore-into-a-fresh-volume idea also works and resets the footprint entirely.


Status changed to Awaiting User Response Railway 30 days ago


tryreppd
HOBBYOP

a month ago

On the leaked file descriptor idea: wouldn't that show up as df much larger than du? Here they agree (df 50.6 MB, du 49.4 MB), so there don't seem to be deleted-but-open files pinning space. That points to the gap being at the zvol layer, not ext4. This is also an idle listmonk with cache_slow_queries off, so it's just baseline Postgres writes (WAL, checkpoints, autovacuum) that never get reclaimed.

On VACUUM FULL: on copy-on-write storage that rewrites into new blocks, so if the zvol isn't reclaiming freed blocks it would shrink df but leave the billed metric flat or higher. Is that right for your setup?

The two things I can't do myself:

  1. Can you run a host-side zpool trim or enable autotrim on the pool? Discard is already mounted and the device supports TRIM, but fstrim in the container fails with Operation not permitted (no CAP_SYS_ADMIN).
  2. Are there retained ZFS snapshots on this volume? A snapshot pinning old blocks would explain a gap that survives both a redeploy and a VACUUM.

Status changed to Awaiting Railway Response Railway 29 days ago


Railway
BOT

a month ago

To correct our earlier reply, the df/du agreement at ~50 MB rules out leaked file descriptors as the cause here, so a redeploy alone would not close the gap. The discrepancy is at the ZFS zvol layer, and we won't be running host-side trim or other manual reclamation commands on the backing pool. Volume billing is based on the zvol's allocated blocks, and the metric you see is what you're billed on. Your dump/restore into a fresh volume is the path that resets the footprint entirely and is the recommended approach here.


Status changed to Awaiting User Response Railway 29 days ago


tryreppd
HOBBYOP

a month ago

This is a reset though not a permanent fix. Is there any plans to run trim for you guys since we will continuously be increasingly charged. We'd have to move off of Railway for this use case if that's the case.


Status changed to Awaiting Railway Response Railway 29 days ago


Railway
BOT

a month ago

We don't have any changes to announce regarding how zvol block reclamation works on the backing pools. The dump/restore into a fresh volume is the current path to reset the footprint, and it would need to be repeated whenever the gap grows beyond what you want to pay for.


Status changed to Awaiting User Response Railway 29 days ago


Railway
BOT

22 days ago

This thread has been marked as solved automatically due to a lack of recent activity. Please re-open this thread or create a new one if you require further assistance. Thank you!

Status changed to Solved Railway 22 days ago


Welcome!

Sign in to your Railway account to join the conversation.

Loading...