yeet CLI
Client-side command reference for yeet.
This is the client-side command reference. Most commands are forwarded to catch.
Yeet uses "host" in two different ways:
- catch host (Tailscale/tsnet): the hostname that
yeettargets for RPC. This is whatCATCH_HOST,--host, and@<host>refer to. - machine host (SSH): the SSH target you use with
yeet init(for installing catch). Example:yeet init root@<host>.
After yeet init, catch will advertise a tsnet hostname (default catch or
--tsnet-host). That tsnet name is the catch host you use for yeet run,
yeet status, etc.
See Tailscale for how RPC and tailnet addressing work.
--host=<host>: target host (overrides prefs).--service=<svc>: force the service name.--tty: force TTY mode for remote commands.--no-tty: disable TTY mode for remote commands.--progress=<mode>: progress output mode (auto,tty,plain,quiet).
Host scoping shortcuts:
yeet <command>@<host>for host-only commands (e.g.events@host).yeet <command> <svc>@<host>to target a service on a specific host.
Note: Binary payloads uploaded by run, stage, and cron are
zstd-compressed; catch auto-decompresses on receipt.
Install or update catch on a remote host via SSH.
yeet init root@<host>
If omitted, init uses the current CATCH_HOST value.
Note: the root@<host> here is the machine host you can reach over SSH. It
is not the catch/tsnet host you target for yeet run or yeet status.
Run init from the yeet source repo (it builds ./cmd/catch). If you are in a
different repo, clone github.com/shayne/yeet and run it from there.
If you omit the SSH user, init will try to use the install user reported by
catch (recorded during the last install). If unavailable, SSH defaults apply.
If catch reports an install host, init will prefer that host for SSH.
In interactive terminals, init will prompt to accept the SSH host key if
needed. For non-interactive runs, pre-populate ~/.ssh/known_hosts.
When stdout is a terminal, init uses the full TTY progress UI (spinners, live
updates). Pipe output for plain text. Use --progress=plain (or
--progress=quiet) for POSIX-friendly scripted output.
Open an SSH session to the catch host over the tailnet/tsnet address. If
you pass a service name, the session starts in that service's data directory
(<data-dir>/services/<svc>/data).
yeet ssh
yeet ssh <svc>
yeet ssh -- uname -a
yeet ssh <svc> -- ls -la
Notes:
yeet sshtargets the catch host, not the machine host you used foryeet init.yeet ssh <svc>resolves the host from<svc>@<host>,--host,CATCH_HOST, oryeet.tomlservice entries, then drops into the service data dir.- Because it connects over Tailscale/tsnet, you can firewall port 22 on public/LAN interfaces and still reach the host via your tailnet.
- If catch reports an install user, that user is used for SSH; otherwise your SSH defaults apply.
- SSH options can be passed before the service name (e.g.,
yeet ssh -i ~/.ssh/id_ed25519 <svc>). - Use
--to pass a remote command. If<svc>is provided, the command runs from the service directory.
Install or update a service based on the payload:
- Binary or script -> systemd service
- Docker Compose file -> docker compose service
- TypeScript file -> docker compose service using a container runtime
- Python file -> docker compose service using a container runtime
- Docker image ref (
nginx:latest,ghcr.io/org/app:latest) -> pulled on the catch host and installed Dockerfile-> built locally for the remote platform, pushed to the internal registry, then installed
Examples:
yeet run <svc>@<host> ./compose.yml
yeet run <svc> ./compose.yml --net=lan
yeet run --pull <svc> ./compose.yml
yeet run <svc> ./Dockerfile
yeet run <svc> ./bin/<svc> -- --app-flag value
yeet run <svc> ghcr.io/org/app:latest
yeet run -p 8000:8000 <svc> ./server.py
-p/--publish publishes host ports for python/TypeScript/Dockerfile payloads
and registry image refs (e.g. -p 8000:8000). For docker compose payloads,
define ports in the compose file.
When stdout is a terminal, run uses the full TTY progress UI (spinners, live
updates). Pipe output for plain text. Use --progress=plain (or
--progress=quiet) for POSIX-friendly scripted output.
If a yeet.toml exists (or after your first run), you can omit the payload:
yeet run <svc>
Yeet will expand the stored payload + args for that service.
If you want to run a local image instead of pulling from a registry, use
yeet docker push --run:
yeet docker push <svc> <local-image>:<tag> --run
Run flags:
--pull: for docker compose/image payloads, pull latest images on deploy. Without this,runreuses existing images.--net=<list>: network modes (ts,svc,lan). Comma-separated.--ts-ver=<ver>: tailscale version for service netns.--ts-exit=<node>: tailscale exit node.--ts-tags=<tag>: tailscale tags (repeatable or comma-separated).--ts-auth-key=<key>: tailscale auth key for service netns.--macvlan-parent=<iface>: parent interface forlan.--macvlan-vlan=<vlan>: VLAN id forlan.--macvlan-mac=<mac>: MAC address forlan.--restart=<bool>: currently unused.
See Tailscale for setup, netns behavior, and Serve examples.
For compose services, yeet docker update <svc> also pulls and recreates
containers.
Stage a change without installing it yet.
Examples:
yeet stage <svc> ./bin/<svc>
yeet stage <svc> --net=ts --ts-tags=tag:app
yeet stage <svc> show
yeet stage <svc> commit
Notes:
stage showprints the staged config.stage clearremoves staged changes without applying them.stage commitapplies the staged version.
Stage flags match run flags (see above).
Copy files between local paths and a service data dir (rsync-like).
Exactly one side must be remote using svc: or svc:path (paths are relative
to the service data/ directory). By default, yeet copy behaves like
rsync -avz (archive + verbose + compress).
Examples:
# Local -> remote (data dir root)
yeet copy ./app.env svc:
# Local -> remote (explicit destination file)
yeet copy ./config.yml svc:config/config.yml
# Local dir contents -> remote dir (rsync-style)
yeet copy ./configs/ svc:config/
# Remote -> local
yeet copy svc:config ./config
Notes:
-ais archive mode (includes recursion and metadata preservation).-zcompresses the transfer stream.- Output mirrors rsync’s file list and summary format.
- File ownership (uid/gid) is not preserved.
- A trailing slash on the source copies directory contents (rsync-style).
- A trailing slash on the destination forces directory semantics.
- Transfers are staged and moved into place only after completion.
Install a cron job from a file. The cron expression must have 5 fields.
yeet cron <svc> ./job.sh "0 9 * * *" -- --job-arg foo
If a yeet.toml exists (or after your first cron), you can omit the payload
and cron expression:
yeet cron <svc>
When stdout is a terminal, cron uses the full TTY progress UI (spinners, live
updates). Use --progress=plain (or --progress=quiet) for POSIX-friendly
scripted output.
Tail service logs.
-f/--follow: follow logs.-n/--lines: number of lines (default-1).
Show service status.
yeet status
yeet status <svc> --format=json
Notes:
- With no
<svc>, yeet usesyeet.tomlhost list (if present) and queries each host. - Use
<svc>@<host>to disambiguate services that exist on multiple hosts. - Table output always includes the
HOSTcolumn, even for single-host queries. - Cron services (installed via
yeet cron) showTYPEascron. - Services show
TYPEasbinary,typescript, orpythonwhen detected; otherwiseserviceordockerbased on the underlying service type. - For docker compose services in the no-
<svc>view, containers are summarized into a singleCONTAINERScolumn and the status shows running/stopped/partial counts.
Show detailed service information collected from both the local yeet.toml and
the remote catch host.
yeet info <svc>
yeet info <svc>@<host>
yeet info <svc> --format=json
Plain output is grouped by section (host, service, client config, server
config, network, runtime, images). Use --format=json or
--format=json-pretty for machine-readable output.
Manage service lifecycle.
Enable or disable autostart for a service (systemd-backed services only).
Remove a service. yeet attempts to stop the service and clean up files, but
will still remove the service from the database even if cleanup fails. Any
stop/cleanup failures are printed as warnings.
Roll back to the previous generation if available.
Print the current env file for a service. Use --staged to inspect the staged
env file.
Edit the env file for a service.
Upload a local env file.
Set one or more env keys (creates the env file if missing). Use KEY= (or
KEY="") to unset a key.
Open the current config for editing in $EDITOR (defaults to vim).
--configedits the service JSON (limited; not implemented for docker/systemd content).--tsis reserved.--restartis currently unused.
Show the service IP (only meaningful when --net=svc was used).
Mount a network filesystem on the host (global, not per-service). If no args are given, it lists mounts.
yeet mount host:/export data-share --type=nfs --opts=defaults
yeet mount # list mounts
Unmount a mount by name.
yeet umount data-share
Interactive setup for the catch host's Tailscale OAuth client secret. The CLI
walks you through creating a tag and a trust credential, then stores the
tskey-client-... secret on the target host. Use --client-secret to skip
the prompt.
See Tailscale for the full setup flow and related flags.
Run tailscale commands inside the service netns.
yeet tailscale <svc> -- serve --bg 8080
yeet tailscale <svc> -- debug daemon-logs
See Tailscale for Serve/TLS details.
Subscribe to events. Use --all for global events.
yeet events@<host> is shorthand for subscribing to all events on that host.
Docker compose maintenance for services that were installed from compose files.
pullprefetches images without restarting.updatepulls images and recreates containers (restart).
Push a local image into the internal registry.
--run: tag image asrun.--all-local: push all local images for the service matching remote OS/arch.
List tailnet hosts matching tags (default tag:catch).
Show or save client preferences.
Show the catch server version for the current host.
Hidden easter egg.
Host terminology
Global flags
Commands
yeet init [REMOTE]
yeet ssh [<svc>] [-- <remote-cmd...>]
yeet run <svc> <file-or-image> [-- <args...>]
yeet stage <svc> [args...]
yeet copy|cp [-avz] <src> <dst>
yeet cron <svc> <file> "<expr>" [-- <args...>]
yeet logs <svc> [-f] [-n <lines>]
yeet status [<svc>] [--format=table|json|json-pretty]
yeet info <svc> [--format=plain|json|json-pretty]
yeet start|stop|restart <svc>
yeet enable|disable <svc>
yeet remove <svc>
yeet rollback <svc>
yeet env show <svc> [--staged]
yeet env edit <svc>
yeet env copy|cp <svc> <file>
yeet env set <svc> KEY=VALUE [KEY=VALUE...]
yeet edit <svc> [--config] [--ts] [--restart]
yeet ip <svc>
yeet mount <source> [name] [flags]
yeet umount <name>
yeet tailscale --setup [--client-secret=...] (alias: ts)
yeet tailscale <svc> -- <tailscale args...> (alias: ts)
yeet events [<svc>] [--all]
yeet docker pull|update <svc>
yeet docker push <svc> <image> [--run] [--all-local]
yeet list-hosts [--tags=<tags>]
yeet prefs [--save]
yeet version [--json]
yeet skirt