<p>Every Laravel dev has the same little ritual. Open the project. Split the terminal. <code>npm run dev</code> in one pane, <code>php artisan queue:work</code> in another, maybe <code>php artisan pail</code> for logs, and — if you're on the modern skeleton — <code>composer run dev</code> to juggle all of it at once. Then you actually start working. And when you switch projects, you do the whole dance again.</p><p>It's a small tax. But it's a daily one, and it's the kind of thing your local environment should just handle. So Grove learned to.</p><p>In 0.5, Grove runs your site's long-running dev processes for you — the Vite dev server and a queue worker — with one click, over trusted HTTPS, with the logs right in the dashboard. No terminal panes to babysit.</p><h2>Why: Grove already serves the app</h2><p>Here's the insight that makes this clean. <code>composer run dev</code> actually starts four things via <code>concurrently</code>:</p><pre><code class="language-bash">php artisan serve       # a dev web server
php artisan queue:listen
php artisan pail         # tail logs
npm run dev             # Vite / HMR
</code></pre><p>But under Grove, the web server part is redundant — Grove already serves your app on <code>https://myapp.test</code> via its own FastCGI + FPM. And <code>pail</code>? Grove has a Logs panel. So the only things actually left to run are Vite and, if your project has a real queue, a worker.</p><p>That's exactly what Grove now supervises — nothing more, nothing you don't need.</p><h2>How: one toggle, or one command</h2><p>In the desktop app, every site in the Sites table has a ⚡ button:</p><pre><code class="language-text">🖥  Sites
┌───────────────────────────────────────────────────────────────┐
│ HOST              DRIVER    PHP   NODE   HTTPS   ACTIONS        │
│ elyra-web.test    laravel   8.4    —      🟢     ↗ ⚡ 🌍 📁 🗑 │  ← ⚡ = run dev
└───────────────────────────────────────────────────────────────┘
</code></pre><p>Click ⚡ and Grove starts Vite (and a queue worker if you have one), using that site's Node and PHP versions, running as you (not root), with output streamed to the Logs panel. Click again to stop.</p><p>Prefer the terminal? It's a Grove-aware <code>composer run dev</code>:</p><pre><code class="language-text">$ grove dev start elyra-web
✓ dev started for elyra-web: vite

$ grove dev list
● dev running: elyra-web
</code></pre><p>And in the logs:</p><pre><code class="language-text">  VITE v8.1.0  ready in 113 ms
  ➜  Network: https://elyra-web.test:5173/
</code></pre><p>Notice that <code>https</code>. Which brings us to the interesting part.</p><h2>The part that took some doing: Vite over trusted HTTPS</h2><p>Your site is served over HTTPS (<code>https://myapp.test</code>), so the Vite dev server has to be HTTPS too — otherwise the browser blocks the assets as mixed content, and you get a sad, unstyled page.</p><p>Grove handles this without you configuring anything, and — importantly — without borrowing any other tool's directories. When it starts Vite for an HTTPS site, it:</p><ol><li><p>Issues a leaf certificate for the host, signed by Grove's already-trusted local CA.</p></li><li><p>Hands it to Vite via the standard <code>VITE_DEV_SERVER_CERT</code> / <code>VITE_DEV_SERVER_KEY</code> env vars that <code>laravel-vite-plugin</code> reads natively.</p></li></ol><pre><code class="language-text">$ cat public/hot
https://elyra-web.test:5173

$ curl -sI https://elyra-web.test:5173/@vite/client | head -1
HTTP/1.1 200 OK          # trusted cert — no -k needed
</code></pre><p>That's the same mechanism Herd uses — Grove just uses its own paths. So a plain, standard <code>vite.config.js</code> works everywhere:</p><pre><code class="language-js">export default defineConfig({
    plugins: [
        laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true }),
        tailwindcss(),
    ],
});
</code></pre><p>No hard-coded cert paths, no tool lock-in. Portable between Grove and anything else.</p><h2>Two honest gotchas we ironed out</h2><p>Building this surfaced a couple of real bugs — the good kind, because fixing them makes the feature solid:</p><ul><li><p><strong>Orphaned dev servers.</strong> If the daemon restarted, its Vite children weren't cleaned up and kept squatting ports, so <code>public/hot</code> would drift to <code>:5174</code>, <code>:5176</code>… Grove now kills its dev processes on shutdown, so a restart always gives you a clean <code>:5173</code>.</p></li><li><p><strong>A strict Content-Security-Policy.</strong> A well-secured app can send a CSP so tight (<code>'self'</code>) that it blocks the Vite dev server — because the dev server, on its own port, is technically a different origin. <code>curl</code> doesn't care about CSP; your browser rightly does. The fix lives in the app: in local dev, allow the Vite origin. (Grove serves everything correctly; the browser was just enforcing the app's own rules.)</p></li></ul><p>Both are the sort of thing you only hit once — and now you won't.</p><h2>Getting it</h2><p>Grove auto-updates. After it relaunches, restart the background service once (Tools → Restart daemon), then:</p><pre><code class="language-text">$ grove dev start myapp
✓ dev started for myapp: vite, queue
</code></pre><p>Open the site, edit a component, watch it hot-reload. No terminal panes, no <code>artisan serve</code>, no remembering.</p><p>That's the whole theme of Grove: take the plumbing you shouldn't have to think about — DNS, HTTPS, PHP, databases, tunnels, containers, and now your dev processes — and quietly handle it, so you can get back to building. One click, and your app is live-reloading. 🌱🌳</p>