Skip to content

Raise a shared dev box

$ grim server:dev:setup 203.0.113.10 --alias=dev1 --email=ssl@example.com

A dev box is one server the whole team develops on. The containers, the builds and Vite run there; your laptop runs an editor attached over VS Code Remote-SSH and a browser. Reach for it when local Docker has become the slow part of the day, or when the team wants preview URLs anyone can open instead of tunnels from somebody's machine.

grim server:dev:setup takes a bare Ubuntu or Debian machine that far, in one run. It is safe to run again: every step checks what is already there, so a second run after updating grim picks up fixes and breaks nothing.

Usage

$ grim server:dev:setup 203.0.113.10 --alias=dev1 --email=ssl@example.com --ghcr-token=ghp_xxx
$ grim server:dev:setup 10.0.0.20 --alias=dev-lan --cert=~/certs/dev.pem --key=~/certs/dev-key.pem
$ grim server:dev:setup 203.0.113.10 --alias=dev1 --email=ssl@example.com --shared-pat=ghp_team --dev-keys=~/team/keys.txt

The first line is a public VPS with Let's Encrypt. The second is a machine on the office network with a certificate from your own authority. The third runs setup again on the first box to seed a team token and the first developers' keys. The host is always an address or a name SSH can resolve; the alias is only a label for later commands.

Arguments and options

Initialize a dev environment (Docker, Traefik, dev user, grim-cli) on a public VPS or a LAN box

Usage

grim server:dev:setup [--alias ALIAS] [--user USER] [--email EMAIL] [--cert CERT] [--key KEY] [--ghcr-token GHCR-TOKEN] [--shared-pat SHARED-PAT] [--dev-keys DEV-KEYS] [--] <host>

Arguments

Argument Description
host Server IP address or hostname (required)

Options

Option Description
--alias=ALIAS Short alias for the server (e.g., dev-vps1)
--user=USER SSH user for initial connection (default: from servers.json, or "root")
--email=EMAIL Email for Let's Encrypt certificates (public VPS)
--cert=CERT Path to a PEM certificate (LAN / internal CA — skips Let's Encrypt)
--key=KEY Path to the PEM private key (required with --cert)
--ghcr-token=GHCR-TOKEN Classic PAT with read:packages for GHCR login
--shared-pat=SHARED-PAT GitHub PAT shared by the dev team (seeded into ~dev/.grim/config.json)
--dev-keys=DEV-KEYS Path to a file containing dev pubkeys (one per line, optional first-time seed)

Not a production server

A dev box and a server raised by grim server:setup are different machines with different jobs. Do not run both on one host: they want the same ports.

Dev box Production server
Ports 80 and 443 Traefik, directly nginx on the host, Traefik behind it
Database A container in each project's stack MySQL on the host
User One shared dev user for everyone A deploy user
grim Installed on the box Not needed there
Assets Vite dev server with hot reload, on a vite. subdomain Built into the image
Code arrives by Editing it live under /home/dev/projects grim release

What it actually does

Each step is printed with its result, and the first failure stops the run.

  1. Checks SSH. Connects as --user, or the login remembered for that server, or root.
  2. Installs Docker, then git, curl and unzip where they are missing.
  3. Raises the inotify limits. Vite watches every project tree. Several projects on one box exhaust the kernel default, and the dev server dies with an error that does not mention files at all.
  4. Creates the dev user. In the docker group, with /home/dev/projects and your own public key in authorized_keys, or the keys from --dev-keys instead.
  5. Creates the traefik-public network and starts Traefik on 80 and 443, in the TLS mode you chose.
  6. Logs the dev user in to GHCR, when --ghcr-token is given. Without it the step is skipped and says so.
  7. Installs grim on the box, with a link in /usr/local/bin so that ssh dev@host grim up finds it too.
  8. Seeds the shared token, only with --shared-pat, into the dev user's grim config.
  9. Remembers the server in your ~/.grim/servers.json: alias, the dev user, the login it bootstrapped with and the TLS mode. grim server:dev:add-project reads the mode from there.

Two ways to get a certificate

Pick exactly one.

Mode Pass Fits
Let's Encrypt --email A public VPS. Every project domain must resolve to the box before its certificate can be issued.
Your own certificate --cert and --key together A LAN box or an internal authority. The PEM files are uploaded and Traefik serves them as they are.

With neither, setup asks for an email. Give --cert without --key and it stops before touching the server.

Pitfalls

Cloud images do not let root in

Cannot connect to root@… on a fresh AWS machine is not a firewall. Ubuntu images log in as ubuntu, Amazon Linux as ec2-user, Debian as admin. Pass --user. A server made by grim infra:aws-ec2 already carries the right login and needs no flag.

GHCR takes a classic token only

A fine-grained token logs in to GitHub and still cannot pull an image. --ghcr-token must be a classic token with read:packages. A failed login does not stop setup, so read the line that step prints.

--dev-keys replaces your key, it does not add to it

With --dev-keys the dev user gets exactly the keys in that file. Leave your own out and ssh dev@host is closed to you, though the bootstrap login still works. Put your key in the file, or add it afterwards with grim server:dev:keys.

One token for a whole team

--shared-pat is stored on the box and every developer there acts on GitHub with it. Make it a token of a machine account with access to the project's repositories, not your personal one.

The browser showing Traefik's default certificate right after setup is usually DNS that has not spread yet, not a broken box. Let's Encrypt cannot issue for a name that does not point at the server. Wait for the record, then reload.

After it finishes

The summary ends with the two commands that come next, filled in with your alias. Let the developers in, then give the box its first project.

$ grim server:dev:keys add dev1 --key=~/keys/anna.pub --label=anna
$ grim server:dev:add-project shop --host=dev1 --repo=acme/shop --domain=shop.dev.example.com

The whole path, from an empty VPS to a developer's editor attached to a running project, is the recipe Set up a dev box for a client.

Recipes that use it