Chapter 3 of 14

A Folder Becomes a Site

One command and every project in a folder has a URL. This chapter is about what just happened — because “it works with no configuration” is a claim worth taking apart before you rely on it.

The problem

Between a folder of code and a working local URL there are three questions, and the traditional answer to each is a file you must remember to edit.

  1. What is this project called? A line in /etc/hosts.
  2. Where does the server send its requests? A virtual host block.
  3. What kind of project is it? Whether the document root is the folder or public/, whether URLs rewrite to index.php — more config.

Three answers per project, none of them interesting, all of them yours to maintain forever.

Park: a folder of projects

grove park ~/Code

Every sub-folder is now a site. ~/Code/freddy is http://freddy.test. Create ~/Code/invoices tomorrow and http://invoices.test works immediately, with nothing run and nothing edited.

That last sentence is the one to notice. The site exists because the directory exists — there is no registry to fall out of sync, no step to forget at eleven at night. Park the folder you keep your work in, once, and the question “how do I set up a local URL” stops being a question you have.

cd ~/work/client-site
grove link                 # → client-site.test
grove link acme            # → acme.test

For projects that live outside your parked folder. grove list shows everything Grove serves, linked and parked together.

Why DNS, and not /etc/hosts

Grove answers DNS for your whole TLD. macOS is told once, in /etc/resolver/test, to ask Grove about anything ending in .test, and Grove answers 127.0.0.1 for all of it.

Compared with a hosts file, that buys four things:

  • New names cost nothing. No file, no sudo, no restart.
  • Subdomains work. api.freddy.test, tenant-one.freddy.test — multi-tenant apps and subdomain routing work locally without a hosts entry per tenant, which is impossible with the other approach.
  • It is visible. grove list and grove doctor can tell you what is being served and whether the resolver is in place. A hosts file is a system file that nothing in your project can see.
  • It is reversible. Stop Grove and your machine stops resolving .test — there is no residue to clean up in six months.

Use .test and nothing else, by the way. It is reserved by the IETF for exactly this and will never be bought by anyone. .dev was a popular choice until Google bought it and turned on enforced HTTPS, which broke a great many machines in an afternoon.

How it knows what your project is

The third question — what kind of project is this — Grove answers by looking. It detects Laravel, WordPress, plain PHP, static sites and proxies, and serves each the way that kind of project expects: the right document root, the right rewrite rules.

Detection is a default, not a decree. When you are running something Grove has never seen — a Node app on a port, a container — say so:

grove proxy dashboard http://127.0.0.1:3000

Now dashboard.test is your Node app, with the same DNS and, from the next chapter, the same trusted certificate as everything else. This is also how a Docker or OrbStack container becomes a proper local site.

Try it now. Park your projects folder and run grove list. Everything you have been meaning to set up a local URL for has one, including the projects you had forgotten were there.

What you learned

  • Park a folder and every sub-folder is a site — the URL exists because the directory does.
  • Link is for projects that live elsewhere, and both show up in grove list.
  • A real resolver gives you wildcards, subdomains and reversibility that a hosts file cannot.
  • Use .test. It is reserved for this; .dev was bought and broke everyone using it.
  • Project type is detected, not configured — and grove proxy is the override for anything it has not seen.
Next: in Chapter 4 HTTPS — why local development needs it now, and how a certificate authority on your own machine can be safe.