Work on a client's dev box
On a dev box the containers, Vite and the database run on the server, and your machine holds only an editor and a browser. You need no grim of your own for this. Everything that is grim runs on the box, in the project directory.
Before you start
Somebody raised the box and put the project on it. That person is the operator. Their machine is the only one that can manage keys and print the project's access card, which is why this recipe begins with a message to them. If that person is you, the other side is Set up a dev box for a client.
You have an SSH key pair. The private half never leaves your machine. When you have none, make one.
$ ls ~/.ssh/id_ed25519.pub
$ ssh-keygen -t ed25519 -C anna@example.comEveryone on the box is the same dev user, in the same working copy, against the same database. You share the checked-out branch with whoever else is on the project. Agree on who works where before you switch it.
The ritual, in order
Six steps. The first three are done once per machine. The fifth restarts the project for everyone on it.
Send your public key
The operator adds the key to the
devuser under a label with your name. Send the.pubfile, or its one line of text. Keep the fingerprint at hand.$ cat ~/.ssh/id_ed25519.pub ssh-ed25519 AAAA… anna@example.com $ ssh-keygen -lf ~/.ssh/id_ed25519.pub 256 SHA256:… anna@example.com (ED25519)The operator's
grim server:dev:keys listshows the sameSHA256:value next to your label. When the two differ, the wrong key went in. A public key is not a secret and can travel by chat or mail.Paste the SSH block
The operator sends back what
grim server:dev:ssh-configprinted on their machine. The command writes nothing anywhere and knows only boxes that were set up from the machine it runs on, so on yours it answersUnknown host "dev1".Paste the block at the end of~/.ssh/config.$ cat ~/.ssh/config Host dev1-dev HostName 203.0.113.10 User dev StrictHostKeyChecking accept-new ServerAliveInterval 30 ServerAliveCountMax 3The name is the operator's alias with
-devadded. The block names no key, so SSH offers your default ones. When the key you sent lives elsewhere, add a lineIdentityFile ~/.ssh/<key>underUser dev.Attach your editor
VS Code Remote-SSH reads its targets from the same file. Try the plain connection first, because its errors are easier to read than the editor's.
$ ssh dev1-dev $ code --remote ssh-remote+dev1-dev /home/dev/projects/shopThe first connection records the box's host key without asking. The editor installs its server part on the box once, then opens the project. The terminal inside it is a shell on the box, and that is where the rest of this recipe happens.
Find your way around the project
Every project lives under
/home/dev/projects, and grim is installed on the box. In the project directory the daily commands work as they do anywhere,grim logsandgrim shellamong them.$ cd /home/dev/projects/shop $ docker compose ps $ grim logs -f vite $ git status --short M grim.jsonThe source is mounted into the containers and Vite pushes changes to the browser, so saving a file is all a code change needs.
grim.jsonshows as modified on every dev box. The operator's tooling wrote adeploymentblock into it that tells grim this is a dev box. Do not commit that block and do not revert it.Reload after a change to .env
Containers read
.envwhen they are created, andgrim upleaves a running container alone.grim reloadrenders the compose file again, recreates every container and clears the config cache.$ grim reload Reloading stack ✓ [1/3] Regenerating docker-compose.yml from template ✓ [2/3] Recreating containers (--force-recreate) ✓ [3/3] Clearing config cacheEveryone on the project loses their open pages and hot-reload sockets for a moment. Say a word first. The database and Redis keep their data. The operator can do the same from their own machine with
grim server:dev:reload shop.Open the database
The project's MySQL is a container on a private network of the stack. No port is published on the box, so an SSH tunnel to port 3306 reaches nothing, whatever the access card says. Open the client inside the container. The container knows its own credentials.
$ docker compose exec mysql sh -c 'mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' $ docker compose exec -T mysql sh -c 'mysqldump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' > ~/shop.sqlThe first line is an interactive client. The second writes a dump into the
devuser's home on the box, andscp dev1-dev:shop.sql .on your machine brings it over. The variables are expanded inside the container, which is why the command is wrapped insh -cwith single quotes.
What you have now
On your machine, one block in ~/.ssh/config and nothing else. On the box, a shared working copy with a running stack.
/home/dev/projects/shop/ the code, mounted into the containers
.env read when containers are created
https://shop.dev.example.com the app
https://mail.shop.dev.example.com Mailpit, every mail the app sendsThe Vite dev server answers on the vite. name and the browser talks to it on its own. Mail never leaves the box. The exact URLs of your project are on the card the operator sent. A second project on the same box is only another path in step three.
When it does not work
“Permission denied (publickey)”
The box does not know the key SSH offered. Either the operator has not added it yet, or you sent one key and SSH offers another. Compare fingerprints as in step one, and name the right file with IdentityFile.
“Unknown host "dev1".”
You ran grim server:dev:ssh-config yourself. It reads the servers.json of the machine it runs on, and yours has no such box. The same goes for grim server:dev:info and grim server:dev:reload, which answer No dev environment registered for project "shop". Ask the operator for the block, and use grim reload on the box.
The app answers 400 “Untrusted Host” after you replaced .env
PHP in the container runs as another user than dev and can no longer read the file, so Laravel falls back to production rules and rejects every host. Make it readable and reload.
$ chmod 644 .env
$ grim reloadThe database password from the card is refused
The card shows what the operator's machine saved when the project was added, not what is in use. A project name with a dash is stored with an underscore, and a password printed by a repeated run was never set. Trust .env in the project directory, or use step six, which asks the container itself.
Questions
Whose name goes on my commits?
Nobody's until you say so. The account and the working copy are shared, so a git config there would speak for the whole team. Name yourself per commit with git -c user.name="Anna" -c user.email=anna@example.com commit.
How do I push from the box?
grim leaves the clone with a plain HTTPS remote and stores no git credentials for the dev user. Git asks for a user name and a token when you push. Use your own, and do not let a credential helper save them on a shared account.
Next recipe
Set up a dev box for a client
Raise a shared development server, give it a registry token, put the first project on it, let developers in by key, hand over the access card, and later take a developer or a project off again.
Read itSpells used here
grim server:dev:ssh-config
Write the SSH block
Print a ready ~/.ssh/config block for a dev box, so ssh and VS Code Remote-SSH reach it under one short name.
grim reload
Apply a changed .env
Recreate every container of the project and clear the config cache, so the app reads the .env as it is now.
grim logs
Read the logs
Show what the containers have been printing, for one service or all of them, on your machine or on a deployed server.
grim shell
Step inside a container
Open an interactive bash session in the app container, or in another service of the local stack.
grim server:dev:keys
Let developers onto the box
List, add and remove the labelled SSH keys that let developers log in as the shared dev user of a dev box.