# Docker Resource Limits — Calculation Methodology

## Overview

Methodology for calculating Docker container resource limits based on actual RSS measurement and host RAM budget.

## Step 1: Measure Actual RSS

### Via /proc/PID/status

```bash
# Get container's host PID
docker inspect <container> --format '{{.State.Pid}}'

# Read VmRSS from /proc
cat /proc/<PID>/status | grep VmRSS
# VmRSS:    844592 kB  → 844 MB
```

### Via cgroup (for systemd-managed containers)

```bash
# Find cgroup path
find /sys/fs/cgroup -name "memory.current" -path "*<container>*" 2>/dev/null | head -1

# Read memory usage
cat /sys/fs/cgroup/system.slice/<container>.service/memory.current
# 84226048 → 80 MB
```

## Step 2: Calculate Host RAM Budget

```
Total RAM:          64 GB
llama-server:      -28 GB  (host process, dominant consumer)
Open WebUI:        -0.8 GB
llama-swap:        -0.2 GB
Hermes gateway:    -0.3 GB
Hermes dashboard:  -0.2 GB
Redis:             -0.005 GB
Docker daemon:     -0.05 GB
OS overhead:       -1.0 GB
─────────────────────────────
Available:         ~33.6 GB
```

**Key constraint:** llama-server uses 28 GB of host RAM. It's a host process (not in Docker), so it always gets priority. Containers share the remaining ~33 GB.

## Step 3: Allocate Limits

Rule: 2x headroom over observed RSS for most containers, 7-16x for lightweight services.

| Container | Observed RSS | Limit | Rationale |
|-----------|-------------|-------|-----------|
| open-webui | 844 MB | 2 GB | Python + embedding models + vector DB. 2.4x headroom. |
| searxng-core | 72 MB | 512 MB | Python web search. 7x headroom. |
| nexstream-scraper | 35 MB | 1 GB | Node.js scraper. Spikes during scraping bursts. |
| open-terminal | 44 MB | 512 MB | ttyd terminal server. 11x headroom. |
| searxng-valkey | 16 MB | 256 MB | Redis-compatible cache. 16x headroom. |

**Total container memory: ~4.5 GB out of 33.6 GB available (13%).**

## Step 4: Apply Limits

```bash
docker run -d \
  --name <container> \
  --memory <hard_limit> \
  --memory-reservation <soft_limit> \
  --memory-swap <hard_limit> \
  --cpus <cpu_limit> \
  --log-opt max-size=10m --log-opt max-file=3 \
  --restart unless-stopped \
  ...
```

- `--memory`: Hard limit (OOM killer activates)
- `--memory-reservation`: Soft limit (kernel starts reclaiming early)
- `--memory-swap`: Equal to memory prevents swap usage
- `--cpus`: CPU limit (optional, prevents container from starving host)

## Research Backing

- Docker memory limits are soft by default — the kernel tries to reclaim memory before OOM
- Setting `--memory-reservation` at 50% of `--memory` gives the kernel a hint for early pressure
- Docker memory limits only apply to host RAM (system memory), not GPU VRAM
- Your llama-server's 28 GB RSS is host RAM used for: model weights (~22 GB), KV cache (~6 GB), CUDA pinned memory overhead
