Skip to content

Liber Tertius · Chapter 6 · Shared dev boxes

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.

≈10 min 6 steps An SSH key pair VS Code with Remote-SSH An operator who runs the box
Source

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.com

Everyone 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.

  1. Send your public key

    The operator adds the key to the dev user under a label with your name. Send the .pub file, 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 list shows the same SHA256: 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.

  2. Paste the SSH block

    The operator sends back what grim server:dev:ssh-config printed 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 answers Unknown 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 3

    The name is the operator's alias with -dev added. The block names no key, so SSH offers your default ones. When the key you sent lives elsewhere, add a line IdentityFile ~/.ssh/<key> under User dev.

  3. 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/shop

    The 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.

  4. 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 logs and grim shell among them.

    $ cd /home/dev/projects/shop
    $ docker compose ps
    $ grim logs -f vite
    $ git status --short
     M grim.json

    The source is mounted into the containers and Vite pushes changes to the browser, so saving a file is all a code change needs. grim.json shows as modified on every dev box. The operator's tooling wrote a deployment block into it that tells grim this is a dev box. Do not commit that block and do not revert it.

  5. Reload after a change to .env

    Containers read .env when they are created, and grim up leaves a running container alone. grim reload renders 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 cache

    Everyone 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.

  6. 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.sql

    The first line is an interactive client. The second writes a dump into the dev user's home on the box, and scp dev1-dev:shop.sql . on your machine brings it over. The variables are expanded inside the container, which is why the command is wrapped in sh -c with 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 sends

The 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 reload

The 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.