Skip to content

Liber Tertius · Chapter 2 · Starting a project

Create a project

One command does nearly all of it, and it takes minutes because it builds an image, installs core and migrates a database. What is left to you is small and easy to skip. Copy a password that is shown once, and know what the first commit holds before you share it.

≈10 min 6 steps A machine where grim doctor passes A database that accepts root without a password
Source

Before you start

The machine is ready: Docker runs, you are logged in, and grim doctor outside any project ends with All checks passed!. If this is a new machine, do the install recipe for your system first.

The install will create the project's database with the mysql client on your machine, as root with no password unless .env says otherwise. When that login does not work, the project is still created without a database and the summary names the login it tried; see When it does not work. Try the exact login now.

$ mysql -u root -e "SELECT 1"
$ cd ~/code

Stand in the directory that should hold the project. The name you pick becomes the directory, the name in grim.json, the database and the local domain. Use lowercase letters, digits and hyphens. grim does not reject other names, and a domain or a database will.

The ritual, in order

Six steps. The first does nearly all the work, the fourth is optional, and only the last touches git.

  1. Create it

    grim create-project first checks that Docker runs and can pull from ghcr.io, then asks two questions. Module preset? offers minimal, all and custom. Frontend theme? offers blank, an existing theme or a new one. Enter takes minimal and blank, and both can be changed later.

    $ grim create-project shop
    Creating project: shop
    …
    ✓ Created shop/grim.json
      0 modules, theme: blank
    
    Running grim install...
    …
      Project installed
      ───────────────────────────────────────
      Project       shop
      Core          2.5.0
      Modules       0
      Themes        2
      · · · · · · · · · · · · · · · · · · · ·
      Admin         admin@shop.test
      Password      3f9c1a7e5b2d8046
      · · · · · · · · · · · · · · · · · · · ·
      Next          cd shop
      Then          grim up

    Between the two halves, grim install prints one line per step with its duration: core, modules, themes, configuration, the lock, the database, the image build, the migrations, the admin. The first failure stops the run.

    The Password row is the only place the admin password is ever shown. Copy it before the terminal scrolls. When the two admin rows are missing, the account was not created, and step four makes one.

  2. Bring it up

    The install leaves the containers running. grim up adds what is still missing: the certificate for shop.test, and the Vite dev server on your machine.

    $ cd shop
    $ grim up
    …
      Development ready
      ───────────────────────────────────────
      URL           https://shop.test
      Mailpit       https://mail.shop.test
      Vite HMR      https://shop.test:5173 — logs: grim logs -f vite

    Look for TLS chain verified above the table. It means the certificate that Traefik serves is the one your browser will trust. On Linux and WSL2 expect a sudo prompt for /etc/hosts, and on WSL2 a UAC prompt on the Windows desktop.

  3. Sign in

    Open the login page and use the Admin and Password rows from step one.

    $ curl -sI https://shop.test/login | head -n 1
    $ open https://shop.test/login

    The first line of the answer is the status the app gave. Any mail the app sends lands in Mailpit at https://mail.shop.test, never in a real inbox. On Linux use xdg-open, and on WSL2 cmd.exe /c start.

  4. Make an account of your own

    Optional. admin@shop.test is a fine account for a project only you run. For a name of your own, or when the password from step one is gone, grim user creates another admin.

    $ grim user anna@example.com

    The summary is titled User created and carries a new generated password. User (check output above) means it failed. The exit code is 0 either way.

  5. Run the suite once

    grim test creates the database shop_test next to shop, migrates it and runs the suite. Run it now, while nothing of yours can be the reason for a failure.

    $ grim test

    A red suite on an untouched project is a finding about the train or the machine, not about your code.

  6. Share the first commit

    grim create-project ended with ✓ git repository created with the first commit. That commit holds the whole tree: core, every module and theme at the release grim.lock records, grim.json, the README and CLAUDE.md. What stays out is named in grim's block of .gitignore: vendor/, node_modules/, .env, storage/, build output, caches and the Docker files grim renders.

    $ git log --oneline
    $ git status --short
    $ git remote add origin git@github.com:acme/shop.git
    $ git push -u origin HEAD

    An empty git status is what the first grim update asks for. A colleague gets the project with git clone and grim install, which adds the dependencies, a .env and the Docker files. grim rewrites only the lines between # >>> grim and # <<< grim in .gitignore; lines you add outside the block are yours.

    When the summary says ⚠ No git repository created instead, it names the reason and the command to run by hand.

What you have now

shop/
  grim.json, grim.lock    what the project is made of, and at which versions
  .gitignore              grim's block of what stays out of git, and your own lines
  .env                    local settings, APP_KEY filled in, DB_HOST=host.docker.internal
  docker-compose.yml      yours to edit from now on
  docker/                 Dockerfiles and nginx config, renewed by grim upgrade
  src/<Module>/           one plain directory per module, committed with the rest
  resources/themes/       one plain directory per theme

On the machine: the database shop and shop_test, a certificate in ~/.grim/traefik/certs, and a stack that grim down stops and grim up starts. Modules come and go with grim require and grim remove.

When it does not work

“Not authenticated to ghcr.io.”

Docker cannot pull the base image. Answer yes to Run `grim auth:login` now to authenticate? and the run continues. When a 403 follows, the token lacks read:packages or is fine-grained. Make a classic one and run grim auth:login --token <new-pat>.

“Database unreachable as root@127.0.0.1:3306”

The Creating local database step logs in with the DB_* lines of .env, and a fresh .env says root without a password. The install does not stop there: the project, the image and the dependencies are complete, and only migrations, translations and the admin are skipped, which is why the summary has no Admin row. Put the login your server accepts into .env and run grim install again inside the project. Core on disk is left alone. Cannot create the database is the same case with a login that may connect but not create: create the database with one that may, as the message shows, and run the install again.

$ cd shop
$ # edit .env: DB_USERNAME=…, DB_PASSWORD=…, DB_PORT=… when not 3306
$ grim install

The containers reach MariaDB from Docker's network, not from localhost, so that user has to be allowed from there as well, for example as 'dev'@'%'. When the user may not create databases, create it yourself first. A project named my-shop gets the database my_shop.

$ mysql -u root -p -e "CREATE DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"

“Directory shop already exists.”

A failed run leaves its directory behind. When nothing in it is worth keeping, stop its containers with grim down inside it, delete it and start again. Otherwise go in and run grim install.

“Could not load module catalog”

You chose custom and the registry did not answer, which is nearly always the token. Run grim auth:login, or name the modules yourself with --modules=cart,payments.

The browser cannot find shop.test

On macOS nothing on the machine answers the .test resolver. On Linux the sudo prompt was declined. Add the line by hand, and on WSL2 also run grim hosts:windows.

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

Next recipe

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.

Read it

Spells used here