Skip to content

Liber Tertius · Chapter 1 · Getting grim

Install on macOS

The installer brings grim and the tools around it, but not the shared plumbing every project leans on. That is a second command, and its last line says "ready" whatever happened. So the procedure ends with the one command that tells the truth about the machine.

≈8 min 5 steps Docker Desktop or OrbStack running A GitHub account with access to grimoiry
Source

Before you start

Docker has to be installed and running. The installer does not bring it, and everything after the installer talks to it. Docker Desktop and OrbStack both do, as long as docker is on your PATH.

PHP you do not need, nor Composer. grim is a PHP program that runs on your machine, not in a container, and it brings its own PHP: one self-contained PHP 8.5 binary in ~/.grim/runtime, pinned by grim's release and checked against its sha256, off your PATH. The grim command is a small script that runs grim on that PHP. A php you already have is neither used nor touched, and every project runs its own PHP, and Composer, in Docker.

$ docker info --format '{{.ServerVersion}}'

Homebrew is not required, but have it. With Homebrew the installer adds mkcert, MariaDB and Node by itself. Without it, MariaDB is left to you.

You will be asked for a classic GitHub token. The installer prints a link with the scopes already selected. A fine-grained token is accepted by GitHub and still cannot pull images.

The ritual, in order

Five steps. The first and the third change the machine, the other three only look.

  1. Run the installer

    One line downloads grim and its PHP into ~/.grim, puts the grim command into ~/.local/bin and fills in what is missing around it: mkcert and its certificate authority, MariaDB, Node and pnpm. Expect the macOS password prompt once, when the authority goes into your keychain.

    It starts with a checklist of the three parts you can leave out: MariaDB, Node.js + pnpm and mkcert. All are ticked; arrows move, space unticks, Enter goes on. Whatever you leave out is listed at the end with what that means.

    Components to set up (arrows move, space toggles, Enter confirms):
      > [x] MariaDB
        [x] Node.js + pnpm
        [x] mkcert

    Without a terminal (CI, a provisioning script, GRIM_NONINTERACTIVE=1) there is no checklist and everything is installed. The options --skip-mariadb, --skip-node and --skip-mkcert answer the checklist up front; they go after bash -s --, and each has an environment twin, GRIM_SKIP_MARIADB=1 and so on. --dry-run shows the choice and stops.

    A database of your own elsewhere, or none yet? Leave MariaDB out. Projects still install without a database: grim install skips migrations and the admin, says so in yellow, and a second grim install finishes them once .env names a login that works.

    $ curl -sS https://grim.grimoiry.com/install.sh | bash -s -- --skip-mariadb
    $ curl -sS https://grim.grimoiry.com/install.sh | bash
    
    GRIM CLI Installer
    
    …
    Downloading grim's own PHP 8.5.8 (macos-aarch64, one self-contained binary, ~15 MB)...
    PHP runtime 8.5.8 installed to ~/.grim/runtime
    …
    Generate the token on GitHub, then paste it here (it stays visible — keep it out of screenshots).
    Token:
    …
    Next steps:
      grim doctor            Check system dependencies

    Green lines are things that were found or installed. Yellow lines are things the installer could not do, each with the command to run yourself. It goes on past them, so read them now. The pasted token stays visible on screen, so keep it off a shared screen and out of screenshots. The token is saved to ~/.grim/config.json, and the installer finishes by running grim auth:login with it, which writes it to Composer's global auth.json and logs Docker in to ghcr.io.

  2. Open a fresh terminal

    The grim command lives in ~/.local/bin, grim itself and its PHP in ~/.grim. When ~/.local/bin was not on your PATH, the installer added it to the profile of your login shell — ~/.zshrc for zsh, ~/.bash_profile for bash — in a block marked # >>> grim-cli PATH, and said so in a yellow line at the end. It cannot change the shell it was piped into, so in that one grim is still command not found. Open a new terminal and ask who grim thinks you are.

    $ grim auth:whoami

    Authenticated as and your GitHub name mean the token arrived. command not found in a new terminal means your shell reads a different profile than the one named at the end of the install; copy the marked block there.

  3. Prepare the machine

    Every project answers at https://<name>.test. One Traefik routes all of them and one rule sends .test to your own machine. grim setup puts both in place, once per machine. It asks for sudo once, to write /etc/resolver/test.

    $ grim setup
    Setting up local dev environment...
    
      Creating traefik-public network... OK
      Checking mkcert... installed
      Installing mkcert CA... OK
      SSL certs will be generated per-project on grim up
      Setting up Traefik... running
      Setting up *.test DNS... needs sudo
      Creating /etc/resolver/test (routes *.test to 127.0.0.1)
      DNS configured
    
    Local dev environment ready.

    Read the indented lines, not the last one. Local dev environment ready. is printed, with exit code 0, even when a line above it says failed. The reason is on the line under the failed one.

    Laravel Herd's nginx holds ports 80 and 443. While it does, the Traefik line says failed and names Herd. Run grim setup --stop-herd to have setup stop it, or herd stop yourself. grim up behaves the same later, with the same flag. Nothing stops Herd without being asked. Your Herd sites are down until you start Herd again, and then grim's are.

  4. Check that a .test name resolves

    The resolver file sends every .test lookup to 127.0.0.1. grim starts nothing that answers DNS there. On a Mac that has Herd, Valet or dnsmasq, something already does. On a clean Mac, nothing does. Find out now, not in the browser.

    $ dscacheutil -q host -a name shop.test
    name: shop.test
    ip_address: 127.0.0.1

    An address means something on the Mac answers for .test. No output means nothing does. That is fine too: grim create-project and grim up then add the project's line to /etc/hosts themselves, and sudo asks for your password when they do.

  5. Ask the doctor

    grim doctor checks the tools, the Docker daemon, the ports, the database login and the token, and gives one verdict.

    $ grim doctor
    …
      Docker daemon    ✓ running
      Traefik ports    ✓ 80, 443 bound
      Composer         ✓ 2.8.4
      MySQL/MariaDB    ✓ 11.4.2 (root@127.0.0.1, no password)
    …
      GitHub auth       ✓ (anna)
      grim.json          - (not found — not in a GRIM project directory)
    
    All checks passed!

    A - is something absent, not something wrong. Outside a project the two manifest lines are always -. The MySQL/MariaDB line matters more than its mark suggests: a - there does not fail the run, but the first project will get no database. Settle it before you go on.

What you have now

~/.local/bin/grim, grim.phar     the tool, updated in place by grim self-update
~/.grim/config.json              your token and the telemetry choice
~/.grim/install.log              what the installer did, for when you ask for help
~/.grim/traefik/                 the shared proxy and, later, one certificate per project
/etc/resolver/test               .test goes to 127.0.0.1

There is also a MariaDB on the machine, outside Docker, that accepts root without a password so the containers can reach it. Keep port 3306 closed to any network you do not trust.

No project yet. That is the next recipe, Create a project. To take a newer grim later, run grim self-update.

When it does not work

“The PHP runtime download does not match its pin”

The PHP that grim's release names came down different from what the release pinned, so nothing was installed. A proxy that rewrites downloads does that, and so would a changed file at dl.static-php.dev. Run the installer again on another network; if it says the same, report it, because every install is affected until a new grim pins the new file. A download that fails outright (no network to dl.static-php.dev) says Failed to download the PHP runtime, and running the installer again is all it takes. It is safe to repeat: everything it finds in place is left alone.

“GitHub token is required. Installation cannot continue without it.”

The prompt offers Press Enter to skip, and skipping ends the install with this message. Every package is private, so there is no install without a token. Create the classic token from the printed link and run the installer again.

“MariaDB/MySQL not reachable on 127.0.0.1:3306 and Homebrew is not installed.”

Without Homebrew the installer has no way to install a database server. Install Homebrew and run the installer again, or point any MySQL or MariaDB you already run at port 3306 with a passwordless root. To finish the install first and sort the database later, run it with --skip-mariadb. The check doctor makes is this one:

$ mysql -u root -h 127.0.0.1 -e "SELECT 1"

Doctor runs the mysql client from your machine. A server without the client on your PATH still shows as not reachable.

A .test name does not resolve

The resolver rule is in place, nothing on the Mac answers it, and the hosts line is missing: the sudo prompt was declined, or the run had no terminal. Add the project's names to /etc/hosts, which is read first, or run grim up again in a terminal.

$ echo '127.0.0.1 shop.test mail.shop.test' | sudo tee -a /etc/hosts

“Port(s) 80, 443 are held by a non-Docker process.”

It appears under Setting up Traefik... failed. A local nginx or Apache is listening. Stop it, run grim setup again, and read the Traefik line.

The keychain prompt was dismissed

mkcert -install failed. Re-run manually later: mkcert -install means the authority was never trusted, and browsers will warn on every .test page. Run mkcert -install, answer the prompt, and restart the browser.