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.txtThe 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.
- Checks SSH. Connects as
--user, or the login remembered for that server, orroot. - Installs Docker, then
git,curlandunzipwhere they are missing. - 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.
- Creates the
devuser. In thedockergroup, with/home/dev/projectsand your own public key inauthorized_keys, or the keys from--dev-keysinstead. - Creates the
traefik-publicnetwork and starts Traefik on 80 and 443, in the TLS mode you chose. - Logs the
devuser in to GHCR, when--ghcr-tokenis given. Without it the step is skipped and says so. - Installs grim on the box, with a link in
/usr/local/binso thatssh dev@host grim upfinds it too. - Seeds the shared token, only with
--shared-pat, into thedevuser's grim config. - Remembers the server in your
~/.grim/servers.json: alias, thedevuser, the login it bootstrapped with and the TLS mode.grim server:dev:add-projectreads 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.comThe 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.
Related spells
grim server:dev:add-project
Settle a project on the box
Clone a repository onto a shared dev box, give it a dev .env and domains, and start its stack there.
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.
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 server:setup
Raise a production server
Turn a bare Ubuntu or Debian VPS into a host for deployed projects, with Docker, MySQL, nginx, Traefik and a deploy user.
Recipes that use it
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.
LAN dev box with a custom certificate
Check a certificate from your own authority by hand, raise a dev box that serves it, put a project on it under names one wildcard covers, make developer machines trust the authority, and renew the certificate without downtime.