Eight self-hosted stacks stayed up for seven days on one 2 GB VPS in our 3–10 November 2026 test. Available memory fell from a day-one median of 296 MiB to 259 MiB, with most of the fall in the first four days. This was light use with empty media libraries, and Home Assistant authentication failed for most of the run.
No recorded memory kills or restarts. All ten containers were up at every one of 9,923 samples.
Headroom mattered more than gradual growth. n8n briefly reached 471 MiB, about 109 MiB above its weekly median.
Measure after setup. The first-hour available-memory median was 308 MiB; an earlier untouched-install test reported 425 MiB.
The limits are substantial. One server, one week, empty libraries and mostly unauthenticated Home Assistant requests do not establish what a busy stack needs.
We left eight self-hosted stacks on one 2 GB server for a week to see how memory changed after setup. A small load generator ran every five minutes. The result was a modest fall in available memory, followed by a much flatter final three days.
What we ran
The same eight stacks as in What Actually Runs on a 2 GB VPS, on the same plan: 1 vCPU, 2 GB of memory, no swap file.
- Vaultwarden
- Navidrome
- Audiobookshelf
- Forgejo
- Jellyfin
- Uptime Kuma, with three monitors
- Home Assistant with Mosquitto
- n8n with PostgreSQL
That is ten containers. Every app had an account created and its first-run setup finished, which the first post did not do.
A small script generated light activity. Every five minutes it attempted about 18 requests across the apps: listing a library, loading a dashboard, calling a webhook. Home Assistant authentication failed after about 30 minutes, so those later requests were refusals rather than signed-in activity. Every half hour it also committed a file to a Forgejo repository. Over the week it made 36,848 requests.
Setup changes the starting point
In the first post the eight stacks sat idle and untouched, and the server had 425 MiB available. Here, with accounts created and the script running, the median for the first hour was 308 MiB.
That is an approximate 117 MiB gap between two different runs, not an isolated measurement of setup cost. The earlier idle test was on 22 October; this run started on 3 November, with setup and light activity completed before measurement. App versions and workload can also affect the comparison. Within this week, available memory fell by 49 MiB between the first-hour and last-hour medians.
What happened over seven days
| Day | Available (median) | Lowest that day | Used (median) | Disk cache (median) |
|---|---|---|---|---|
| 1 | 296 MiB | 157 MiB | 1,672 MiB | 489 MiB |
| 2 | 280 MiB | 231 MiB | 1,688 MiB | 477 MiB |
| 3 | 269 MiB | 210 MiB | 1,699 MiB | 468 MiB |
| 4 | 263 MiB | 226 MiB | 1,705 MiB | 464 MiB |
| 5 | 262 MiB | 220 MiB | 1,706 MiB | 464 MiB |
| 6 | 258 MiB | 207 MiB | 1,710 MiB | 463 MiB |
| 7 | 259 MiB | 215 MiB | 1,709 MiB | 465 MiB |
The fall is front-loaded. Available memory dropped 33 MiB between day one and day four, and 4 MiB between day four and day seven.
The lowest point of the whole week, 157 MiB, came 38 minutes after the start and lasted a single sample. The containers' measured anonymous memory was almost unchanged: 1,074 MiB versus 1,075 MiB in the preceding sample. We did not trace the cause of the low available-memory reading or measure its duration between samples. Available memory never went that low again.
Which apps grew
Each container's anonymous memory, read from its control group, using the first-hour and last-hour medians. This excludes file-backed cache and is not the container's entire memory footprint:
| Container | First hour | Last hour | Change | Peak in the week |
|---|---|---|---|---|
| n8n | 357 MiB | 362 MiB | +5 | 471 MiB |
| Home Assistant | 316 MiB | 319 MiB | +3 | 319 MiB |
| Uptime Kuma | 121 MiB | 128 MiB | +7 | 132 MiB |
| Forgejo | 101 MiB | 102 MiB | +1 | 104 MiB |
| Jellyfin | 97 MiB | 97 MiB | 0 | 108 MiB |
| Audiobookshelf | 45 MiB | 53 MiB | +8 | 54 MiB |
| PostgreSQL (for n8n) | 21 MiB | 11 MiB | -10 | 21 MiB |
| Navidrome | 10 MiB | 12 MiB | +2 | 13 MiB |
| Vaultwarden | 6 MiB | 8 MiB | +2 | 11 MiB |
| Mosquitto | 1 MiB | 1 MiB | 0 | 1 MiB |
The sum of the table's container medians rose from 1,075 to 1,093 MiB, an 18 MiB increase. Summing each sample first and then taking the hourly median gives 1,077 to 1,093 MiB, a 16 MiB increase. These are different summaries; neither measures all memory used by the apps. No container's anonymous-memory median grew by more than 8 MiB. The largest measured spike was n8n's: it jumped to 471 MiB for one sample at the end of day one, about 110 MiB above its normal level, and was back to normal a minute later. Two days later, at the same time of day, it reached 443 MiB. The two spikes came almost exactly 24 and 72 hours after n8n started, which is consistent with periodic activity. We did not trace its cause. A sample a minute can miss shorter spikes, so there may have been more.
On a server with 260 MiB to spare, a spike that size is nearly half the headroom. Available memory read 186 MiB in that sample.
The cost you don't see in free memory
The absence of recorded kills and restarts did not mean there was no memory pressure.
The sampler reported about 1,700 MiB used and about 465 MiB in buffers/cache late in the run. Linux memory fields overlap in how they account for reclaimable memory, so these are not slices to add together. The host also recorded major page faults: page faults requiring data to be read from storage. With no swap, these are consistent with file-backed pages being read, but the counter does not identify the files or prove which app caused the reads. Daily counts rose through day six and eased on day seven:
| Day | Major page faults |
|---|---|
| 1 | 316,000 |
| 2 | 394,000 |
| 3 | 521,000 |
| 4 | 579,000 |
| 5 | 662,000 |
| 6 | 812,000 |
| 7 | 741,000 |
The kernel also reports how long programs were stalled waiting for memory. Over the week that came to 326 seconds, which is 0.05% of the time. The worst minute averaged 2.7%.
Successful-request response times also changed slightly; the run does not isolate memory pressure as the cause. Counting successful requests only, the median response time moved from 18 ms on day one to 23 ms on day seven, and the 95th percentile from 420 ms to 436 ms. One request in the week took longer than two seconds, and it was one of the refused Home Assistant calls.
What this means for a 2 GB server
- A week of uptime did not break it. That describes this workload and these versions. It does not predict whether another stack will develop a leak.
- Measure after setup, not before. An untouched install understates what the app needs once it has an account and data.
- Leave room for spikes. Size for the peak of your largest app, not its average.
- Consider swap as a buffer. We ran without it. Swap may help absorb some spikes at the cost of storage latency; this run did not test that trade-off.
- Allow more room for real work. This test omitted media, scans, transcoding and substantial user activity. A larger plan or fewer apps provides more headroom, but needs measuring with your workload.
What we did not measure
- Home Assistant was mostly idle. Authentication stopped working about half an hour in: 4,341 of our 4,354 requests to it were refused. Only 13 succeeded. It stayed running, but the run did not sustain the signed-in workload we intended.
- Empty libraries. Jellyfin, Navidrome and Audiobookshelf held no media. No transcoding and no library scans ran.
- One light user. No bursts, no large uploads, no heavy workflows.
- One week, one server. A slower leak would need a longer run to show.
- Historical images. The measurements describe the images pulled on 3 November 2026. Newer app versions can behave differently.
How we measured
Choose room for the work you will actually run
The test used a PrivateByte Flare VPS with 1 vCPU and 2 GB RAM. That is a measured starting point for this light workload. If you will add media, background scans or heavier workflows, choose more memory or fewer stacks, then measure again after setup.
Common questions
Do self-hosted apps leak memory over time? We observed little growth in container anonymous memory in this run: the median of the per-sample total rose by 16 MiB between the first and last hours. This does not establish whether an app leaks memory under other versions, heavier use or a longer run.
How much free memory should a 2 GB VPS have? Enough to absorb your largest app's spike. Our server ran with about 260 MiB available, and one app briefly took 110 MiB more than usual. That is an observation from this test, not a universal minimum; larger workloads can need much more spare memory.
Do I need swap on a 2 GB VPS? We ran without it and nothing was killed. Swap may absorb some spikes, but it can add latency and cannot guarantee an app will avoid being killed. We did not measure performance with swap in this run.
Why did available memory fall if the apps barely grew? Host available memory and container anonymous memory measure different things. The first-hour to last-hour available-memory medians fell by 49 MiB, while the median of the summed container anonymous memory rose by 16 MiB. File-backed memory, the kernel and other host processes are outside that container metric. We did not break down or trace the remainder.
Can I run all eight apps on 2 GB? All ten containers were running at every sample with no recorded out-of-memory kills or restarts. That result applies to empty libraries and light activity, with Home Assistant mostly unauthenticated. A 4 GB plan provides more memory, but populated libraries and heavier workflows still need their own measurements.
