Homelab service manager built around Tailscale RPC.
yeetrun.com · Quick Start · Install · First-Run Validation · Docs
Yeet is open source homelab infrastructure tooling. You run the yeet CLI from
your workstation and install the catch daemon on Linux hosts you control.
From there, yeet deploys containers, host services, cron jobs, and
Firecracker-backed Linux VMs over Tailscale/tsnet RPC.
Yeet is intentionally opinionated:
- Hosts run Linux with systemd.
- SSH is used for
yeet init; RPC uses catch's embedded tsnet node. - Docker is used for container payloads.
- Services are currently managed as root-owned systemd units.
- VM payloads require x86_64 Linux, KVM (
/dev/kvm), TUN/TAP, and VM filesystem/networking tools on the catch host.
Within those constraints, the release path is intended to be installable on a fresh Ubuntu/Debian-style host with SSH access.
Install the release binary:
curl -fsSL https://yeetrun.com/install.sh | shNightly build:
curl -fsSL https://yeetrun.com/install.sh | sh -s -- --nightlyCheck the local CLI and the selected catch host:
yeet upgrade checkUpgrade both from verified GitHub release assets:
yeet upgradeIn a project with multiple catch hosts in yeet.toml, scan and upgrade all of
them explicitly:
yeet upgrade check --all
yeet upgrade --allTo reinstall the latest public release even when a component is already current, newer, or currently a source/dev build:
yeet upgrade --all --forceTo install a specific public release instead of latest, select the tag
explicitly. Use --force when that would reinstall or downgrade a component:
yeet upgrade --all --version v0.6.1 --forceStart with a Linux host that has systemd and SSH access. Docker can be
installed by yeet init on Debian/Ubuntu-style hosts. A plain first run is
valid; if catch needs to join Tailscale, yeet init prompts for a Tailscale
OAuth client secret and uses it to enroll catch as a tagged device:
yeet init root@<machine-host>
yeet init --install-docker root@<machine-host>On KVM-capable hosts where you plan to run VM payloads, let init install the VM filesystem tools too:
yeet init --install-docker --install-vm-tools root@<machine-host>Catch uses an embedded Tailscale node for RPC. That node must end up with a
tag-based identity, such as tag:catch; user-owned catch nodes are rejected.
If your tailnet policy does not already allow that tag, update tagOwners in
Tailscale before first setup. The OAuth client secret should have the
auth_keys scope and be allowed to assign tag:catch, either directly or via
an owner tag such as tag:yeet.
For repeatable or non-interactive bootstrap, pass the OAuth client secret:
yeet init --install-docker --install-vm-tools --ts-client-secret=<secret> root@<machine-host>Advanced users can also create a preauthorized Tailscale auth key that assigns the catch server tag and pass it with:
yeet init --install-docker --install-vm-tools --ts-auth-key=<key> root@<machine-host>If your tailnet separates catch hosts by cluster or location, include the tags your ACLs or grants expect on that key.
Host names matter:
root@<machine-host>is the SSH target used only for init/install.CATCH_HOST,--host, and<svc>@<host>refer to the catch tsnet hostname.
See Installation and Tailscale for details.
Confirm the control plane is reachable:
yeet version
yeet statusIf you have Tailscale installed locally, yeet list-hosts can also discover
tagged catch nodes. It is optional for normal RPC because yeet embeds tsnet.
Then run a disposable container:
yeet run -p 18080:80 yeet-smoke-web nginx:alpine
yeet ssh -- curl -fsS http://127.0.0.1:18080/ >/dev/null
yeet rm --clean-data yeet-smoke-webFor the full fresh-host playbook, including script services, cron timers, VM capability, LAN networking, and ZFS checks, use First-Run Validation.
Docker Compose:
yeet run <svc> ./compose.yml
yeet logs -f <svc>
yeet run --pull <svc> ./compose.ymlDocker image:
yeet run -p 8080:80 <svc> nginx:alpineDockerfile:
yeet run <svc> ./DockerfileBinary or script:
GOOS=linux GOARCH=amd64 go build -o ./bin/<svc> ./cmd/<svc>
yeet run <svc> ./bin/<svc>
yeet run <svc> ./script.sh -- --app-flag valueCron job:
yeet cron <svc> ./job.sh "0 9 * * *"VM on a KVM-capable host:
yeet vm images catalog
yeet run devbox vm://ubuntu/26.04
yeet ssh devbox
yeet vm console devboxThe official VM catalog is loaded from yeet-vm-images at runtime. The catalog
defines supported vm://... families, and each family points at a stable latest
manifest. New image versions are picked up through that manifest rather than a
yeet release.
Run a vm:// payload once per VM name; change an existing VM with yeet vm set
or remove and recreate it for a fresh guest.
Detach from an active VM console by pressing Enter, then typing ~.. The VM
keeps running.
Official VM images also include NixOS:
yeet run lab vm://nixos/26.05For a VM that should also request an address on the catch host's LAN, keep the default management network and add LAN networking:
yeet run devbox vm://ubuntu/26.04 --net=svc,lanLAN-only VMs are reached directly at their guest LAN IP. svc or svc,lan
keeps a catch-proxied yeet ssh management path.
Services and VMs on svc also resolve other service-network names through
yeet DNS, including short names and *.yeet.internal names.
Local image built on your workstation:
yeet docker push <svc> <local-image>:<tag> --runAfter the first successful deploy, yeet writes a yeet.toml replay file. You
can usually rerun the same service with:
yeet run <svc>See Workflows and Payloads for the complete guides.
Yeet works without every optional feature. The host determines which payloads and network modes are available:
- Docker is required for container payloads.
- x86_64 Linux, KVM, TUN/TAP, and VM filesystem/networking tools are required
for VM payloads.
yeet initchecks this and--install-vm-toolsinstalls missing Debian/Ubuntu packages when the host can run VMs. - LAN/macvlan networking requires a host network where macvlan and DHCP make sense.
- ZFS is optional and enables dataset-backed service roots, snapshots, and fast repeated VM disk clones.
--net=tsservice networking requires Tailscale auth for each service netns.
Yeet warns during init or deploy when a host cannot support a requested feature. See Networking, VMs, and ZFS.
The docs site is the user manual and the source of truth for behavior:
- Quick Start
- Installation
- First-Run Validation
- Workflows
- Payloads
- Architecture
- Networking
- Tailscale
- ZFS
- CLI Reference
- Troubleshooting
- FAQ
Use mise to install the pinned toolchain from .mise.toml:
curl https://mise.run | sh
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
mise installBuild locally:
go build ./cmd/yeet
go build ./cmd/catchInstall repo hooks once:
mise run install-githooksRun the normal local quality gate:
mise run qualityHeavier checks are available for release or deeper quality work:
mise run race
mise run fuzz
mise run mutation
mise run quality:goalServices managed by catch currently run as root. That is acceptable for a
single-operator homelab, but it is not a good default for production or
multi-tenant setups. See the FAQ for current
limitations.
BSD 3-Clause. See LICENSE.