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.

Server hacked

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  javae

CPU 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:

  1. The attacker sends a malicious HTTP request to the Next.js endpoint
  2. React Server Components (RSC) deserializes the hostile payload
  3. Remote code execution is achieved inside the container
  4. The attacker downloads and installs crypto miners
  5. 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 --tls

The --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:

  1. Stopped and removed the infected Umami container
  2. Enabled UFW firewall, blocking everything except SSH, HTTP, and HTTPS
  3. 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.