> ## Content Index
> Fetch the complete content index at: https://blog.diengdoh.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Mail + Web on One Server: 4 GB or 8 GB of RAM? I Measured It
- URL: https://blog.diengdoh.com/mail-server-ram-budget-4gb-vs-8gb/
- Published: 2026-09-24T10:00:00.000Z
- Updated: 2026-10-11T10:16:27.000Z
- Description: Real RSS numbers from a panel box with mail and web: ClamAV eats over a gigabyte. Why summing ps lies, how to measure with smem, and when 4 GB is enough.
- Author: Lukas
- Tags: Linux, Security

I had to size a server for a small setup: a WordPress site, a Nextcloud, a forum, and mail for three mailboxes, all on one box with a hosting panel. The question was 4 GB or 8 GB of RAM. Instead of guessing, I measured an existing panel server that already runs mail + web, and the answer came down to one daemon: **ClamAV**.

**TL;DR:** On the measured box, `clamd` alone sits at about **1.1 GB** resident. MariaDB ~~680 MB, Rspamd \~430 MB, fail2ban \~330 MB. My estimate for the target workload: \*\*~~3.5 GB with ClamAV\*\* — too tight for 4 GB — and **\~2.5 GB without** — fine on 4 GB with 2 GB of swap. Want headroom or keep ClamAV: take 8 GB. Want the cheaper box: 4 GB and switch ClamAV off; Rspamd still does the spam filtering.

## The measurement

Quick and dirty first: resident memory per process name, summed.

```bash
ps -eo rss,comm --no-headers \
  | awk '{m[$2]+=$1} END {for (p in m) printf "%8.0f MB  %s\n", m[p]/1024, p}' \
  | sort -rn | head -15

```

On the panel box (Apache + PHP-FPM, MariaDB, Postfix/Dovecot, Rspamd, ClamAV, fail2ban — the standard stack from [KeyHelp](/keyhelp-free-plesk-alternative/)) the top of the list looked like this, rounded:

| Process                        | RSS      |
| ------------------------------ | -------- |
| apache2 (all processes summed) | \~4.2 GB |
| clamd                          | \~1.1 GB |
| mariadbd                       | \~680 MB |
| rspamd                         | \~430 MB |
| fail2ban-server                | \~330 MB |

And then you look at `free -m` and notice that the box doesn't *have* enough RAM for the Apache number to be true.

## Why summing RSS lies

RSS (resident set size) is all physical memory a process currently has mapped — **including shared pages**. Apache's prefork/worker children are forked from one parent and share most of their memory: the binary, the libraries, the modules, copy-on-write pages from the parent. Each child reports all of that in its own RSS.

So with many Apache processes each showing tens of MB, the sum counts the same shared pages over and over. That 4.2 GB is mostly the same memory counted many times. It's the classic way to convince yourself you need a bigger server.

Single-process daemons like `clamd` and `mariadbd` don't have this problem — one process, its RSS is roughly its real cost. That's why those numbers in the table are trustworthy and the Apache one isn't.

## Measure PSS instead

**PSS** (proportional set size) divides each shared page by the number of processes sharing it. Sum PSS across processes and you get a number that actually adds up. `smem` does that:

```bash
apt install smem

# per process, sorted by PSS, totals at the bottom
smem -t -k -s pss -r -c "pid name pss" | head -25

# just Apache
smem -t -k -P apache2

# overall view: user processes, total used
smem -w -k

```

Use the PSS column for budgeting. For Apache specifically, also remember that a lot of the real work happens in PHP-FPM pools, which are separate processes — measure those too:

```bash
smem -t -k -P php-fpm

```

## The rest of the toolbox

Total picture, including cache:

```bash
free -m

```

Look at **available**, not **free**. Linux uses idle RAM as page cache, which is good, and gives it back when needed. A box with 200 MB "free" and 3 GB "available" is perfectly healthy.

Per service, which is often more useful than per process because systemd groups everything a service spawns:

```bash
systemd-cgtop -m          # interactive, sorted by memory
systemd-cgtop -m -n 1     # one snapshot, scriptable

```

That gives you `clamav-daemon.service`, `mariadb.service`, `rspamd.service` etc. as single lines, and cgroup memory accounting doesn't have the shared-page double counting problem in the same way.

Swap usage per process, if the box is already under pressure:

```bash
smem -k -c "name swap pss" -s swap -r | head

```

And note what the hardware actually gives you: a box sold as 8 GB showed about 7.6 GB total in `free`. Budget against the real number.

## Building the estimate

The target workload: WordPress, Nextcloud, a forum, mail for three mailboxes. Light traffic, a small team.

My rough budget, starting from the measured base stack:

| Component                                         | Estimate             |
| ------------------------------------------------- | -------------------- |
| clamd                                             | \~1.1 GB (measured)  |
| MariaDB                                           | \~0.7 GB (measured)  |
| Rspamd                                            | \~0.4 GB (measured)  |
| fail2ban                                          | \~0.3 GB (measured)  |
| Apache + PHP-FPM for three apps, real (PSS) usage | the bulk of the rest |
| Postfix, Dovecot, panel, OS, small stuff          | a few hundred MB     |
| **Total with ClamAV**                             | **\~3.5 GB**         |
| **Total without ClamAV**                          | **\~2.5 GB**         |

That's an estimate, not a benchmark. The PHP part swings the most: Nextcloud with background jobs and a forum during a busy hour behave very differently from an idle WordPress. But the measured daemons are fixed costs you pay whether anyone visits the site or not.

### With ClamAV: 4 GB is too tight

\~3.5 GB on a box with roughly 3.8 GB usable leaves essentially nothing for page cache, PHP spikes or a MariaDB that decides to grow its buffers. It'll run — until a backup, an update and a traffic peak overlap, and the OOM killer picks something. Often enough it picks the biggest single process, which on this box is `clamd` or `mariadbd`. Neither is a fun thing to lose silently.

### Without ClamAV: 4 GB + swap is fine

\~2.5 GB leaves real headroom on 4 GB. Add 2 GB of swap as a safety net, not as working memory:

```bash
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
sysctl vm.swappiness=10
echo 'vm.swappiness=10' > /etc/sysctl.d/99-swappiness.conf

```

With low swappiness the kernel only pushes rarely used pages out, and a short spike doesn't turn into an OOM kill.

## Is switching off ClamAV a security problem?

Less than you'd think, for this use case. What ClamAV does on a mail server: scan attachments for known malware signatures. What it doesn't do: filter spam. Spam scoring, SPF/DKIM/DMARC checks, greylisting, reputation — that's **Rspamd**, and Rspamd keeps running.

For three mailboxes that are read in modern clients, with users who don't run random `.exe` attachments, ClamAV is a relatively expensive layer for its benefit. On a box with dozens of mailboxes, or clients that forward everything to a Windows desktop fleet, I'd keep it and buy the RAM.

If you turn it off, do it properly: use the panel's switch if there is one, rather than just stopping the service. Then check that Rspamd's antivirus integration doesn't treat an unreachable `clamd` as an error that affects delivery — send yourself a test mail with an attachment and read the log.

```bash
systemctl status clamav-daemon clamav-freshclam
journalctl -u rspamd --since "10 min ago" | grep -i -E "clam|antivirus"

```

And don't forget `freshclam`: with `clamd` gone, the signature updater has no reason to keep running either.

## The money side

Cloud and VPS prices went up in 2026, so the difference between a 4 GB and an 8 GB plan matters more than it used to. Two things I'd weigh beyond the monthly price:

- **Contract term.** Many providers are noticeably cheaper with 12-month terms or prepayment than with monthly billing. That's good if you're sure about the size — and bad if you guessed wrong, because you're stuck with it.
- **Resizing.** Check whether you can upgrade a plan in place later. If you can upgrade but not downgrade, start small. If you're locked into a term, size for the term, not for today.

My decision rule after measuring: **8 GB** if you want ClamAV or headroom for growth without thinking about it. **4 GB** if the workload is known and small, ClamAV goes, and you add swap and monitoring. Either way, measure the real box a week after go-live with `smem` and `systemd-cgtop` — with [Netdata](/netdata-monitoring-ubuntu/) or similar you'll see trends instead of snapshots.

And if a box ever does run out of something, it's usually not RAM first anyway — [disk full forensics](/linux-disk-full-forensics/) is the post I end up needing more often. Also worth keeping in mind: fail2ban at \~330 MB is not free either; if you're tight on memory and curious about lighter options, there's [CrowdSec](/crowdsec-modern-fail2ban-alternative/), though I haven't measured it against fail2ban on this box.

---

### Related posts

- [Postfix SNI: "map lookup problem" and TLS Alert 80 — the texthash Fix](https://blog.diengdoh.com/postfix-sni-hash-map-lookup-problem/)
- [Plesk License Expired: What Still Works and How I Got Around It](https://blog.diengdoh.com/plesk-expired-license-what-still-works/)
- [My Server Ran Out and My Backups Were Truncated: What I Learned](https://blog.diengdoh.com/server-provider-outage-backup-lessons/)