In development, not yet released

Hosting for small Node products.
No dependencies.

Set up a server once. Then each product is one command to launch, and every push that passes CI deploys itself. GitHub never holds a key to your server.

$ modest-host launch shop shop.example.org --server myserver
Made the repository example/shop.
Added shop on myserver.
Gave GitHub the read-only deploy key, and the webhook.

$ git push


shop: running 3f2a9c1

Signed · main · CI passed · answered

Approach

GitHub sends a message, not commands

Many deploy setups store a key to the server in GitHub, so anyone who gets into the repository or a workflow gets into the server too. modest-host turns that around: the server fetches and builds for itself, and GitHub can only tell it that CI passed.

A usual deploy

  • An SSH key to the server, stored in GitHub
  • A workflow that logs in and runs commands
  • A container registry, and more tools to trust
  • Products sharing one user

modest-host

  • No key to the server anywhere but the server
  • A signed message, checked, that runs nothing
  • Node, git, systemd, and Caddy: already there
  • Each product fenced off from the others

Deploying

What happens when you push

Nothing runs between pushes. When CI passes on main:

  1. GitHub sends a signed messageSigned with a secret only the server and GitHub know.
  2. The server checks itThe signature, the branch, the workflow, the repository, and that CI passed. Anything else is refused.
  3. It fetches and buildsOnly main's newest commit, with its own read-only key; dependencies exactly as the lockfile says, with install scripts off.
  4. It switches, and checksIf the new version doesn't answer, the one before comes back.

Features

What it does

One command to launch

Makes the GitHub repository, sets the product up on the server with its domain and HTTPS, and connects the two.

Signed deploys

Only a signed message for a passed CI run on main deploys, and only main's newest commit, so an old message sent again can't roll it back.

Products fenced off

Each runs as its own user, sandboxed by systemd: read-only code, its own data folder, and none of the server's keys.

Goes back if it fails

A version that doesn't answer is switched back to the one before, so a bad push doesn't take a product down.

Nightly copies

Every product's SQLite databases are copied each night with SQLite's own backup, safe while they're in use.

Checks the server

Before it does anything, it checks SSH, automatic updates, and what's listening, and says plainly what to fix.

$ sh test/e2e/run.sh
== a stand-in GitHub that isn't GitHub is refused
   ok: nothing deployed: GitHub's real host keys are pinned
== a signed message for a passed CI run deploys
   ok: shop v1 is running, with modest-auth installed from npm
== each product is sandboxed
   ok: it can write its own data, and nothing else it tried
== a version that doesn't start is switched back
   ok: v2 is running again

End to end: every step passed.

Compatibility

Runs on Ubuntu, with systemd and Caddy

On one server you've set up, with products written for Node 24 or later.

  • Ubuntu 24.04
  • systemd
  • Caddy
  • Node 24
  • GitHub

Getting started

It's in development, and not yet on npm. The docs describe it as it's being built, and the repository has everything so far.