v1.4.2 · stable
Elyra Grove · The Local Environment

Local development,
nothing to install around it .

Grove serves *.test domains with automatic routing, local HTTPS, bundled multi-version PHP and Node, bundled databases and a mail-catcher — from a single Rust core. No Homebrew, no Composer, no dnsmasq.

Responses and uploads stream, in both directions: a Server-Sent Events endpoint or an AI token stream arrives token by token instead of landing all at once when PHP closes the request, and a large upload no longer sits in memory while it is forwarded.

Coming from Herd? Migrate in a few minutes →

Grove Pro · Teams
$99 per seat / year

The free core stays free. Pro adds shared, synced team infrastructure on top of your local environment.

  • End-to-end encrypted team secret sync
  • Reproducible team environments — clone to running in one command
  • License activation, priority support & commercial license
See Grove Pro

Free & open source for solo use — no account needed.

The Grove dashboard — sites with local HTTPS and public tunnels
Download · v1.4.2

Download Grove

Native installers for Apple Silicon macOS and Linux, or build from source. Intel Macs are no longer shipped as a build — notarization on the Intel runner hangs too often to release from — but cargo build --release still works there.

All releases

On 1.4.0 or 1.4.1? Upgrade now — those builds serve nothing.

Every connection was reset, on every site: ERR_CONNECTION_RESET and no page at all. The header-read timeout added in 1.4.0 was configured without giving hyper a timer, and hyper neither falls back to a default nor complains at setup — it panics the first time a connection reaches that code, which is every connection. 1.4.2 installs the timer.

The daemon reported itself healthy the whole time. grove status and the GUI both said Running while not one site rendered, because 1.4.0 also stopped a panic from killing the process. That change was right and we said so on this page — it is also what turned a total outage into a quiet one. Under the previous behaviour the first request would have taken the daemon down and the fault would have been obvious.

On 1.3.0 or earlier? Update.

1.3.1 closes two disclosures in the request path. A plain PHP project's document root is the project root, so /.env was served in full — APP_KEY included — and any existing .php file was handed back as source rather than executed, disclosing the front controller. Locally that is bad enough; with grove share both were reachable from the internet.

Dot-prefixed paths are now refused outright (.well-known/ excepted, so ACME still works) and .php goes to PHP-FPM as nginx and Apache do. If you shared a site from an older build, rotate anything that was in that .env.

S.01 — What it does

One Rust core,
everything bundled.

DNS, reverse proxy, FastCGI and TLS are built in. PHP, Node, databases and mail are downloaded and supervised by Grove itself — no Homebrew, Composer, dnsmasq or OpenSSL required.

Automatic *.test routing

An embedded DNS resolver serves your *.test domains — no manual hosts-file editing, no dnsmasq.

Local HTTPS

A private root CA with on-demand per-site leaf certificates, so every site gets trusted TLS locally. Through 1.3.2 that CA was re-minted every time it was read: loading it from disk parsed the PEM and called self_signed again, so cert_pem() reported a certificate with a new serial and a new validity window — neither the file on disk nor the one the OS trust store had been told to trust. Leaf certificates still chained, because the name and key matched, which is why it survived as long as it did. From 1.4.0 the CA on disk is used as it is.

Bundled PHP

Install multiple self-contained PHP versions (grove php install 8.5 | 8.4 | 8.3), with per-site isolate and lazy FPM pools.

Bundled Node.js

Download node, npm and npx with per-site Node versions — no nvm or Homebrew.

Bundled services

Grove downloads and supervises PostgreSQL, MySQL and Redis itself — no separate database or cache to install.

Built-in mail-catcher

An SMTP server captures outgoing mail, with a Mailpit-style viewer to inspect it.

Reproducible environments

Commit a grove.toml and grove up brings the whole project environment — PHP/Node versions, services, dev processes — to a known-good state on any machine.

The app declares, Grove supervises

grove dev used to guess your dev processes — a hardcoded Vite server and queue worker, blind to Reverb, Horizon or stripe listen, and assuming npm in a bun project. On Laravel 13.16+ it now reads artisan dev:list and supervises exactly what the application declares, each with its own log file, autostarted at boot with no terminal left open. It skips the processes Grove already is (it serves the site over FPM and tails the log itself) and excludes vendor-registered ones, so a Composer package cannot start processes inside the daemon. Vite still gets trusted local HTTPS automatically.

Database client

A built-in database browser auto-connects from your .env — browse tables and run queries free; the Pro schema inspector adds row editing, with a guard that blocks edits against production.

Database time-travel

grove db takes point-in-time snapshots of Grove’s bundled databases and restores them instantly — undo a bad migration or seed in seconds.

Request timeline & replay

Grove records a live timeline of requests across all your *.test sites — expand any request to see headers and body, and replay it with one click (or grove replay) while you fix the code.

Local webhook hub

Any request to /__grove/hooks/<bucket> is captured and acknowledged — a local webhook.site. Expose it with grove share, point Stripe/GitHub at it, inspect each delivery and re-deliver it to your app.

Turn a request into a test

From any captured request or webhook, copy it as a curl command, a .http file, or a Pest feature test — turn a failing request into a regression test in one click. The timeline keeps at most 1 MiB of a body, and every export now says so when it applies: a partial body used to be exported as if it were complete, so the app's puzzled response looked like a bug in your code rather than Grove's capture limit.

Environment bundles

grove bundle export packages a project's grove.toml, .env and database into one shareable file; grove bundle import brings it up and loads the database — reproducible dev environments without Docker.

AI tools (MCP)

grove mcp runs a local-only Model Context Protocol server that exposes your sites, request timeline, webhooks, logs and database schema to AI clients like Claude and Cursor. Read-only by default; opt-in agent-safe write tools (grove_migrate_sandboxed, grove_sql_sandboxed) appear only with --allow-write and run inside an automatic snapshot sandbox — each returns its blast radius (schema diff + causal chain) so you can see exactly what a write did before keeping it.

Causal chain & explain

grove_request_chain correlates a captured request with the SQL, logs and errors Grove observed while handling it; grove explain <id> (and grove_explain) curate a full debugging bundle for one request — so an agent gets the whole story, not just the stack trace.

Docker & OrbStack sites

Grove auto-discovers running containers and Compose projects and serves them as name.test with trusted local HTTPS — 🐳 in the same dashboard, with start/stop/restart controls and grove share tunnelling.

Public tunnels

Expose a local *.test site at a public URL with grove share — a native, self-hostable ngrok/Expose alternative. Zero-config via grove.elyracode.com.

Request inspector

A live table of recent tunnelled requests (method, path, status, duration) — ideal for debugging webhooks, in the GUI and the terminal. For a streaming response the duration column is time to headers, not total time, so a 16-second SSE stream reads as a few milliseconds.

And nothing is done twice

The sequel to that, and the more ordinary kind of waste. A proxied request built a new HTTP client per request — and a client is the connection pool, so every asset from a Vite dev server paid a fresh TCP handshake. Static files carried nothing to revalidate against, so an unchanged asset was re-read and re-sent on every reload; they now carry an ETag from size and mtime, needing no extra read, and answer If-None-Match with 304. DNS answers went out with TTL 0, which forbids caching, so the system resolver asked Grove again for every single connection — with mDNSResponder in that path on macOS, on every first byte. It is cached for five minutes now, the answer being always loopback. And the release profile optimised for binary size, which is the one thing that does not matter for a daemon on the request path.

One bad request stops being everybody's problem

The release build used panic = "abort", so a single unwrap on a poisoned mutex — in one request, for one site — took down the daemon and with it DNS, TLS and every other site you had open. Panics unwind now, so a failure stays inside the task that caused it. Two more of the same kind: the accept loop answered failure with a bare continue, which under EMFILE fails again immediately and forever, burning a core and never recovering — it backs off from 5 ms to 1 s now, which also gives descriptors time to be released. And neither the TLS handshake nor the wait for request headers had a deadline, so a crashed browser or a port scanner could hold a task and a file descriptor for as long as it liked. Worth knowing what that first change cost on the way in: the header deadline was configured without giving hyper a timer, so 1.4.0 and 1.4.1 panicked on every connection and served nothing — and because panics no longer killed the process, the daemon stayed up reporting itself healthy while no site rendered. Fixed in 1.4.2. Making a failure survivable also makes it quieter, and quiet is the harder half.

Nothing is buffered whole, either direction

Responses used to accumulate until PHP closed the request, so an SSE endpoint delivered nothing until it ended and a 2 GB download cost 2 GB of memory. Uploads were worse: the body was collected before Grove had even resolved which site the request belonged to, and then held in triplicate — 400 MB of upload cost 1.2 GB of RSS, now about 3 MB. Headers go out as soon as PHP flushes them, and request bodies stream into FastCGI with CONTENT_LENGTH taken from the body. A chunked upload declares no length and CGI insists on one, so Grove measures it — in memory to 1 MiB, then a 0600 spool file inside a 0700 directory, removed on Drop so neither an error nor a disconnect leaves upload contents on disk. Refusing chunked requests with 411 would have made Grove the reason a valid request fails; nginx and Apache spool, so Grove spools. Bodies beyond 2 GiB are refused with 413.

Hide & restore sites

grove forget removes a site from the list without deleting its files; grove restore brings it back.

Driver system

First-class drivers for Laravel, WordPress, generic PHP, static sites, and reverse proxy (Vite/Node).

GUI + CLI in parity

Both are thin clients over the same daemon, plus a macOS menu-bar icon. Built with Tauri 2 + Svelte 5.

Zero external dependencies

DNS, proxy, FastCGI and TLS are all built into the Rust core. Nothing to install around it.

shell
# Share a local site publicly — no config needed
$ grove share myapp
 https://quiet-river-4821.grove.elyracode.com
  → myapp.test (HTTPS, streaming)

# Live request inspector for webhooks
12:04:31  POST  /webhooks/stripe  200  142ms
12:04:33  POST  /webhooks/stripe  200   88ms
New in 0.2

Share localhost,
without ngrok.

grove share exposes any local *.test site at a public HTTPS URL for demos, real-device testing and webhooks — a native, self-hostable alternative to Expose/ngrok, built right in.

  • Zero-config via grove.elyracode.com — works out of the box
  • Self-host your own server with the included deployment kit
  • Live request inspector for debugging webhooks
  • Streaming bodies proxied end-to-end over a single connection
Public tunnels guide
Coming soon In development

Build it. Run it. Store it.
Ship it.

There is one place the ecosystem still asks you to leave it: getting code from your machine onto a server. Grove already provisions a whole environment locally, and Askr already reloads without dropping a request — rolling one worker at a time, and aborting the whole reload if the first one looks unhealthy.

grove deploy is the missing middle: immutable releases, an atomically swapped current symlink, migrations run before the swap, and a rollback that is one command — or automatic, when Askr's canary says no.

Deploying to your own box

# from your machine, to your server
grove deploy --dry-run
2 migrations pending · e4f5a6b
grove deploy
upload · build · migrate · swap
canary healthy — rolling the rest
grove deploy rollback

No hosted control plane, no third party in the path — your machine to your box. The design is public while it is being built: read the deploy contract, or the honest state of the whole stack on the architecture page.

Not shipping yet. Nothing above is in Grove today — the commands are the agreed design, not a changelog. When it lands, this section becomes a feature and the headline on the front page gains a fourth verb.

Up in minutes

Park a folder,
open the browser.

Point Grove at a directory of projects and every subfolder is served at name.test with HTTPS — or import an existing Valet setup.

CLI reference
shell
# Install a PHP version
$ grove php install 8.5

# Serve every project in a folder at *.test
$ grove park ~/Code

# Or link a single site
$ cd ~/Code/app && grove link
 https://app.test
Pricing

Free for solo. Pro for teams.

Everything you need for local development is free and open source, forever. Grove Pro adds shared, synced team infrastructure.

Open source

Grove

Free MIT-licensed
  • Automatic *.test routing with local HTTPS
  • Bundled PHP, Node, PostgreSQL, MySQL, Redis
  • Mail-catcher, public tunnels, request timeline
  • Database snapshots and the full toolchain on your PATH
Download Grove
Grove Pro · Teams

Grove Pro

$99 per seat / year
  • Everything in the free version
  • End-to-end encrypted team secret sync
  • Reproducible team environments — clone to running in one command
  • One-command onboarding and drift detection
  • Priority support and commercial license
Total $ / year

No account needed to buy — we email your license key and login after checkout.

Colophon — Get Grove

Your machine,
your dev environment.

Download Grove and serve your projects locally with HTTPS in minutes. Free, open source, and dependency-free.

Elyra Grove · v1.4.2 Built in Norway · MMXXVI