The self-hosted Infisical backend (infisical/infisical) sits at 600–800 MB RSS even when idle.
On a small/single-user box it's often the single fattest process on the machine. Here's the diagnosis,
plus an honest account of what actually controls it and what doesn't.
$ docker stats --no-stream infisical-backend
682.3MiB / 3.827GiB infisical-backend
One process inside the container:
$ docker exec infisical-backend ps -eo rss,args | sort -rn | head -1
641360 node --enable-source-maps dist/main.mjs # ~641 MB
$ docker exec infisical-backend sh -c 'echo $NODE_OPTIONS'
--max-old-space-size=2048
- One fat Node monolith. The whole backend — API + BullMQ background workers (rotation, integration syncs, scanning) + audit + KMS/crypto + migrations — runs in a single Node process. Its baseline working set is genuinely a few hundred MB.
- V8 heap ceiling is 2048 MB (
--max-old-space-size=2048, baked into the image). V8 grows the heap to cover allocation peaks and does not return that memory to the OS, so RSS parks at the high-water mark. - Long uptime compounds it. A week of API calls + queue jobs + source-map-laden stack traces accumulate retained heap. It's not a leak — it's normal V8 behavior with a high ceiling.
Known, maintainer-acknowledged upstream issue, no fix yet: Infisical/infisical#4795 — "[Self Hosted] Backend memory usage high (600-700 MB)".
The obvious lever — NODE_OPTIONS=--max-old-space-size=512 (down from the baked 2048) — barely moves
RSS. Measured on v0.160.11:
image default (2048 heap): ~680 MB (creeps up over days)
cold boot with 512 cap: ~260 MB
~6 min after boot, 512 cap: ~628 MB <-- climbed right back; the cap did not hold it down
Reason: --max-old-space-size caps only V8's old-space heap. Most of this process's footprint lives
outside old-space — native (malloc) allocations, the background workers, --enable-source-maps
overhead, and other V8 spaces (new-space, code-space, large-object-space). So total RSS is largely
unaffected. Keep the flag if you like (harmless), but don't expect savings from it.
- A hard
mem_limit. A ceiling Docker enforces, so the backend can never balloon and OOM the host. Pair withrestart: unless-stoppedso it recovers if it ever hits the wall. Don't set it so low the backend gets OOM-killed under normal load — ~1 GB is safe for a light single-user instance. - A periodic restart. Cold start is ~120–260 MB and it creeps up from there; since Node never returns retained memory, a scheduled restart is the only way to keep it near baseline.
# docker-compose
services:
backend:
container_name: infisical-backend
image: infisical/infisical:<your-tag>
restart: unless-stopped
environment:
- NODE_ENV=production
# Optional; little effect on RSS for this image (see above). Harmless to keep.
- NODE_OPTIONS=--max-old-space-size=512
# The lever that actually matters: a hard ceiling so it can't OOM the host.
mem_limit: 1024m# Weekly restart to reset the high-water-mark (~10s of API downtime)
0 4 * * 0 docker restart infisical-backendThe 600–800 MB idle footprint is inherent to the current backend image (upstream #4795). The V8 heap
flag is a red herring for RSS; the pragmatic controls are a hard mem_limit to protect the host and a
scheduled restart to keep it near cold baseline.