My Synology DS920+ locked up on a Sunday afternoon while I was mid-transfer, and the web interface took forty seconds just to load a page. The little memory graph in Resource Monitor was pinned at 98 percent, and swap usage was climbing like a stock chart nobody wanted to own.
Something was eating the RAM. I just didn't know what yet.
That afternoon taught me a repeatable process, and it works whether you run one container or fifteen. Below is exactly how I hunt down the culprit, confirm it, and put a cap on it so the box stops choking. No guesswork, just a few commands and a couple of menus.
First, confirm it is actually a memory problem
Before blaming any app, make sure RAM is the real bottleneck and not disk or CPU wearing a memory costume. Open DSM Resource Monitor, go to the Performance tab, and watch Memory alongside the swap graph.
Real memory pressure looks like this: physical memory sitting above 90 percent for minutes at a time, and swap actively growing. A brief spike to 95 percent during a backup is normal and harmless. Sustained swap usage is the tell, because that means the kernel has run out of fast RAM and is shoving pages onto your slow system partition.
If swap is flat near zero, your slowness is coming from somewhere else, and you can stop here. If swap is creeping upward, keep going.

Get the per-container numbers with docker stats
The single most useful command for this lives in the terminal. Enable SSH in Control Panel > Terminal & SNMP, connect with something like PuTTY or the built-in Windows SSH client, then run one line.
The command is docker stats. On DSM 7 with the Container Manager package you may need sudo docker stats instead.
You get a live table that refreshes every second. The columns that matter are MEM USAGE / LIMIT and MEM %. Here is roughly what a problem looks like in the wild:
| Container | MEM USAGE / LIMIT | MEM % |
|---|---|---|
| plex | 1.9GiB / 3.83GiB | 49.6% |
| jellyfin | 412MiB / 3.83GiB | 10.5% |
| homeassistant | 3.1GiB / 3.83GiB | 81.2% |
| vaultwarden | 38MiB / 3.83GiB | 0.97% |
See that Home Assistant line? That LIMIT column showing the full 3.83GiB for everything is the real story: none of these containers have a memory limit set, so each one can grab the entire box if it wants to. The one climbing toward 80 percent is your culprit.
Press Ctrl+C to stop the live view once you have spotted the offender.
Confirm the offender is actually leaking
A high number in one snapshot is not proof. Some apps genuinely need a lot of RAM and hold it happily. The difference between a hungry app and a leaking one is direction over time.
Take a snapshot with the no-stream flag, wait an hour of normal use, then take another. If a container climbed from 900MiB to 2.4GiB while doing nothing new, that is a leak or a runaway cache, and it will keep climbing until swap thrash sets in.
In my case the guilty party was a media indexer container that had a plugin stuck in a scan loop. It grew about 300MiB every ten minutes and never released a byte. Classic runaway.
Common repeat offenders I see
- Plex during library scans or on-the-fly transcoding of 4K files, especially with lots of subtitle burn-in.
- Home Assistant with a recorder database that was never given a purge interval.
- *arr apps (Sonarr, Radarr, Prowlarr) doing large RSS or metadata refreshes on huge libraries.
- Elasticsearch or a database container that grabbed a JVM heap sized for a server with 32GB, not your 4GB NAS.
Cap the container so it can never do this again
Once you know the offender, give it a hard ceiling. A memory limit means the container gets killed and restarted if it exceeds the cap, instead of dragging the whole NAS down with it. Your other apps stay responsive.
If you launched the container from the command line or a Docker Compose file, add a limit and recreate it. In compose the line is simple:
mem_limit: 1g for the older short syntax, or under deploy: resources: limits: memory: 1g for the newer schema.
From a plain docker run you add --memory="1g" --memory-swap="1g". Setting memory-swap equal to memory is the important half, because it stops the container from spilling into swap and thrashing your disk anyway.
Doing it from the DSM interface
If you prefer clicking, DSM 7's Container Manager lets you set this without touching a terminal. Stop the container, open its settings, and look under the resource or advanced settings section for a memory limit field. Enter a value in MB, apply, and start it again. Older DSM 6 Docker packages hide the same option in the container's Edit dialog under Resource Limitation.
When the fix is not a cap but a config change
Sometimes capping just papers over a setting that should be corrected. Home Assistant's recorder is the classic example: by default it logs every sensor state forever, and the database balloons. Adding a purge_keep_days: 10 and excluding chatty sensors cuts memory use dramatically without any Docker limit at all.
Java-based containers are the other big one. If an app runs a JVM, look for an environment variable like JAVA_OPTS or a heap flag and set something sane such as -Xmx512m. Left alone, the JVM sizes its heap based on total host RAM and will happily claim a quarter of your NAS.
The official Docker resource constraints documentation covers every flag if you want the full reference, and Synology's own Resource Monitor help page explains what each graph is actually measuring.
A quick note on adding more RAM
Buying a memory stick feels like the obvious fix, and on some models it genuinely helps. A DS920+ officially takes 8GB and unofficially runs fine at 20GB with a single 16GB SODIMM, often for around 40 dollars.
But throwing RAM at a leaking container just delays the crash by a few hours. Fix the leak first, then add memory if your genuine, well-behaved workload actually needs it. I have watched people spend money to make a memory leak take longer to lock up their box, which is the worst of both worlds.
Putting it into a five-minute routine
Once you have done this a couple of times it becomes muscle memory. Confirm swap is growing in Resource Monitor, run docker stats to spot the greedy container, take two snapshots an hour apart to prove it is climbing, then either cap it or fix the config that let it run away.
The whole hunt takes maybe five minutes now, and I run it any time the DS920+ feels sluggish. Nine times out of ten it is the same handful of suspects, and a sensible limit keeps them honest for good.
Frequently asked questions
Why does my NAS show 80 percent memory used when almost nothing is running?
Linux fills unused RAM with disk cache to speed up file access, and that cache counts as used memory in the graph. It is released instantly when an app needs it, so it is not a problem. Watch the swap graph instead, because growing swap is the true sign of memory pressure.
What is the difference between docker stats and DSM Resource Monitor?
Resource Monitor shows the whole system's memory and swap, which tells you there is a problem. docker stats breaks it down per container so you can see exactly which app is responsible. You use Resource Monitor to confirm the symptom and docker stats to find the cause.
Will setting a memory limit crash my container?
It can if you set the cap too low, because the container gets OOM-killed and restarts when it exceeds the limit. Set the ceiling 10-20 percent above the container's normal steady-state usage from docker stats, and it will only be killed if it genuinely runs away. That is exactly the behaviour you want.
My container keeps growing even after a restart. Is that a leak?
Take two docker stats --no-stream snapshots an hour apart during normal use. If usage climbs steadily with no new work, it is either a memory leak or an unbounded cache, such as a database with no purge interval. A hungry but healthy app rises then plateaus rather than climbing forever.
Should I just add more RAM instead of chasing the culprit?
Not until you have fixed the leak. Extra RAM only delays the crash on a leaking container, sometimes by just a few hours. Diagnose and cap the offending app first, then add memory only if a well-behaved workload genuinely needs the headroom.