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 →
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
Free & open source for solo use — no account needed.
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.
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.
macOS · Apple Silicon
Grove_1.4.2_aarch64.dmg
12.1 MB
macOS · Apple Silicon
grove-v1.4.2-aarch64-apple-darwin.tar.gz
6.9 MB
Linux · x86_64
grove-v1.4.2-x86_64-unknown-linux-gnu.tar.gz
7.7 MB
Linux · x86_64
Grove_1.4.2_amd64.deb
13.0 MB
Linux · x86_64
Grove-1.4.2-1.x86_64.rpm
13.0 MB
Linux · x86_64
Grove_1.4.2_amd64.AppImage
85.2 MB
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.
# 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
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
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
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.
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.
# 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
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.
Grove
- 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
Grove Pro
- 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
Your machine,
your dev environment.
Download Grove and serve your projects locally with HTTPS in minutes. Free, open source, and dependency-free.