> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hill90.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Local development

> Run the whole infrastructure stack on a Mac with one command.

# Local development

The same compose files production uses will run the whole stack on a Mac. Docker Desktop
must be running. Nothing else is required — no SOPS, no age key, no Tailscale, no VPS
access, no secrets of any kind. `.env.local` is created for you from
`.env.local.example` on first run.

```bash theme={null}
bash scripts/local.sh up
```

That builds and starts every stack — edge, observability, vault, database and identity —
and waits until they actually answer.

```bash theme={null}
bash scripts/local.sh health     # probe every routed surface
bash scripts/local.sh urls       # print the URLs
bash scripts/local.sh status     # container status
bash scripts/local.sh logs       # follow everything, or: logs <container>
bash scripts/local.sh down       # remove containers and networks, keep data
bash scripts/local.sh reset      # also delete the local volumes (prompts)
```

## Verified cold — before the database and identity stacks existed

The local runbook was verified on 2026-07-26 against `main` at `0b40403` — not "it worked
on the machine that built it", but a fresh clone into an empty directory on a Mac with
every stack image deleted, no volumes, no networks, no `.env.local` and an empty build
cache. Every command was run exactly as written, and nothing outside the runbook was
needed.

<Warning>
  **These figures are historical.** They were measured before Postgres and Keycloak were
  added, and `scripts/local.sh` now brings both up as well. The container, target and router
  counts below are therefore lower than what a clean run produces today, and the timings
  predate three more images — Postgres, postgres-exporter and Keycloak. Nobody has re-run
  the cold verification since; treat the numbers as evidence the procedure worked, not as
  today's expected output.
</Warning>

| Step                                          | Result                                     |
| --------------------------------------------- | ------------------------------------------ |
| `local.sh up` from a fresh clone, zero images | 10/10 containers, 1:22                     |
| `local.sh health`                             | 7/7 checks pass                            |
| Traefik routers                               | 4 registered, all enabled                  |
| Prometheus targets                            | 7 targets, 7 up, 0 down                    |
| Grafana `admin/admin`                         | logs in; 3 datasources, 4 dashboards       |
| `local.sh down`                               | containers 10→0, networks 3→0, volumes 7→7 |
| `local.sh up` again                           | 10/10 containers, 1:06, data intact        |

Cold start is roughly a minute, most of it Grafana installing plugins. The point of those
commands is that the result is re-verifiable at any time — run them and check.

## Source documents

* [`docs/runbooks/local-development.md`](https://github.com/jonhill90/Hill90/blob/main/docs/runbooks/local-development.md)
  — the full runbook, local URLs, and what differs from production
