Skip to content
Q
QuoteNode

Installation

System Resources & Tuning

Minimum and recommended RAM/CPU for a QuoteNode deployment, how container limits work, how to adjust them, and how to avoid out-of-memory issues.

System Resources & Tuning

QuoteNode runs as a small set of Docker containers. The Compose files ship with memory and CPU limits tuned for a 4 GB-RAM VM, and every limit is overridable with an environment variable, so you can right-size the stack for your machine.

TierHost RAMHost CPUNotes
Minimum4 GB2 coresDefault profile. Comfortable for a small team (≈10 users / ≈1 000 products).
Recommended8 GB4 coresHeadroom for concurrent imports/exports and PDF bursts.
Loaded8–16 GB4–8 cores≈100 users / ≈20 000 products with parallel bulk operations.

The stack is idle-light: at rest the whole stack uses about 1.5 GiB of RAM regardless of how much data is in the database (there is no in-JVM entity cache — data lives in PostgreSQL). RAM scales with concurrency of bulk operations (import/export/PDF), not with the number of users or records.

How container limits work

mem_limit is a hard ceiling, not a reservation. A container uses only what it actually touches, up to the limit; a container that exceeds its limit is OOM-killed. A higher limit does not cost memory — it just raises the safety boundary.

The default profile sums to about 3 520 MiB of ceilings, which fits a 4 GB host with ~575 MiB left for the operating system. Because real idle usage is far lower (~1.5 GiB), the ceilings are headroom for load spikes, not expected consumption.

cpus is a ceiling too, but CPU is free when idle — a generous CPU ceiling costs nothing at rest and lets a service use cores when it needs them. A cpus value above the host core count simply means “use all available cores”, so the defaults are safe on a 1–2 core VM.

Default per-service limits (4 GB profile)

ServiceMemoryCPURole
backend1536m4.0API / web server (JVM heap auto-sizes to 70% of this)
postgres512m4.0Database
backup-worker832m2.0Scheduled backups (pg_dump)
gotenberg448m2.0Chromium PDF rendering
frontend96m2.0Caddy serving the static build
caddy96m2.0TLS / reverse-proxy ingress — raise CADDY_MEM_LIMIT for heavy TLS traffic

Adjusting RAM

Every limit is an optional environment variable. Set it in your .env.prod to override the default:

# Example: an 8 GB machine with more concurrent load
BACKEND_MEM_LIMIT=3g
POSTGRES_MEM_LIMIT=1g
GOTENBERG_MEM_LIMIT=768m

The backend’s Java heap auto-scales to 70% of BACKEND_MEM_LIMIT (via MaxRAMPercentage), so raising the limit raises the heap automatically — no JVM flags to edit. The same applies proportionally to the backup-worker (which uses 45%).

Available variables: BACKEND_MEM_LIMIT, BACKEND_CPUS, POSTGRES_MEM_LIMIT, POSTGRES_CPUS, WORKER_MEM_LIMIT, WORKER_CPUS, GOTENBERG_MEM_LIMIT, GOTENBERG_CPUS, FRONTEND_MEM_LIMIT, FRONTEND_CPUS, CADDY_MEM_LIMIT, CADDY_CPUS.

CPU

Leave the CPU defaults generous. The only place CPU matters in practice is cold start: the JVM-based backend and worker boot a large application context, which is CPU-bound. On a single core a cold start can take minutes; with 2+ cores it is well under a minute. The worker’s health-check grace period already accounts for slow starts, so a constrained machine recovers on its own without a restart loop.

Avoiding out-of-memory issues

  • Do not shrink WORKER_MEM_LIMIT below ~768m. A backup run (pg_dump + tar + gzip + the JVM) peaks at about 607 MiB; with less headroom it can be killed mid-backup.
  • On a 2 GB VM, the default profile is too large — lower the limits (e.g. BACKEND_MEM_LIMIT=768m, POSTGRES_MEM_LIMIT=384m) so the sum stays below physical RAM. The backend heap follows the limit automatically.
  • Watch the host, not just containers. If the host runs other workloads, count their memory too — Docker only enforces per-container ceilings, not total host usage.
  • A container repeatedly restarting with exit code 137 indicates an OOM kill; raise that service’s *_MEM_LIMIT.

Last reviewed: Recently