I Got Hacked! My Server Started Mining Monero One Morning
A developer discovers their Hetzner server was silently mining Monero for 10 days after an attacker exploited a critical Next.js vulnerability in the Umami analytics container, and shares the lessons learned about container security.
One morning I woke up to an abuse notification from Hetzner. My server was reportedly scanning networks. I had 4 hours to respond. What followed was a crash course in server security that I want to share with everyone who runs their own infrastructure.

The Discovery
I SSH'd into my server and immediately checked the system load. What I saw made my stomach drop:
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM
12345 1001 20 0 2.5g 1.2g 4096 S 819.3 15.2 javaeCPU consumption of 819% from a process named javae, located at /tmp/.XIN-unix/. That's not Java — that's a crypto miner trying to disguise itself.
Digging deeper, I found multiple xmrig cryptocurrency mining processes consuming all available resources. And according to the process timestamps, mining had been going on since December 7th — a full 10 days of unauthorized activity on my server.
How Did They Get In?
I run several services in Docker containers on this server. Among them was Umami — a privacy-focused analytics tool. Umami is built on Next.js, and Next.js had a critical vulnerability: CVE-2025-66478 in React Server Components deserialization.
The exploit chain worked like this:
- The attacker sends a malicious HTTP request to the Next.js endpoint
- React Server Components (RSC) deserializes the hostile payload
- Remote code execution is achieved inside the container
- The attacker downloads and installs crypto miners
- Monero is mined continuously, 24/7
What I Found Inside
The malware was hiding inside the Umami container at a very creative path:
/app/node_modules/next/dist/server/lib/xmrig-6.24.0/Nestled right inside the Next.js node_modules directory — where no one would think to look during a casual inspection.
The xmrig process was running with these parameters:
./xmrig --url auto.c3pool.org:443 --user [wallet_address] --pass [encoded_pass] --donate-level 0 --tlsThe --donate-level 0 flag was a nice touch — even the hacker didn't want to donate to the xmrig developers.
Why It Wasn't Worse
Here's the silver lining: Docker container isolation actually saved me. The damage was contained because:
- The container ran as a non-root user (
nextjs, UID 1001) — the attacker couldn't escalate privileges - No privileged access was granted — the container couldn't access host devices or kernel features
- No volumes were mounted to the host system — the attacker couldn't read or write host files
- Process isolation prevented direct host filesystem access
The suspicious /tmp/.XIN-unix/javae path that appeared in the host's process listing didn't actually exist on the host — it was merely visible because Docker containers share the host kernel. The process was entirely confined within the container.
Cleanup
Remediation was straightforward once I understood the scope:
- Stopped and removed the infected Umami container
- Enabled UFW firewall, blocking everything except SSH, HTTP, and HTTPS
- CPU load immediately normalized
The entire issue was resolved within 2 hours of discovery.
Lessons Learned
1. Your dependencies are your attack surface
"I don't use Next.js" was my initial thought. But I did use Umami, which uses Next.js. Your security is only as strong as your weakest dependency, and dependencies have dependencies of their own.
2. Container security configuration matters
Proper Docker configuration — non-root users, no mounted volumes, no privileged access — prevented what could have been a total server compromise. If that container had run as root with mounted volumes, I would have been looking at a complete infrastructure rebuild.
3. Layered defense is essential
A firewall, monitoring, and timely updates are all critical. Any single layer can fail, but multiple layers dramatically reduce risk.
4. Monitor your resources
819% CPU usage went unnoticed for 10 days. That's embarrassing, but it's a common story. If I had basic CPU monitoring with alerts, I would have caught this on day one.
What I Changed
After this incident, I implemented several changes:
- Firewall from day one — UFW is now enabled before any services are deployed
- fail2ban for SSH — brute force protection
- CPU and load monitoring with alerts — never again will a miner run undetected for 10 days
- Key-only SSH authentication — password authentication disabled entirely
- Immediate security patches — CVE announcements are now part of my monitoring pipeline
The takeaway is simple: security isn't something you add later. It's something you build in from the start. I got lucky — container isolation saved me from a much worse outcome. Not everyone will be so fortunate.