# Host Memory Budget

Total physical RAM: 12 GB

## Allocation

| Component | Budget | Notes |
| --- | --- | --- |
| ZFS ARC | 4 GB | `vfs.zfs.arc_max=4294967296` in loader.conf |
| Host OS + nginx + system | 1.5 GB | base system, sshd, tailscale, cron |
| controlplane jail | 2 GB | Clawdie agent runtime, Node.js, PI |
| db jail | 1.5 GB | PostgreSQL 18 only |
| cms jail | 1 GB | Strapi + Astro builds |
| Headroom | 2 GB | burst capacity, agent processes |

## Enforcement

### ZFS ARC (enforced)

Set live:

```sh
sudo sysctl vfs.zfs.arc_max=4294967296
```

Persisted in `/boot/loader.conf`:

```
vfs.zfs.arc_max="4294967296"  # 4 GB
```

### Jail memory limits (not yet enforced)

`kern.racct.enable` is currently `0`. To enable RCTL-based jail memory limits:

1. Add to `/boot/loader.conf`:
   ```
   kern.racct.enable="1"
   ```
2. Reboot
3. Then set limits:
   ```sh
   rctl -a jail:controlplane:memoryuse:deny=2G
   rctl -a jail:db:memoryuse:deny=1536M
   rctl -a jail:cms:memoryuse:deny=1G
   ```

Until RACCT is enabled, use application-level limits:

- PostgreSQL: `shared_buffers = 384MB` in `postgresql.conf`
- Strapi: `NODE_OPTIONS="--max-old-space-size=512"` in Strapi env
- Astro builds: `NODE_OPTIONS="--max-old-space-size=512"` during build

### Monitoring

```sh
# overall memory
top -b -d 1 | head -10

# ZFS ARC usage
sysctl vfs.zfs.arc_max vfs.zfs.arc_min kstat.zfs.misc.arcstats.size

# per-jail (requires RACCT)
rctl -h jail:controlplane
rctl -h jail:db
rctl -h jail:cms
```

## Future: RACCT enablement

Enabling RACCT requires a reboot. Plan this during a maintenance window:

1. ZFS snapshot all jails
2. Add `kern.racct.enable="1"` to `/boot/loader.conf`
3. Reboot
4. Apply rctl rules
5. Persist rules in `/etc/rctl.conf`
