Conjure a project
$ grim create-project shop
This is the first command most people run in earnest. Give it a name and you get a directory with a grim.json pinned to the current train, the modules and the theme you picked, and, once the install has run, core, the Docker setup and a database to go with them. It asks what goes in rather than handing you a fixed skeleton.
It runs the other way too. Pointed at an application that is already running, it reads what that application enables and writes the grim.json for it. See importing a running instance.
Usage
$ grim create-project shop # asks for modules and a theme
$ grim create-project shop --modules=cart,payments # modules named up front
$ grim create-project shop --preset=minimal --no-install # grim.json only, install later
$ grim create-project --from-instance=~/Sites/shopRun it from the directory that should hold the project. The name is lowercase kebab-case. It becomes the directory, the name in grim.json and the local domain, so shop answers at https://shop.test. An existing directory of that name stops it: Directory shop already exists.
Arguments and options
Create a new GRIM project interactively
Usage
grim create-project [--from-instance FROM-INSTANCE] [--host HOST] [--preset PRESET] [--modules MODULES] [--refresh-registry] [--new-theme NEW-THEME] [--theme-public] [--theme-org THEME-ORG] [--no-install] [--no-enable] [--] [<name>]Arguments
| Argument | Description |
|---|---|
name |
Project name (lowercase, kebab-case). Optional with --from-instance, which takes the name from the deploy host. (optional) |
Options
| Option | Description |
|---|---|
--from-instance=FROM-INSTANCE |
Convert a running instance into a grim project: reads its .env, storage/config/modules-enabled.php and deploy.php. The instance is never written to. |
--host=HOST |
With --from-instance: which deploy.php host names the project (default: the host matching the instance directory name) |
--preset=PRESET |
Non-interactive: all | minimal | custom (custom still drops into multi-select) |
--modules=MODULES |
Non-interactive modules list (comma-separated kebab names) — implies --preset=custom |
--refresh-registry |
Force refetch of the grimoiry/grim-* package list (ignore 24 h cache) |
--new-theme=NEW-THEME |
Slug for a brand-new frontend theme (scaffolded from blank template + published as grimoiry/grim-theme-{slug}) |
--theme-public |
Create the new theme GitHub repo as public instead of private |
--theme-org=THEME-ORG |
Create the new theme repo under an org instead of the authenticated user |
--no-install |
Skip automatic grim install after creation (always the case with --from-instance) |
--no-enable |
Skip enabling modules after install (propagated to grim install) |
What it actually does
- Checks Docker and the registry login before asking anything. The daemon has to run; in a terminal grim offers to start Docker Desktop and wait up to 60 seconds. Docker also has to be able to read the base image on
ghcr.io; when it is not logged in, grim offers to rungrim auth:loginon the spot.--no-installskips this step. - Asks what goes in. First a module preset:
minimalfor no modules, which is the default,allfor every module in the registry that has a release, orcustomto pick from a list. Then the frontend theme:blank, an existing theme by name, or a new one.--presetand--modulesanswer the first question,--new-themethe second. - Writes
grim.jsonand.gitignore. The name,coreconstrained to the current train, the modules underrequiresand both theme slots on the same constraint, Horizon and the scheduler switched on and Reverb off, and adeployblock with a health check. The.gitignoretracks itself,grim.jsonandgrim.lock, and nothing else. It prints✓ Created shop/grim.json. - Scaffolds the new theme, when you asked for one. This is
grim make:themerun inside the project: a copy ofblank, published as a repository of its own.--theme-publicand--theme-orgare handed to it. - Runs
grim installin the new directory. Core, the modules and themes, the Docker files,grim.lock, the local database, migrations and an admin user all come from there.--no-installstops before this step and prints what to run next.--no-enableis handed on to the install. - Registers the domain. On Linux and WSL2 it adds
shop.testandmail.shop.testto/etc/hoststhroughsudo. On WSL2 it then adds the same to the Windows hosts file, behind a UAC prompt. On macOS the resolver thatgrim setupwrote only points.testat your machine. Where Herd, Valet or dnsmasq answers there, nothing more happens. On a Mac where the name does not resolve, the same line goes into/etc/hoststhroughsudo.
Anything left out in step 2 can be added later with grim require.
On a machine that has never run grim, run grim auth:login and grim setup first. The module list comes from a registry that needs your token, and the .test domain needs the certificate authority and the DNS rule that setup puts in place.
Importing a running instance
Moving an application that already runs onto grim: point the command at the instance directory, read the dry run, point .env at a new database, then install. A conversion never installs on its own, with or without --no-install.
$ grim create-project --from-instance=~/Sites/shop
$ cd shop
$ grim install --dry-run
$ grim installIt reads three files of the instance and writes to none of them. The new project lands in a new directory under the one you are in.
| Instance file | What it becomes |
|---|---|
.env |
description from APP_NAME, the theme slots from THEME_* and THEMES_*_AVAILABLE, and deploy.domain from APP_URL when deploy.php names no domain. The file is then copied into the project unchanged. |
storage/config/modules-enabled.php |
requires: every enabled module except the ones core ships, each pinned to the current train. |
deploy.php |
The project name and deploy.domain from the matching host() block, and deploy.environments when that host has a staging counterpart. |
The name is the deploy host, not the directory. ~/Sites/shop3 deployed as host('shop') becomes the project shop. When deploy.php lists several hosts and none carries the directory's name, the run stops, lists the hosts it found and asks for --host=<name>. A name argument changes only the directory that is created.
Two kinds of finding are reported. A module the instance enables that now ships with core is a warning, Cart is core, omitted from requires, and is left out. A module with no package is an error, and all of them are listed before anything is written: Module Foo (foo) has no package — it is not in the registry, or archived, or private without access. Without a stored token the modules are not checked at all, and a warning says so.
In the dry run, read the section headed Resolved but not in grim.json. It lists what the install would pull in that the instance never asked for. The conversion is meant to run once; from then on grim.json is the source of truth.
Pitfalls
The converted .env still names the old database
--from-instance copies .env unchanged, so DB_* points at the instance's database. grim install runs php artisan migrate --force and creates an admin in whatever DB_* names. That is why the conversion stops before the install and prints a warning. Load a dump into a new database and set DB_DATABASE before you run grim install. Import an existing app walks through it.
--modules does not make it silent
The theme question is still asked. In a script without a terminal, add --no-interaction and every question takes its default, which for the theme is blank.
A new theme that could not be published stays local
⚠ Theme "boulder" scaffolded locally only — install will resolve it from disk. Publish to GitHub later to share with the team. means the theme directory exists and the GitHub part failed. The project installs and runs. A colleague who clones it cannot install until the theme is published.
On Windows, where you start decides how fast the app is
Started from a directory on the Windows drive, the command first offers to create the project inside the WSL2 filesystem instead. Take the offer. A project on the Windows side is reached by Docker through a slow bridge, and every request pays for it.
The local database needs a passwordless root
The install creates the project's database with the mysql client, as root with no password. When that login does not work, the step is passed over without a word and no database is made. grim doctor, run outside a project, tells you whether the login works before you start.
After it finishes
The install ends with a summary: project, core version, module and theme counts, and the admin login it created, with a generated password. Note the password down. Then bring the environment up.
$ cd shop && grim upTraefik takes ports 80 and 443. If something else on the machine already has them, this is where you find out.
Questions
Can I change the modules afterwards?
Yes. grim require adds a module and grim remove takes one out. Nothing chosen here is final.
Why does the .gitignore ignore almost everything?
The project repository is a prescription, not a checkout. git clone followed by grim install rebuilds the rest from grim.json and grim.lock. Add a line for each file the project itself owns.
Related spells
grim install
Assemble the whole project
Turn grim.json into a working project by fetching core, every module and theme, writing grim.lock, building the image and starting the stack.
grim up
Raise the stack
Start the project's containers and its Vite dev server, with a trusted certificate and a working address, and say where to open it.
grim require
Summon a module or theme
Add a module or theme to grim.json, install it with everything it depends on, and rewrite grim.lock, all inside the train the project is on.
grim make:theme
Weave a new theme
Scaffold a frontend or admin theme in the project, with its layouts and entry points, and publish it to a GitHub repository of its own.
grim setup
Prepare the machine
Put the shared pieces of local development in place, once per machine: the Traefik proxy, a trusted local certificate authority and .test name resolution.
grim auth:login
Log grim in to GitHub
Give grim a GitHub token, from your gh login or a personal access token, and hand the same token to Composer and Docker.
Recipes that use it
Create a project
Answer two questions, let the install run, bring the stack up, sign in as the generated admin, run the suite once and commit the three files that are the project.
Bring an existing app onto grim
Convert the instance into a grim.json without installing, read the dry run, settle the modules nobody asked for, point the copied .env at this machine, install, move your own files in, then test and commit.