This is a follow-up to #1473014. If you haven't read that first, start there.** The patch works. But applying it is only part of the DoS battle. After running it for a few days, my JAM instance on Umbrel kept crashing anyway with the offer locked and dead. Here below I list more details of what was continuing killing it, how has been diagnosed, and what you need to do beyond the patch to stay stable.
What Was Killing ItWhat Was Killing It
Three separate problems were compounding each other:
1. Swap was exhausted. The OOM (out of memory) killer was firing and sending SIGKILL to jmwalletd, the harshest possible termination. The process didn't crash or exit cleanly, it was murdered by the kernel. This showed up in Docker logs as:
dinit: Service jmwalletd terminated due to signal 9
dinit: Service jmwalletd restarting too quickly; stopping.
[STOPPD] jmwalletd
Signal 9 = SIGKILL = kernel ran out of memory and started executing processes. Check yours:
free -h
sudo dmesg | grep -i "oom\|killed\|out of memory" | tail -20
2. Log files were filling disk. The rate-limiting patch controls RAM and CPU, but it still generates log entries for every dropped connection. Under active attack those entries accumulate fast. A XXXMB log file in one session is not unusual. Without log rotation this eventually fills your disk and triggers another crash path.
3. Dead directory nodes in config. The joinmarket.cfg still had several defunct onion addresses that no longer exist. On every startup JAM was hammering these dead nodes with retry loops, wasting Tor circuits, generating log noise, and consuming resources on connections that would never succeed.
Fix 1: Diagnose and Recover SwapFix 1: Diagnose and Recover Swap
Umbrel uses a swap file on the data partition. After an OOM event it may be exhausted, misconfigured, or in some cases missing entirely. Check:
free -h
cat /proc/swaps
If swap shows 0 or is missing from /proc/swaps, find your swap partition:
lsblk
On Umbrel Home the data disk (sda1) is mounted at /swap, create your swap file there:
sudo fallocate -l 4G /swap/swapfile
sudo chmod 600 /swap/swapfile
sudo mkswap /swap/swapfile
sudo swapon /swap/swapfile
echo '/swap/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Verify:
free -h
# Should show Swap: 4.0Gi 0B 4.0Gi
Why /swap and not /? Umbrel's root filesystem is an overlay that doesn't support swap. The swapon command will fail with Invalid argument if you try to create the swap file on the wrong filesystem. /swap is on the real data disk.
Fix 2: Nuclear Log CleanupFix 2: Nuclear Log Cleanup
After a crash, old session logs pile up. Each time JAM restarts it generates a new nick and a new log file. Left alone these accumulate:
du -sh /home/umbrel/umbrel/app-data/jam/data/joinmarket/logs/*
You may find multiple files from previous sessions, some hundreds of MB. Truncate them all instantly, safe for running processes since it zeroes the file without deleting it:
sudo truncate -s 0 /home/umbrel/umbrel/app-data/jam/data/joinmarket/logs/*.log
Delete rotated archives:
sudo rm -f /home/umbrel/umbrel/app-data/jam/data/joinmarket/logs/*.log.1
sudo rm -f /home/umbrel/umbrel/app-data/jam/data/joinmarket/logs/*.gz
Then set up logrotate so it never gets this bad again:
sudo nano /etc/logrotate.d/jam
/home/umbrel/umbrel/app-data/jam/data/joinmarket/logs/*.log {
daily
rotate 1
nocompress
missingok
notifempty
copytruncate
}
rotate 1 keeps only one old file before deleting. nocompress skips gzipping, under active attack you don't need archives, you need small files. Force it to run immediately after setup:
sudo logrotate -f /etc/logrotate.d/jam
Fix 3: Update Directory NodesFix 3: Update Directory Nodes
The default Umbrel JAM config ships with old directory node addresses that no longer exist. Your maker wastes startup time and Tor circuits retrying them forever.
The current live nodes as of May 2026 (verify at joinmarket-ng.sgn.space):
| Name | Address |
| JMA | jmarketxf5wc4aldf3slm5u6726zsky52bqnfv6qyxe5hnafgly6yuyd.onion:5222 |
| COI | coinjointovy3eq5fjygdwpkbcdx63d7vd4g32mw7y553uj3kjjzkiqd.onion:5222 |
| NAK | nakamotourflxwjnjpnrk7yc2nhkf6r62ed4gdfxmmn5f4saw5q5qoyd.onion:5222 |
| SAT | satoshi2vcg5e2ept7tjkzlkpomkobqmgtsjzegg6wipnoajadissead.onion:5222 |
| JMR | jmrust7bgdbdl6skkvuzhqost4jkikrluj6alemspeifm5hvgqz2qaad.onion:5222 |
The config file is owned by the Docker container so you need to edit from inside it:
sudo docker exec -it jam_web_1 bash
and
sed -i 's|^directory_nodes =.*|directory_nodes = jmarketxf5wc4aldf3slm5u6726zsky52bqnfv6qyxe5hnafgly6yuyd.onion:5222,coinjointovy3eq5fjygdwpkbcdx63d7vd4g32mw7y553uj3kjjzkiqd.onion:5222,nakamotourflxwjnjpnrk7yc2nhkf6r62ed4gdfxmmn5f4saw5q5qoyd.onion:5222,satoshi2vcg5e2ept7tjkzlkpomkobqmgtsjzegg6wipnoajadissead.onion:5222,jmrust7bgdbdl6skkvuzhqost4jkikrluj6alemspeifm5hvgqz2qaad.onion:5222|' /root/.joinmarket/joinmarket.cfg
Then verify:
grep "directory_nodes" /root/.joinmarket/joinmarket.cfg
and
exit
More directories means more redundancy. If one goes down under attack takers can still find you via the others. It does not affect your selection probability (that's purely fidelity bond weight) but it keeps you visible and reachable.
Recovering jmwalletd After a CrashRecovering jmwalletd After a Crash
When the OOM killer fires on jmwalletd, dinit (the process manager inside the JAM container) marks the service as failed after too many fast restarts and stops trying. The container stays up but the wallet daemon is dead, hence the 502 Bad Gateway in the JAM UI.
Restart it manually from inside the container:
sudo docker exec -it jam_web_1 bash
dinitctl start jmwalletd
sleep 8
dinitctl status jmwalletd
If it shows State: STARTED after 8 seconds, exit and refresh the JAM UI. If it dies again immediately, check the actual error:
cat /var/log/jam/jmwalletd_stdout.log | tail -30
Common causes after a crash:
RPC connection to Bitcoin Core was not established: Bitcoin Core is still starting up, wait a few minutes and retrywallet lockfileerrors: remove stale lockfiles from/root/.joinmarket/wallets/No such file or directory: wrong path, find jmwalletd:find / -name jmwalletd.py 2>/dev/null
What I LearnedWhat I Learned
The DoS patch is necessary but not sufficient. The attacker's flood still generates log entries even when connections are dropped. At that point, swap exhaustion is the silent killer. The OOM event leaves no obvious trace in application logs, only in dmesg. And the default Umbrel JAM config has been shipping with dead directory nodes for a while, meaning many makers are wasting resources on ghost connections they don't know about.
Fixed all three together and the setup is looking now finally stable.
Track the official PR for a permanent merge: github.com/JoinMarket-Org/joinmarket-clientserver/pull/1840
The PR will most likely never be merged... But the good news is that Jam v2 with joinmarket-ng backend is on the way!
That's a great news! Any time estimate?
I would say in a month or two. jm-ng is already part of the tests for jam v2 development.
Great, I've a machine parked here and waiting for it!