Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Memory

The formula

peak ≈ 12 MiB                                 base (14.5 MiB with glibc)
    + 5.5 MiB                                 if an S3 backend is used
    + hash_workers × m + 2 MiB                password checks
    + sessions × 0.15 MiB
    + connections in the hash queue × 70 KiB  at most hash_queue
    + local transfers × 4 MiB
    + S3 uploads × 26 MiB + S3 downloads × 8 MiB
    + addresses tracked by bans × 225 B

m is the largest argon2 m in the users file: 19 MiB with the owasp-min profile (default of hash-password), 64 MiB with the rfc9106-low-mem profile. The startup line gives this term: peak_memory="1 thread × 19 MiB = 19 MiB". See Password hashing.

The formula is an upper bound: it adds up peaks that do not all happen at the same moment. No measured case has exceeded it. File size does not enter it: transfers are streamed.

The terms are measurements on the published image (static musl binary, x86_64), with an OpenSSH client. A connection in the hashing queue weighs ~42 KiB in Basic, a local download 2.3 MiB. At large scale with S3, uploads dominate: 4 threads at m=65536 and 10 S3 uploads measure 415 MiB (formula: 537). Nothing caps the number of concurrent S3 uploads; max_sessions_per_user sets a bound per user.

A small deployment under 80 MB

30 users, ~900 files per day: a few concurrent clients.

[auth]
hash_workers = 1      # one check at a time: 1 × 19 MiB
hash_queue = 32       # a full queue: 32 × 70 KiB = 2.2 MiB

With hash-password hashes (profile owasp-min), and no rfc9106-low-mem hash in the file:

VariantFormulaFits under 80 MB
local backend45.5 MiByes
2 threads, local backend64.5 MiByes
S3, one upload at a time64.7 MiByes
S3, 3 concurrent uploads116.5 MiBno
profile rfc9106-low-mem90.5 MiBno: a single verification exceeds the budget
default queue (1024)+70 MiB with a full queueno

glibc binaries

The Docker images (musl) give memory back after each peak. A glibc binary (releases, glibc host) keeps the memory of the hashes in its arenas: 95 MiB retained after a burst (4 threads, m=19456), 19 MiB with MALLOC_MMAP_THRESHOLD_=4194304. Set this variable (Environment= in the systemd unit) and do not set MALLOC_ARENA_MAX, which raises the peak.

Kubernetes

SettingWhat it must cover
limits.memorythe peak of the formula, for the expected number of concurrent transfers and sessions, plus a margin
requests.memorymemory at rest and under ordinary load: base and sessions

The chart ships requests.memory: 64Mi and limits.memory: 256Mi. With its default (1 thread, derived from requests.cpu: 100m), 1000 SSH connections queued with the rfc9106-low-mem profile reach 146 MiB. On a busy node, raise the request toward the expected peak.

A pod without limits.cpu sees the node’s CPUs: see Password hashing to set the pool size.

Watching memory

/metrics exposes process_resident_memory_bytes and process_peak_resident_memory_bytes (Linux only), and the admin console plots them live. See Metrics.

Not measured: REST transfers, SFTP proxy, active telemetry, thousands of users, aarch64 and Windows.