Running it on a server
Leave it running, and reach it safely
Goobers is self-hosted — no managed service, no hosted tier. A cloud VM runs the same single binary as your laptop, left unattended. The Kubernetes-and-Temporal tier is a deployment goal, not something you can run at v0.3.0 — quarantined, not a path.
goobers up
The daemon: an embedded scheduler (cron triggers and run conditions), the local runner, a loopback HTTP API, and a webhook listener if one's configured. On startup it resumes runs a prior crash interrupted before admitting new work, from their last durable checkpoints.
instance.yaml is read once, at startup — changing it needs a restart. --watch-config is narrower than that: it separately watches direct edits to
the materialized config/ directory, a process-local development convenience,
not how a server picks up definition changes (that's below).
Install it as a service
goobers service install
goobers service start
goobers service status
Wraps systemd on Linux, launchd on macOS, and the Windows Service Control Manager. install registers, enables, and starts it with restart and backoff
preconfigured, and never overwrites an existing registration. It's per-user on Linux and
macOS, since the credentials it runs with are the user's; Windows needs an elevated
terminal. Updates go through self-update, never a reinstall. The daemon —
and every stage it runs — inherits the service's environment, not your shell's dotfiles.
Shutting down cleanly
The first SIGINT or SIGTERM stops new work and drains. The
drain is indefinite by default — there's no implicit timeout. --drain-timeout sets a deadline, and its default value of 0 means no deadline, not immediate shutdown. A second signal always
forces a shutdown without prompting. A skipped drain is a dirty restart, and the daemon
records that on its next start — the absence of a dirty-restart event is how you confirm
every prior restart went graceful.
Staying current
goobers self-update --policy on-release
Three policies: manual (a specific release tag), on-release (the default), and on-main (the configured branch).
Every policy stages the new binary and smoke-checks it before requesting supervised
activation, rather than replacing the running binary in place. Config is never touched by
a self-update.
Disk
goobers runs du
goobers runs list
goobers telemetry prune
goobers telemetry compact runs du and runs list report journal disk usage; retention
pruning past that point is opt-in and is still reporting until you configure it, not
automatic cleanup.
Changing definitions while it runs
Definitions live in git; the running daemon never rewrites them. A Git workflowSource reconciles its tracked ref continuously:
local ref changes wake the loop immediately, a periodic fetch-and-compare poll is always
active underneath that, and an authenticated GitHub push delivery wakes it too, when webhook.secret is configured. An invalid revision is rejected outright and
the last-known-good definitions keep running; a run already in flight stays pinned to the
version it started on. goobers apply forces reconciliation immediately
instead of waiting for the next poll, and goobers config diff catches drift
against a canonical tree on demand.
Reaching it from another machine
Loopback is unauthenticated by design — fine for a solo laptop, not for anything else. A
reverse proxy, tunnel, port forward, published container port, or any other non-loopback
listener all count as exposure, and every one of them requires both TLS
and OIDC on api.auth — there is no insecure override. Roles are ordered: admin includes operate and view, and operate includes view. An authenticated principal with no
mapped role is denied, and a discovery or signing-key failure fails closed rather than
falling back to anonymous access.
| Request | Result |
|---|---|
| No token | 401 |
| Valid token, mapped role | 200 |
| Valid token, no mapped role | 403 |
| Wrong issuer, audience, or expired | 401 |
Full IdP registration and the tier-3 Entra posture are in the product repo.
Not yet
There is no atomic cross-instance claim lock. Running two instances against one
repository is a soft boundary today — a disjoint required label per instance plus a
distinct branchNamespace, and neither alone is sufficient. Full pattern in
the product repo
.