Conceptual editorial illustration
AI-generated editorial illustration. The measurements and experiment results are presented separately below.

A small VPS can report very little free RAM while still estimating plenty of memory available for new applications. It can also have occupied swap without actively moving pages at that moment. These numbers answer different questions.

Start here: inspect available memory, changes in swap counters, and memory stall time during the work you care about. Then compare those observations with application errors and response times. One screen of memory totals cannot decide whether to upgrade a server.

Our original 4 GB server note reported a single snapshot. This follow-up shows how to collect a short sequence and interpret it. The download records one real sequence; it is a field observation, not a capacity benchmark.

Four signals that should not be treated as one

SignalWhat it answersWhat it cannot settle
MemFreeHow much RAM is currently unused?How much cache the kernel can reclaim.
MemAvailableHow much RAM does the kernel estimate is available to start applications without swapping?A new workload's peak demand or its container limit.
Swap usedHow much swap space is occupied now?Whether swap is active during this interval.
Swap counter changes and memory PSIAre pages moving through swap, and are tasks spending time stalled on memory?Which service caused it or whether users experienced unacceptable latency.

The kernel's available-memory estimate accounts for reclaimable memory and reserves. It is more useful for this question than unused RAM alone, but it is still an estimate. A container can also reach its own memory ceiling while the host has available RAM. Host totals do not override that ceiling.

Collect a minute you can inspect

First choose a period: before a normal scheduled job, during it, or after it. Note that context privately. We did not deliberately start a job or stress workload for the sample below, so we cannot attribute its changes to any particular service.

Download and inspect the read-only Python collector. On a Linux host with Python 3.9 or later, run it from a directory where you can create output files:

python3 memory_sample.py --out ./memory-check --samples 13 --interval 5

The command takes about a minute and creates a new memory-sample-... subdirectory containing samples.csv, samples.json and summary.json. Each run uses a new directory. It reads aggregate fields from /proc/meminfo, /proc/vmstat and, when available, /proc/pressure/memory. It does not collect process names, hostnames, IP addresses, environment variables or credentials. There is no network upload.

Reading these fields normally does not need root; local restrictions may differ. The collector does not change swap, clear caches, restart a service or generate memory load. Run it in the host context for host measurements. Inside a container, the visible /proc values need not describe the container's own allowance; this script does not read cgroup limits or attribute pressure to a container.

What our 60-second sample actually showed

We ran the collector directly on our shared Linux VPS on November 2, 2026, from 00:05:50 to 00:06:50 UTC (08:05:50–08:06:50 in Shanghai). It recorded 13 observations and 12 intervals over 60.013 seconds. The operating system reported 3,910.06 MiB of total memory and a 4,096-byte page size.

MeasureObserved range
Unused RAM190.57–276.91 MiB
Available RAM1,488.58–1,541.91 MiB
Occupied swap992.17–1,000.42 MiB
Swap-in rate, per sampled interval0–1.814 MiB/s
Swap-out rate, per sampled interval0–2.505 MiB/s
Memory PSI some, per sampled interval0–5.145% of elapsed time
Memory PSI full, per sampled interval0–3.165% of elapsed time

These are independent minimum–maximum ranges; their endpoints need not occur together. The rates average each roughly five-second interval. They are not instantaneous peaks, network or disk throughput benchmarks, or the kernel's avg10 PSI values.

Download the actual samples (CSV) or the unrounded summary (JSON). These contain aggregate counters, without private service configuration.

Unused RAM was much lower than available RAM. At the same time, the interval counters show that swap activity and memory stalls did occur. The useful conclusion is that an available-memory total and memory pressure can coexist in a short observation. It would be incorrect to describe this as a completely idle, pressure-free machine.

We did not identify the responsible workload, measure request latency or observe a full daily cycle. These figures do not establish sustained thrashing, an application failure, a safe number of extra containers or a need to buy more RAM. They identify periods worth investigating alongside the actual workload.

Read the exported rates correctly

pswpin and pswpout are cumulative swap page counters. The collector subtracts successive readings, converts pages using the system's page size, and divides by the actual monotonic interval:

swap MiB/s = counter change × page size / 1,048,576 / elapsed seconds
memory stall % = PSI total change in microseconds / elapsed seconds / 10,000

For memory PSI, some means at least some tasks were stalled on memory; full means all non-idle tasks were stalled simultaneously. A nonzero value reports time lost to that condition, not the percentage of physical RAM used.

The first observation has no preceding interval, so its rates are null in JSON and empty in CSV. Missing PSI or an unavailable memory field stays unknown. A counter that moves backwards also produces an unknown rate. None of those cases is reported as a measured zero.

Decide what to check next

  • Low free RAM with available memory: do not clear caches to improve the display. Repeat during the workload and examine the other signals.
  • Occupied swap with unchanged counters and no observed stalls: occupancy alone does not establish current pressure. A quiet interval also cannot rule out trouble outside the window.
  • Swap activity or stalls that recur with slow jobs: compare timestamps with your own latency, errors and overlapping jobs before changing settings. Our collector does not perform that application check.
  • A container fails while host available RAM looks comfortable: inspect that container's memory limit and recorded failure reason. A configured limit, a usage sample and a host total are different quantities.

If you use docker stats --no-stream as an additional sample, note its time and units separately. Docker's Linux CLI memory display subtracts cache from its usage figure; it is not interchangeable with a raw host reading. Do not sum one set of values and present it as proof of spare peak capacity.

Use the VPS memory budget calculator after collecting representative measurements. Its arithmetic helps you write down allowances. It cannot discover peaks, infer service demand from a name, or turn this minute's maximum into a safe deployment budget.

Sources, scope and reproducibility

Limit: one host, one minute, no induced workload, no per-service attribution, latency test, OOM recovery test or long-term capacity result. The collector's parser, missing values, counter resets, page-size conversion and elapsed-time calculations have separate synthetic checks; those checks do not validate Linux performance or production readiness.