# Local Runbook: Taming Memory on Apple Silicon (M1 Mac mini, 8 GB RAM)

This is a local working note for running this project on a base-model Apple Silicon Mac mini with 8 GB of unified memory. It is intentionally gitignored because it is machine-specific and may change as the local setup evolves.

The goal is to reduce memory pressure and avoid OOM kills while keeping the same Docker CLI and dev-container workflow.

---

## TL;DR — Try this first (Docker Desktop, no migration)

**Step 1 — Cap Docker Desktop's memory**

Docker Desktop → Settings → Resources → Advanced → set **Memory** to **3 GB** → Apply & Restart.

That single change is likely to fix or significantly reduce OOM incidents. Do this before considering a Colima migration.

**Step 2 — Never run heavy commands concurrently**

```bash
pnpm lint && pnpm test && pnpm build   # good — sequential
pnpm lint & pnpm build                 # bad — concurrent
```

**Step 3 — If builds still OOM, cap Node's heap**

```bash
NODE_OPTIONS="--max-old-space-size=2048" pnpm build
```

If Docker Desktop is still sluggish after all three steps, see [Migrating to Colima](#migrating-to-colima) below.

---

## Option A: Tune Docker Desktop (recommended first step)

### Why the default Docker Desktop settings cause OOM

By default, Docker Desktop on Mac has no memory cap — it will claim as much of the unified memory pool as it wants. On an 8 GB machine shared between macOS, VS Code, the browser, and the Docker VM, this causes the host to swap under any real build load.

### Setting the memory limit

1. Open **Docker Desktop**
2. Click the **gear icon** (Settings) in the top-right
3. Go to **Resources → Advanced**
4. Set **Memory** to **3 GB**
5. Optionally set **CPUs** to **4** (leaving headroom for macOS/VS Code)
6. Click **Apply & Restart**

Docker Desktop will restart its VM with the new cap. Your containers and volumes are unaffected.

### How to verify it worked

After Docker restarts, open a terminal inside the dev container and check:

```bash
free -h
```

The `total` line under `Mem:` should show roughly 3 GB.

From the host, watch per-container usage:

```bash
docker stats --no-stream
```

### Why 3 GB

On 8 GB unified memory, the rough idle budget looks like this:

| Workload | Memory |
|---|---|
| macOS kernel + system | ~1.5 GB |
| VS Code + extension host | ~0.8 GB |
| Browser (a few tabs) | ~0.8 GB |
| Available for Docker VM | ~4.9 GB |

That math suggests 4 GB is safe — but macOS, VS Code, and Chrome all spike above their idle footprint, especially when Next.js is building inside the VM at the same time. A 3 GB cap leaves macOS 5 GB of headroom to absorb those spikes without swapping.

### If 3 GB is too tight for builds

Try 3.5 GB. Docker Desktop accepts fractional values. Or keep 3 GB for daily dev and temporarily bump to 4 GB when you need to run a full build, then drop it back.

---

## Option B: Migrate to Colima

Colima is worth considering if:

- Docker Desktop itself (its tray app, update daemon, and analytics processes) is consuming noticeable memory even when idle — typically 200–400 MB
- You want tighter scripted control over the VM (named profiles, startup automation)
- You prefer not running Docker Desktop's proprietary layer

**If Option A fixed your OOM issues, you do not need this.** The memory saving from switching to Colima is real but modest on its own.

### What Colima gives you

- A lightweight Lima-based VM with no background UI processes
- Named profiles so you can switch between a lean dev VM and a beefier build VM
- The same Docker CLI and dev-container workflow — nothing else changes

### Why `--vm-type vz` and `--mount-type virtiofs` are still correct

`vz` (Apple Virtualization.framework) uses Apple's native hypervisor rather than QEMU's software paths. It has lower CPU overhead on Apple Silicon. `virtiofs` is genuinely faster than `sshfs` (the old Colima default) for file I/O, which matters for `node_modules` watching and hot reload.

Switching VM type does **not** fix OOM — both `vz` and `qemu` draw from the same unified memory pool. The `--memory` flag is the only lever for memory.

### Recommended Colima start command

```bash
colima start \
  --profile dev \
  --runtime docker \
  --arch aarch64 \
  --vm-type vz \
  --mount-type virtiofs \
  --cpu 4 \
  --memory 3 \
  --disk 60
```

Same 3 GB cap as the Docker Desktop recommendation above, same reasoning.

### Named profiles (optional but useful)

```bash
# Lean daily-dev profile
colima start --profile dev \
  --runtime docker --arch aarch64 \
  --vm-type vz --mount-type virtiofs \
  --cpu 4 --memory 3 --disk 60

# Beefier build profile — stop dev first
colima stop --profile dev
colima start --profile build \
  --runtime docker --arch aarch64 \
  --vm-type vz --mount-type virtiofs \
  --cpu 4 --memory 4 --disk 60

# Switch back
colima stop --profile build
colima start --profile dev
docker context use colima-dev
```

Profile names determine the Docker context name: `colima-dev`, `colima-build`, etc. Always confirm after switching:

```bash
docker context ls
docker context use colima-dev
```

---

## OOM Playbook

Applies whether you are on Docker Desktop or Colima.

### Step 1 — Diagnose where memory is going

Check host memory pressure:

```bash
memory_pressure
```

A `System-wide memory free percentage` below ~15% means macOS is swapping heavily.

Check what the VM thinks it is using:

```bash
# Docker Desktop
docker run --rm alpine free -h

# Colima
colima ssh --profile dev -- free -h
```

Check per-container usage:

```bash
docker stats --no-stream
```

### Step 2 — Reduce what is running in the VM

Stop Inngest if you are not testing workflows:

```bash
# Inside the dev container
pkill -f inngest || true
```

Restart only the app container:

```bash
docker compose -f .devcontainer/docker-compose.yml restart app
```

### Step 3 — Cap Node's heap

```bash
NODE_OPTIONS="--max-old-space-size=2048" pnpm build
```

If still failing:

```bash
NODE_OPTIONS="--max-old-space-size=1536" pnpm build
```

A lower cap forces more frequent GC rather than letting Node fill the VM and get killed. Add it to your shell profile inside the dev container to make it permanent:

```bash
export NODE_OPTIONS="--max-old-space-size=2048"
```

### Step 4 — Use targeted commands while iterating

Instead of the full test suite:

```bash
pnpm exec vitest run tests/ticket-state-machine.test.ts
```

Instead of full lint:

```bash
pnpm exec eslint src/app/FormFields.tsx src/app/(app)/submit/SubmitForm.tsx
```

Instead of a full build to check types:

```bash
pnpm exec tsc --noEmit
```

---

## Migrating to Colima (step by step)

Only do this if you have tried Option A and still want to switch.

### 1. Commit or stash your work

```bash
git status
```

### 2. Back up your local database

Docker Desktop and Colima do not share volumes.

```bash
mkdir -p ~/Desktop/ask-qien-db-backups

docker exec -t ask-qien-db-1 pg_dump -U postgres -d askqien \
  > ~/Desktop/ask-qien-db-backups/askqien-before-colima.sql
```

Compressed:

```bash
docker exec -t ask-qien-db-1 pg_dump -U postgres -d askqien \
  | gzip > ~/Desktop/ask-qien-db-backups/askqien-before-colima.sql.gz
```

Check the container name first with `docker ps` if unsure.

### 3. Stop dev services

Inside the dev container:

```bash
pkill -f "next dev" || true
pkill -f "inngest" || true
```

On the host:

```bash
docker compose -f .devcontainer/docker-compose.yml down
```

### 4. Install Colima

```bash
brew update
brew install colima docker docker-compose
```

Verify:

```bash
colima version
docker version
docker compose version
```

### 5. Quit Docker Desktop

Quit Docker Desktop from the menu bar. Do not uninstall it yet.

### 6. Start Colima and set context

```bash
colima start --profile dev \
  --runtime docker \
  --arch aarch64 \
  --vm-type vz \
  --mount-type virtiofs \
  --cpu 4 \
  --memory 3 \
  --disk 60

docker context use colima-dev
docker ps   # should return empty, not an error
```

### 7. Reopen in VS Code Dev Containers

```bash
cd /path/to/repo
code .
```

Then run `Dev Containers: Reopen in Container`.

If VS Code still tries Docker Desktop: quit VS Code, confirm Docker Desktop is not running, confirm `docker context use colima-dev`, then reopen from the terminal.

### 8. Restore the DB dump

```bash
cat ~/Desktop/ask-qien-db-backups/askqien-before-colima.sql \
  | docker exec -i ask-qien-db-1 psql -U postgres -d askqien
```

Gzip version:

```bash
gunzip -c ~/Desktop/ask-qien-db-backups/askqien-before-colima.sql.gz \
  | docker exec -i ask-qien-db-1 psql -U postgres -d askqien
```

Clean restore (wipes existing data):

```bash
docker compose -f .devcontainer/docker-compose.yml down -v
docker compose -f .devcontainer/docker-compose.yml up -d db
# wait a few seconds
cat ~/Desktop/ask-qien-db-backups/askqien-before-colima.sql \
  | docker exec -i ask-qien-db-1 psql -U postgres -d askqien
```

### 9. Confirm everything works

```bash
docker compose -f .devcontainer/docker-compose.yml ps
```

Expected services:

| Service | Port |
|---|---|
| `app` (Node/Next.js) | 3000 |
| `db` (Postgres + pgvector) | 5432 |
| `mailpit` | 8025 (UI) |
| Inngest dev UI (when running) | 8288 |

Work normally for a full day before deciding whether to uninstall Docker Desktop.

---

## Useful Commands

```bash
# Docker Desktop memory check (from host)
docker run --rm alpine free -h

# Colima
colima status --profile dev
colima stop --profile dev
colima ssh --profile dev -- free -h
colima delete --profile dev      # destructive — loses all volumes and images

# Docker context
docker context ls
docker context use colima-dev

# Memory diagnostics
docker stats --no-stream
memory_pressure

# Compose
docker compose -f .devcontainer/docker-compose.yml ps
docker compose -f .devcontainer/docker-compose.yml down       # keep volumes
docker compose -f .devcontainer/docker-compose.yml down -v    # delete volumes
```

---

## Troubleshooting

### `Cannot connect to the Docker daemon`

Docker Desktop: confirm it is running.

Colima:

```bash
colima status --profile dev
colima start --profile dev
docker context use colima-dev
docker ps
```

### VS Code says Docker is unavailable (Colima)

1. Confirm Colima is running.
2. Quit Docker Desktop.
3. Quit VS Code.
4. `docker context use colima-dev`
5. `code .` from the terminal.

### Postgres port 5432 already in use

```bash
lsof -nP -iTCP:5432 -sTCP:LISTEN
```

Common causes: Docker Desktop Postgres still running, Homebrew Postgres, stale compose project. Stop the conflicting service before starting the dev container.

### `no matching manifest for linux/arm64`

Current images support Apple Silicon natively:

- `mcr.microsoft.com/devcontainers/typescript-node:22`
- `pgvector/pgvector:pg16`
- `axllent/mailpit:latest`

If a future image is amd64-only, prefer a multi-arch alternative over switching to `--arch x86_64` (emulation is significantly slower).

### Next.js dev server is slow or dies

```bash
docker stats --no-stream
memory_pressure
```

Then: stop Inngest, add `NODE_OPTIONS="--max-old-space-size=2048"`, run checks one at a time.
