Workflows

Common deploy, update, recovery, and cleanup loops.

Start here when you know what you want to do and need the command shape. For payload-specific details, use the linked payload guide.

For real homelab services, run these commands from a Service Workspace. Yeet first looks for yeet.toml in or above the current directory. If none is found, it uses the configured workspace that owns the target catch host.

The pattern is deliberately repetitive: run the command, inspect status, read logs, then make the next change. Infrastructure gets easier when the feedback loop is short and visible.

Deploy with the web form

Use the guided flow for a first deploy or when you do not want to remember the flags:

yeet run --web
yeet run --web <svc>
yeet run --web <svc> ./compose.yml

The form saves the same service config to yeet.toml that the CLI uses. Runtime errors appear in the browser and local terminal so you can edit and retry without restarting the command.

Deploy a compose stack

yeet run <svc> ./compose.yml
yeet info <svc>
yeet logs -f <svc>

After the first successful deploy, rerun the saved service from the workspace that contains yeet.toml:

yeet run <svc>

Use Containers for Compose, Dockerfiles, images, Python, and TypeScript payloads.

Run a public image

yeet run -p 8080:80 <svc> nginx:alpine
yeet status <svc>
yeet status <svc-a> <svc-b>

Replace the full published-port list later with service set:

yeet service set <svc> -p 80:80 -p 443:443
yeet service set <svc> --publish-reset -p 443:443

Push a local image

Use this when you already built a container image on your workstation. Yeet pushes it to the catch host's registry and can run it from there:

yeet docker push <svc> <local-image>:<tag> --run

Build from a Dockerfile

yeet run <svc> ./Dockerfile
yeet logs -f <svc>

Deploy a binary or script

Build binaries for the catch host architecture before deploying:

GOOS=linux GOARCH=amd64 go build -o ./bin/<svc> ./cmd/<svc>
yeet run <svc> ./bin/<svc>

For scripts:

yeet run <svc> ./script.sh
yeet run <svc> ./script.sh -- --app-flag value

Use Binaries & Scripts for environment and file setup.

Run a cron job

yeet cron <svc> ./job.sh "0 9 * * *"
yeet cron <svc> ./job.sh "0 9 * * *" -- --job-arg foo
yeet logs -f <svc>

Use Cron Jobs for timer behavior and update flow.

Create a VM

yeet run <vm> vm://ubuntu/26.04
yeet ssh <vm>

VM payloads require a KVM-capable Linux catch host. Use NixOS when you want that image family:

yeet run <vm> vm://nixos/26.05

Use VMs for host requirements, image cache commands, networking, file copy, and recovery.

Update a service

For binaries, scripts, Dockerfiles, and Compose files, update the local payload and rerun:

yeet run <svc> ./compose.yml
yeet run <svc> ./bin/<svc>

For Compose image refreshes:

yeet run --pull <svc> ./compose.yml
yeet docker outdated
yeet docker update <svc>
yeet docker update --outdated

Use --force when you want to redeploy even if yeet detects no payload or config change. It reruns the deploy; it does not diagnose why the previous state was wrong:

yeet run --force <svc> ./compose.yml

Stage before applying

Use staging when you want to upload and inspect a payload before applying it. That adds a checkpoint to the deploy queue, which is useful when the payload is large or the timing matters:

yeet stage <svc> ./bin/<svc>
yeet stage <svc> show
yeet stage <svc> commit

Discard the staged payload:

yeet stage <svc> clear

Edit env, config, or files

yeet edit <svc>
yeet env edit <svc>
yeet env copy <svc> ./app.env
yeet env set <svc> PORT=8080 LOG_LEVEL=debug
yeet copy ./config.yml <svc>:config/config.yml

For VM guests, copy to the VM endpoint:

yeet copy ./configs/ <vm>:~/configs/
yeet copy <vm>:"/var/log/*.log" ./logs/

Choose networking

Use host ports when you only need a host-level TCP port:

yeet run -p 8080:80 <svc> nginx:alpine

Choose networking by access path and outbound internet path:

  • Use svc for private yeet traffic, yeet DNS, and internet egress through the catch host.
  • Use svc,ts when the service also needs its own Tailscale identity.
  • Use lan when the service should request a LAN or VLAN address and use that network's DHCP gateway.
yeet run <svc> ./compose.yml --net=svc
yeet run <svc> ./compose.yml --net=svc,ts
yeet run <svc> ./compose.yml --net=lan

Avoid plain --net=ts unless the service is intentionally tailnet-only or has a Tailscale exit node. See Networking before using lan, ts, or combined modes on a new host. Network mode is not just "who can connect"; it also decides what the service can reach.

Move a service root

Set the root at first deploy:

yeet run <svc> ./compose.yml --service-root=/srv/apps/<svc>
yeet run <svc> ./compose.yml --service-root=tank/apps/<svc> --zfs

Stop a service before moving its existing root:

yeet stop <svc>
yeet service set <svc> --service-root=/srv/apps/<svc> --copy
yeet start <svc>

Use --empty instead of --copy when you want a new empty root. See ZFS for dataset-backed roots and snapshots. Moving a root is a state migration, so read the plan before applying it.

Recover data

List recovery points:

yeet snapshots list <svc>
yeet snapshots inspect <svc> <snapshot>

Clone first when you want to inspect recovered data safely. This keeps the original service out of your first recovery experiment:

yeet snapshots clone <svc> <snapshot> <svc>-recover
yeet info <svc>-recover

Restore in place when you are ready:

yeet snapshots restore <svc> <snapshot> --stop --yes

See ZFS for snapshot defaults, retention, and VM recovery.

Target a host

Here, <catch-host> is the catch Tailscale hostname, not the SSH machine host. SSH was for installation. Normal operations use the catch identity.

CATCH_HOST=<catch-host> yeet status
yeet --host=<catch-host> status
yeet status@<catch-host>
yeet run <svc>@<catch-host> ./compose.yml

Save a default host:

yeet config --host=<catch-host>

Remove a service

Uninstall the service but keep its data:

yeet rm <svc>

Remove service data and the local config entry too:

yeet rm --clean <svc>

Read the confirmation prompt before deleting data.