#mechanics.run: a Control Plane You Talk to Over SSH

This blog is deployed through mechanics.run, and the entire setup — repository, CI, deployment, public HTTPS route — happened without opening a browser once. Here’s what the platform is and how the workflow feels.

What it is

mechanics.run is an SSH control plane for a self-hosted developer platform. The first thing it tells you when you connect sets the tone:

Mechanics is an SSH control plane. It is not a shell.
Run one structured command per SSH connection.

There’s no interactive session. Every operation is a single structured command:

ssh -T mechanics.run -- <command> [args]

Your SSH key is your identity. Signup links the key to a GitHub account, and from then on every connection is authenticated by the key you already have. No passwords, no tokens to rotate, no web login.

The core model

Five nouns cover the whole platform:

  • user — your SSH key, linked to GitHub at signup
  • repo — a Forgejo git repository with optional CI
  • image — a container tag published by Forgejo Actions
  • app — an Argo CD deployment in your own Kubernetes namespace
  • route — a public HTTPS hostname in front of an app

Each noun is a command group: repo, app, route, plus signup and setup for the client side. Everything is discoverable through --help over SSH.

The workflow

From empty directory to public URL:

# 1. Create a repo and push code (Dockerfile at the root)
ssh -T mechanics.run -- repo create blog
git remote add origin ssh://git@forgejo.mechanics.run/<user>/blog.git
git push -u origin main

# 2. Add the standard CI workflow (committed server-side)
ssh -T mechanics.run -- repo -r blog scaffold ci
git pull --ff-only origin main

# 3. Wait for the image build
ssh -T mechanics.run -- repo -r blog build wait --commit $(git rev-parse HEAD)

# 4. Deploy
ssh -T mechanics.run -- app create blog --repo blog --tag <short-sha>
ssh -T mechanics.run -- app -a blog wait --tag <short-sha>

# 5. Expose
ssh -T mechanics.run -- route -a blog create

That last command prints https://blog.<user>.mechanics.run with a valid certificate. Updates after that are three commands: push, build wait, set-image.

What’s underneath

The platform doesn’t hide its internals — it commits them to a git repo you can read:

  • Forgejo hosts your source and a personal gitops repository.
  • Forgejo Actions builds the Dockerfile and pushes tags (full SHA, 12-char SHA, latest) to the built-in registry.
  • app create doesn’t apply anything to the cluster directly. It commits a Deployment, Service, and kustomization into gitops:apps/<name> and lets Argo CD pick it up. Image updates are kustomize newTag commits.
  • route create commits an HTTPRoute (Gateway API) into gitops:network/routes/<name> and wires up listeners on an Istio gateway, HTTP-to-HTTPS redirect included.

So “deploy” means “git commit to a repo owned by you”. The full state of your infrastructure is git clone away, and you can fix things by hand in the gitops repo if you want — the CLI marks its own commits and refuses to overwrite yours without --force.

Sharp edges I hit

Two, both small:

  1. The generated Service assumes your container listens on port 80. I started with nginx-unprivileged (port 8080) and got 503s from envoy while the pod sat there Healthy. Switching to plain nginx:alpine fixed it. Check the generated manifests before debugging the app.
  2. One command per connection means no shell tricks server-side. Loops, pipes, and conditionals live on your machine. In practice this is a feature — every operation is scriptable and composable from local shell — but it takes a minute to stop typing ssh mechanics.run expecting a prompt.

Why I like it

The interface fits in your head. Five nouns, a dozen verbs, all self-documenting over the same channel you already use for git. No dashboard to click through, no YAML to write (but all the YAML readable in git when you care), and the whole thing automatable with nothing but ssh and git — which is exactly how this post got published.