Files
homarr/pnpm-workspace.yaml
T
Thomas Camlong 3cc6ff33df perf(docker): stop loading ssh2, which allocates a 16 MiB WASM heap nobody uses
Profiling at the memory high-water mark - not at idle, which is what every earlier
round measured - found the largest single retainer in every snapshot was one V8
Context holding a 16.00 MiB ArrayBuffer. Expanding it through the dominator tree
named it: ssh2's crypto module, whose POLY1305_WASM_MODULE instantiates a
WebAssembly memory for ChaCha20-Poly1305 as soon as it is required.

Homarr has no SSH feature and does not depend on ssh2. The chain is
dockerode -> docker-modem -> ssh2, and docker-modem/lib/modem.js requires './ssh'
at module scope while using it in exactly one place, already guarded by
`protocol === 'ssh'`. A pnpm patch moves the require inside that branch. SSH-based
Docker hosts still work - require() is cached, so they pay it once - and every
socket-based install, which is everyone else, never loads it.

Measured over four alternated runs on a restored real backup, external memory:

                     before        after
  board loaded    25.4, 25.1    8.3, 11.2
  nav-01          28.5, 25.4   10.1,  8.6
  multi-tab       31.1, 35.8   13.0, 10.0
  post-soak       21.4, 21.5    5.5,  7.2

Consistently -15 to -18 MiB, ranges non-overlapping, and 30 fewer files loaded
(1470 -> 1440).

Peak RSS does NOT improve, and that is worth stating plainly: V8 sizes its heap
against available headroom, so freeing native memory lets it grow further before
collecting. What changes is the composition - a fixed 16 MiB of unusable WASM
buffer becomes headroom the app can actually use, which matters under a container
memory limit but does not move the peak number.

Also adds the instruments this needed. A heap snapshot can never explain a peak,
because V8 runs a full GC before serialising one and reports only survivors:

  - memory-probe.cjs gains in-process allocation sampling, on-demand snapshots via
    v8.writeHeapSnapshot, an smaps endpoint, and require-graph recording.
  - analyze-allocations.mjs diffs two stages to isolate what one stage allocated.
  - analyze-smaps-stages.mjs splits each stage's RSS into JS heap, JIT, native and
    file-backed pages.
  - analyze-heap-dominators.mjs can now expand the largest retainer, which is what
    identified ssh2. A plain graph walk escapes through __proto__ and is useless.

One trap recorded in the findings: taking the peak snapshot over CDP delivers it as
JS string chunks and added ~134 MiB of ArrayBuffers to the process being measured,
producing a fake 693 MiB reading. v8.writeHeapSnapshot streams from C++ instead, and
the probe now reports its own observerCostMiB so the cost of looking is visible.
2026-08-09 22:17:25 +02:00

257 lines
6.8 KiB
YAML

packages:
- apps/*
- packages/*
- tooling/*
catalog:
"@babel/parser": ^7.29.7
"@auth/core": ^0.41.2
"@auth/drizzle-adapter": ^1.11.2
"@ctrl/deluge": ^8.0.0
"@ctrl/qbittorrent": ^9.14.0
"@ctrl/transmission": ^7.4.0
"@dnd-kit/core": ^6.3.1
"@dnd-kit/modifiers": ^9.0.0
"@dnd-kit/sortable": ^10.0.0
"@dnd-kit/utilities": ^3.2.2
"@drizzle-team/brocli": ^0.12.0
"@extractus/feed-extractor": 7.2.1
"@gfazioli/mantine-onboarding-tour": ^4.0.6
"@gitbeaker/rest": ^43.8.0
"@homarr/gridstack": ^1.12.0
"@homarr/node-unifi": ^2.6.0
"@immich/sdk": ^3.0.2
"@jellyfin/sdk": ^0.13.0
"@kubernetes/client-node": ^1.4.0
"@modelcontextprotocol/sdk": 1.29.0
"@mantine/charts": ^9.4.1
"@mantine/colors-generator": ^9.4.1
"@mantine/core": ^9.4.1
"@mantine/dates": ^9.4.1
"@mantine/dropzone": ^9.4.1
"@mantine/form": ^9.4.1
"@mantine/hooks": ^9.4.1
"@mantine/modals": ^9.4.1
"@mantine/notifications": ^9.4.1
"@mantine/spotlight": ^9.4.1
"@mantine/tiptap": ^9.4.1
"@octokit/auth-app": ^8.2.0
"@paralleldrive/cuid2": ^3.3.0
"@playwright/test": ^1.61.1
"@semantic-release/changelog": ^7.0.0
"@semantic-release/commit-analyzer": ^13.0.1
"@semantic-release/git": ^11.0.1
"@semantic-release/github": ^12.0.9
"@semantic-release/npm": ^13.1.5
"@semantic-release/release-notes-generator": ^14.1.1
"@t3-oss/env-nextjs": ^0.13.11
"@tabler/icons-react": ^3.44.0
"@tanstack/react-devtools": ^0.10.8
"@tanstack/react-query": ^5.101.2
"@tanstack/react-query-devtools": ^5.101.2
"@tanstack/react-query-next-experimental": ^5.101.2
"@testcontainers/mysql": ^12.0.4
"@testcontainers/postgresql": ^12.0.4
"@testcontainers/redis": ^12.0.4
"@tiptap/extension-color": 3.27.3
"@tiptap/extension-highlight": 3.27.3
"@tiptap/extension-image": 3.27.3
"@tiptap/extension-link": ^3.27.3
"@tiptap/extension-placeholder": ^3.27.3
"@tiptap/extension-table": 3.27.3
"@tiptap/extension-table-cell": 3.27.3
"@tiptap/extension-table-header": 3.27.3
"@tiptap/extension-table-row": 3.27.3
"@tiptap/extension-task-item": 3.27.3
"@tiptap/extension-task-list": 3.27.3
"@tiptap/extension-text-align": 3.27.3
"@tiptap/extension-text-style": 3.27.3
"@tiptap/extension-underline": 3.27.3
"@tiptap/react": ^3.27.3
"@tiptap/starter-kit": ^3.27.3
"@trpc/client": ^11.18.0
"@trpc/next": ^11.18.0
"@trpc/react-query": ^11.18.0
"@trpc/server": ^11.18.0
"@trpc/tanstack-react-query": ^11.18.0
"@turbo/gen": ^2.10.4
"@types/adm-zip": 0.5.8
"@types/bcrypt": 6.0.0
"@types/better-sqlite3": 7.6.13
"@types/chroma-js": 3.1.2
"@types/cookies": 0.9.2
"@types/crypto-js": ^4.2.2
"@types/css-modules": ^1.0.5
"@types/dockerode": ^4.0.1
"@types/node": ^24.13.3
"@types/node-unifi": ^2.5.1
"@types/pg": ^8.20.0
"@types/prismjs": ^1.26.6
"@types/react": 19.2.17
"@types/react-dom": 19.2.3
"@scalar/api-reference-react": ^0.9.54
"@types/video.js": ^7.3.58
"@types/ws": ^8.18.1
"@types/xml2js": ^0.4.14
posthog-js: ^1.399.1
posthog-node: ^5.40.0
"@vitejs/plugin-react": ^6.0.3
"@vitest/coverage-v8": ^4.1.10
"@vitest/ui": ^4.1.10
"@xterm/addon-canvas": ^0.7.0
"@xterm/addon-fit": 0.11.0
"@xterm/xterm": ^6.0.0
adm-zip: 0.5.18
jszip: ^3.10.1
sql.js: ^1.14.1
babel-plugin-react-compiler: ^1.0.0
bcrypt: ^6.0.0
better-sqlite3: ^12.11.1
chroma-js: ^3.2.0
clsx: ^2.1.1
concurrently: ^10.0.3
conventional-changelog-conventionalcommits: ^10.2.0
cookie: 2.0.1
cookies: ^0.9.1
croner: 10.0.1
cross-env: ^10.1.0
crypto-js: ^4.2.0
dayjs: ^1.11.21
deepmerge: 4.3.1
dns-caching: ^0.2.9
dockerode: ^5.0.1
dotenv: ^17.4.2
dotenv-cli: ^11.0.0
drizzle-kit: ^0.31.10
drizzle-orm: ^0.45.2
drizzle-zod: ^0.8.3
esbuild: ^0.28.0
fast-xml-parser: ^5.9.3
fastify: ^5.9.0
flag-icons: ^7.5.0
glob: ^13.0.6
ical.js: ^2.2.1
ioredis: 5.11.1
jotai: ^2.20.1
jsdom: ^29.1.1
json5: ^2.2.3
jsonpath-plus: ^10.4.0
ldapts: 8.1.8
lodash.clonedeep: ^4.5.0
lucide-react: ^1.24.0
"mantine-contextmenu": ^9.3.1
mantine-datatable: ^9.3.1
mantine-react-table: npm:mantine-react-table-open@9.0.3
mcp-handler: ^1.1.0
maria2: ^0.4.1
mysql2: 3.22.6
next: 16.3.0-preview.9
next-auth: 5.0.0-beta.31
next-intl: 4.13.1
node-loader: ^2.1.0
octokit: ^5.0.5
oxfmt: ^0.58.0
oxlint: ^1.73.0
pg: ^8.22.0
postcss-preset-mantine: ^1.18.0
prismjs: ^1.30.0
proxmox-api: 1.1.1
react: 19.2.7
react-dom: 19.2.7
react-error-boundary: ^6.1.2
react-jsx-parser: ^2.4.1
react-markdown: ^10.1.0
react-quill-new: ^3.8.3
react-simple-code-editor: ^0.14.1
recharts: ^3.9.1
sass: ^1.101.0
semantic-release: ^25.0.5
semver-parser: ^4.2.1
superjson: 2.2.6
svgson: ^5.3.1
testcontainers: ^12.0.4
timezones-ical-library: ^2.2.1
trpc-to-mcp: ^1.3.2
trpc-to-openapi: ^3.3.0
tsdav: ^2.3.0
tsx: 4.20.4
turbo: ^2.10.4
typescript: 7.0.2
vite: ^8.1.4
undici: 7.28.0
use-deep-compare-effect: ^1.8.1
video.js: ^8.23.9
vite-tsconfig-paths: ^6.1.1
vitest: ^4.1.10
winston: 3.19.0
ws: ^8.21.0
xml2js: ^0.6.2
yaml: 2.9.0
zod: ^4.4.3
zod-form-data: ^3.0.2
zod-validation-error: ^5.0.0
linkedom: ^0.18.13
dompurify: ^3.4.13
catalogMode: strict
nodeLinker: hoisted
strictPeerDependencies: false
engineStrict: true
verifyDepsBeforeRun: warn
minimumReleaseAge: 4320
minimumReleaseAgeExclude:
- next
- "@next/*"
- "typescript@7.0.2"
- "@immich/sdk@3.0.0"
# Renovate security update: turbo@2.9.14
- turbo@2.9.14
# Renovate security update: form-data@4.0.6
- form-data@4.0.6
# Renovate security update: undici@7.28.0
- undici@7.28.0
# Renovate security update: esbuild@0.28.1
- esbuild@0.28.1
overrides:
"@babel/helpers@<7.26.10": ">=7.29.7"
"@babel/runtime@<7.26.10": ">=7.29.7"
"axios@>=1.0.0 <1.8.2": ">=1.18.1"
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.7"
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.7"
"esbuild@<=0.24.2": ">=0.28.1"
"form-data@>=4.0.0 <4.0.4": ">=4.0.6"
"hono@<4.6.5": ">=4.12.28"
"linkifyjs@<4.3.2": ">=4.3.3"
"nanoid@>=4.0.0 <5.0.9": ">=5.1.16"
"prismjs@<1.30.0": ">=1.30.0"
"proxmox-api>undici": "7.28.0"
"react-is": "^19.2.7"
"rollup@>=4.0.0 <4.22.4": ">=4.62.2"
"sha.js@<=2.4.11": ">=2.4.12"
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.3"
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.3"
"tmp@<=0.2.3": ">=0.2.7"
"vite@>=5.0.0 <=5.4.18": ">=8.1.4"
patchedDependencies:
"@types/node-unifi": "patches/@types__node-unifi.patch"
docker-modem@5.0.7: patches/docker-modem@5.0.7.patch
"trpc-to-openapi": "patches/trpc-to-openapi.patch"
allowUnusedPatches: true
allowBuilds:
"@tree-sitter-grammars/tree-sitter-yaml": true
bcrypt: true
better-sqlite3: true
cpu-features: false
esbuild: true
sharp: true
ssh2: true
tree-sitter: true
tree-sitter-json: true
"@scarf/scarf": false
"@swc/core": false
core-js: false
core-js-pure: false
msw: false
protobufjs: false
vue-demi: false