

The most useful number you can get is from your own stack: put your actual app on the box and hit it with k6 or wrk using a realistic mix of pages, and watch memory rather than CPU. On 2 GB the failure mode is almost always RAM: too many PHP-FPM/worker processes plus a database on default buffers, then swap thrashing and the OOM killer, long before the single core is the limit.
What buys the most headroom on boxes this size:
- cap the worker count (pm.max_children or the equivalent) to what actually fits in RAM
- size the DB buffer pool deliberately instead of leaving defaults
- zram for bursts
- serve anything static or cacheable straight from nginx so it never touches the app
And rate-limit or block the obvious scrapers. As someone said above, bot traffic is often the real load.
If you’d rather keep Vikunja as the backend and web UI, it has a CalDAV endpoint, so you can point DAVx5 at it and use Tasks.org or jtx Board on Android for the reminders themselves. That gets you native Android notifications without email, and you keep the web interface for planning. Vikunja’s CalDAV side is less mature than its web UI, though, so test whether due dates and reminders come across the way you expect before moving everything over.
For notifications that aren’t tied to a task app (say, “remind me when X happens” from a script or Home Assistant), ntfy is the simplest self-hosted push to Android, as mentioned above.