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.
Minimum and recommended
| Tier | Host RAM | Host CPU | Notes |
|---|---|---|---|
| Minimum | 4 GB | 2 cores | Default profile. Comfortable for a small team (≈10 users / ≈1 000 products). |
| Recommended | 8 GB | 4 cores | Headroom for concurrent imports/exports and PDF bursts. |
| Loaded | 8–16 GB | 4–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)
| Service | Memory | CPU | Role |
|---|---|---|---|
| backend | 1536m | 4.0 | API / web server (JVM heap auto-sizes to 70% of this) |
| postgres | 512m | 4.0 | Database |
| backup-worker | 832m | 2.0 | Scheduled backups (pg_dump) |
| gotenberg | 448m | 2.0 | Chromium PDF rendering |
| frontend | 96m | 2.0 | Caddy serving the static build |
| caddy | 96m | 2.0 | TLS / 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_LIMITbelow ~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.