Failing Experiments Are Useful
Today I tried to push my togo-lab.io setup (see 2nd block at my landing page for all services) a bit further than strictly necessary. So maybe you noticed some outages.
Why I did this? Not only for production security reasons, but also out of curiosity: How far can I go with limited resources? I want to understand where the real limits for my setup are.
In this experiment, I wanted to see whether a single small VPS could reasonably host Nextcloud, a Matrix server, and Gitea, and still handle antivirus scanning for file uploads. At first glance it looked tight, but maybe possible. In practice, it pushed this small VPS over the edge.
That outcome was useful for my learning and understanding. When things fail or become unstable, I usually learn much more than when everything works smoothly. As a technician, breaking things on purpose for learning is, for me, typically the fastest way to understand them.
The following section is a post-mortem of that experiment: what it revealed, what I learned from it, and what would be required if I want to add antivirus scanning in the future.
Context:
This server hosts multiple self-managed services on a small VPS (1 GB RAM, 1 vCPU):
- Nextcloud (primary collaboration platform)
- Matrix server
- Gitea
- Supporting stack (PHP-FPM, MariaDB, Redis, Apache/Nginx, Fail2Ban)
The goal was to add antivirus scanning for uploaded files in Nextcloud, as preparation for future collaborative use.
Initial Goal
Enable server-side antivirus scanning for Nextcloud uploads using ClamAV, with the following constraints:
- Lightweight
- Automated
- No interactive maintenance
- Suitable for a self-hosted environment
This is a reasonable baseline requirement once multiple external contributors are involved.
Attempted Approaches
1. ClamAV Daemon (clamd) + Nextcloud (Socket Mode)
What was tried
-
Installed ClamAV daemon
-
Tuned
clamd.confaggressively (single thread, reduced parsers, size limits) -
Added strict systemd memory limits
-
Disabled background scans
-
Socket-based integration with Nextcloud
-
**changes to the default
/etc/clamav/clamd.conf# === VPS-safe limits === MaxThreads 1 ConcurrentDatabaseReload no # File size limits (Nextcloud uploads) MaxFileSize 50M MaxScanSize 75M StreamMaxLength 75M # Archive / recursion limits MaxRecursion 10 MaxFiles 5000 # Timeouts ReadTimeout 120 CommandReadTimeout 120 # Disable low-value / memory-heavy scanners ScanHTML false ScanMail false ScanSWF false ScanHWP3 false ScanXMLDOCS false # Reduce bytecode impact Bytecode true BytecodeTimeout 20000 # Reduce RAM further (Nextcloud upload use-case) PhishingSignatures false PhishingScanURLs false DisableCache true ExtendedDetectionInfo false -
best I got, via
free -h
| total | used | free | shared | buff/cache | available | |
|---|---|---|---|---|---|---|
| Mem: | 960Mi | 926Mi | 68Mi | 31Mi | 101Mi | 33Mi |
| Swap: | 1.0Gi | 843Mi | 180Mi | – | – | – |
$ swapon --show
NAME TYPE SIZE USED PRIO
/swapfile file 1024M 848.5M -2`
Observed behavior
clamdresident memory usage: ~500–600 MB- Heavy swap usage even after tuning
- Periodic stalls, SSH lag, partial service unresponsiveness
- OOM kills during database reload or startup
Conclusion Even heavily tuned, resident ClamAV is not viable on a 1 GB VPS that already runs multiple services.
2. ClamAV Executable Mode (clamscan on upload)
What was tried
- Disabled
clamdentirely - Used Nextcloud Antivirus for Files app in Executable mode
- Scanning only on upload
- Strict size limits
- No background scans
FYI Final authoritative configuration in NextCloud App “Antivirus for Files”
- Mode: ClamAV
Executable - Path to clamscan:
/usr/bin/clamscan - Extra command line options (comma-separated):
--no-summary,--infected,--max-filesize=50M,--max-scansize=75M - Stream Length:
104857600 - Block uploads when scanner is not reachable:
Yes - Block unscannable files:
No - Background scans: effectively
off(unchecked)
Observed behavior
- Technically functional
- No permanent memory footprint
- However:
- Uploads caused noticeable CPU + IO spikes
- PHP-FPM workers stalled under load
- Combined service activity still led to instability
Conclusion Even non-resident scanning adds too much peak load for this VPS when combined with:
- Nextcloud
- Matrix
- Gitea
- Database and cache services
Final Decision
Antivirus disabled (for now)
The Nextcloud Antivirus app is currently disabled.
Reasons:
- System stability has higher priority than partial security measures
- Trusted users only
- Strict file permissions
- Regular backups
- No public upload endpoints
After disabling AV and rebooting:
- System became stable
- Swap usage normalized
- All services responsive:
- Nextcloud
- Matrix
- Gitea
Post-Mortem Summary
| Item | Result |
|---|---|
| Configuration error | ❌ No |
| ClamAV bug | ❌ No |
| Nextcloud bug | ❌ No |
| VPS resource limit | ✅ Yes |
| Wrong architecture | ✅ Yes (for this size) |
Meaning:
- This was not a misconfiguration.
- It was a capacity mismatch.
Lessons Learned
-
1 GB VPS is already at the limit for:
- Nextcloud
- Matrix
- Gitea
combined.
-
Antivirus scanning is loadwise not “free”, even in executable mode.
-
Security features that trigger CPU + IO spikes must be sized for worst-case concurrency, not idle averages.
-
Adding AV without increasing resources creates negative security by destabilizing the system.
When Antivirus Will Be Re-Enabled
Antivirus scanning will be mandatory once this instance is used for real group collaboration.
That will require one of the following options:
Option A — VPS Upgrade (actual preferred)
- Upgrade to ≥ 2 GB RAM
- Re-enable ClamAV (daemon or executable mode)
- Keep all services on one host
Option B — Service Split
- VPS 1: Nextcloud
- VPS 2: Matrix + Gitea
- Antivirus enabled only on Nextcloud host
Current Security Posture (Interim)
- If, than only trusted users
- No public upload endpoints
- Strict permissions
- Fail2Ban + firewall
- Frequent backups
- Fast restore tested
This is acceptable temporarily, but not a final state.
Closing Notes
This experiment was intentional and valuable, learned a lot, also during configuration and tuning.
It clarified:
- the real resource cost of antivirus scanning
- the practical limits of small VPS setups
- and the architectural decisions required for future growth
When collaboration expands, the infrastructure will be expanded accordingly. So clamav and configuration stays, but Nextcloud App is disabled for now, but not uninstalled.
