Skip to content

Raise the stack

$ grim up

The start of a working day, the first run after a clone, the morning after a reboot. grim up is what you type when the project should answer in a browser and does not yet. It is safe to run on a project that is already up: containers that are running are left as they are, and only what is missing gets started.

That same habit makes it the wrong command after an edit to .env. A running container keeps the values it started with. For that there is grim reload.

Usage

$ grim up                  # everything, Vite included
$ grim up --build          # rebuild the app image first
$ grim up app redis        # only these services
$ grim up --no-vite        # containers only

Run it from the project directory or anywhere below it. Docker has to be running, and on your own machine mkcert has to be installed.

Arguments and options

Start Docker services + Vite dev server

Usage

grim up [-b|--build] [--no-vite] [-t|--timeout TIMEOUT] [--] [<services>...]

Arguments

Argument Description
services Specific services to start (optional, repeatable)

Options

Option Description
-b, --build Rebuild images before starting
--no-vite Skip starting Vite dev server
-t, --timeout=TIMEOUT Docker compose timeout in seconds

What it actually does

On your own machine, in this order. Each step is printed with its result and the time it took.

  1. Checks Traefik. The one Traefik that routes every project on the machine is started when it is not running, and ports 80 and 443 are checked. A running Laravel Herd holding those ports is stopped, and a line after the steps tells you so.
  2. Makes the certificate. mkcert issues one for shop.test and mail.shop.test into ~/.grim/traefik/certs, and Traefik is restarted to serve it. On later runs the certificate is checked and left alone when it is still good.
  3. Runs docker compose up -d, with --build and the service names when you gave them.
  4. Generates translations. php artisan lang:js in the app container. A failure here does not stop the run.
  5. Starts Vite on your machine, not in a container. See Vite on the host.
  6. Installs Composer dependencies, only when the app container has no vendor/autoload.php yet. On a fresh clone this is the long step.
  7. Runs php artisan storage:link.
  8. Copies vendor/ to the host, only when the project directory has none, so your editor can resolve classes.
  9. Probes port 443 on 127.0.0.1, then verifies the certificate chain end to end and prints TLS chain verified. When a link of the chain is broken it restarts or recreates Traefik on its own and says which.

The run ends with a table titled Development ready: the URL, Mailpit, Horizon when the project uses it, the Vite address and the duration.

A project with its own certificate, tls.mode set to custom in grim.json with tls.domain, tls.cert and tls.key, skips mkcert and the hosts entries. The certificate is copied to Traefik as it is, and docker-compose.yml is written again so the routed domain follows tls.domain.

Linux and Windows add steps of their own. On Linux, the line 127.0.0.1 shop.test mail.shop.test is added to /etc/hosts before anything else, and sudo may ask for your password. A Mac where shop.test does not resolve gets the same line; one with Herd, Valet or dnsmasq is left alone. On Windows the same entries go into the Windows hosts file. Both also make storage/ and bootstrap/cache/ writable inside the container.

Vite on the host

Locally the dev server is a process on your machine, started with yarn dev and left running in the background. It is skipped when the project has no package.json or when you pass --no-vite.

File What it holds
.grim/vite.pid The process id grim down uses to stop it.
.grim/vite.log Everything Vite printed. Read this when assets do not load.
public/hot The dev server address. While it exists the app loads assets from Vite.
.env Three VITE_DEVELOPMENT_* lines, appended once, pointing Vite at the project's certificate.

When node_modules is missing, yarn install runs first. A Vite left over from an earlier run of the same project is stopped before the new one starts.

On a shared dev box

A project whose grim.json says deployment.kind is vps-dev takes a different and shorter road. There is no mkcert, no hosts file and no Vite process: certificates come from the box's Traefik, and MySQL and Vite are services in the stack.

Your machine Shared dev box
docker-compose.yml Left as it is Written again from the template on every run
Database On the machine, outside the stack A mysql container
Vite A process on the host A vite container
Migrations Not run php artisan migrate --force on every run
Address https://shop.test deployment.domain from grim.json

It checks that the traefik-public network exists before starting anything, and ends with a table titled Dev environment ready. The box itself is raised by grim server:dev:setup.

Pitfalls

Without yarn on the host, Vite is skipped without a word

No step fails and nothing is printed. The sign is a summary table with no Vite HMR row, and pages that load built assets or none. Install Node and yarn, then run grim up again.

Something else holds port 80 or 443

Port(s) 80, 443 are held by a non-Docker process. Free them before running grim (e.g. stop the conflicting service). Herd is stopped for you; any other web server is not. Stop it and run grim up again.

mkcert is missing

The certificate step fails with mkcert is not installed or not on PATH and the install command for your system. No project container has been started at that point. Install it and run grim up again; the first run also adds the local authority to the system trust store.

The first start times out

docker compose up gets 300 seconds when the app image has to be built and 120 when it exists. On a slow connection the first build can need more. Pass --timeout=600, or set docker.timeout in grim.json to keep it.

On your own machine grim up does not run migrations. A fresh clone with an empty database answers with an SQL error until you run grim artisan migrate.

After it finishes

Open the URL from the table. Mail the app sends lands in Mailpit at https://mail.shop.test. When a page errors, the next stop is the log.

$ grim logs -f app

At the end of the day, grim down stops it all and keeps your data.

Questions

Can two projects be up at once?

Yes. Each has its own containers and its own .test name, and the one Traefik routes to both.

Does grim up app still start Vite?

Yes. Service names only narrow what docker compose up starts. Add --no-vite when you want containers and nothing else.

Recipes that use it