Troubleshooting

Start from the symptom, then check the boundary that usually breaks.

Start with the symptom you see. If this is a fresh host, run First-Run Validation after fixing the first connection problem.

Most failures happen at a boundary: workstation to catch, catch to Docker, service to network, VM to KVM, or local config to live state. Debug the boundary first.

yeet cannot reach catch

Confirm you are targeting the catch hostname, not the SSH machine hostname:

CATCH_HOST=<catch-host> yeet status
yeet --host=<catch-host> status

Then check the layers in order:

  • The catch Tailscale node exists.
  • The node has a tag such as tag:catch.
  • Your device or admin tag can reach the catch tag on TCP port 41548.
  • The catch service is running on the host.

If you have the Tailscale CLI installed locally, this can help confirm the tagged node is visible:

yeet list-hosts

See Tailscale Setup for the tailnet policy shape.

yeet init says catch needs a tag

Catch rejects user-owned Tailscale nodes. It must join the tailnet with a tag-based identity. That keeps the daemon in the server side of the trust model.

Fix the Tailscale policy first, then rerun init:

yeet init root@<machine-host>

yeet init got a different catch hostname

Tailscale assigned a suffix such as catch-1 because another device already owns the requested hostname. This is not random naming. It is stale or conflicting state showing up as DNS.

Use the assigned catch host:

yeet --host=catch-1 status

Or remove the stale/conflicting Tailscale device and rerun yeet init.

If you are adding a second catch host, choose a distinct name instead of reusing the default catch name:

yeet --host=morpheus-catch init root@<machine-host>

Docker payloads fail

Container payloads require Docker on the catch host. On Debian/Ubuntu-style hosts, interactive init can ask before installing it:

yeet init root@<machine-host>

If local image pushes do not show up on the host, rerun bootstrap and allow Docker setup. Yeet configures the Docker settings it needs during catch install.

For Compose services:

yeet info
yeet info <svc>
yeet logs <svc>
yeet ssh -- docker ps

See Containers for update and image workflows.

Published port changes fail

yeet service set protects the full published-port list. Include every mapping you want to keep. Otherwise you are not adding a port; you are replacing the list with a smaller one:

yeet service set vaultwarden -p 80:80 -p 443:443

To replace the list or clear it, use --publish-reset:

yeet service set vaultwarden --publish-reset -p 443:443
yeet service set vaultwarden --publish-reset

Use yeet info vaultwarden before changing ports.

Service networking does not work

First confirm the service has the network mode you expect:

yeet info <svc>
yeet ip <svc>

For svc networking, yeet info and yeet ip should show private service-network addresses. For lan, the host network must support macvlan and DHCP. A WAN-only VPS usually should not use lan.

If the service was running before a network change, restart it:

yeet restart <svc>

See Networking before changing modes on a shared host.

A service has no outbound internet

Check the service network mode first:

yeet info <svc>
yeet ip <svc>

Use the mode to choose the next check:

  • svc and svc,ts use the catch host's gateway for ordinary internet access. Confirm the catch host itself can reach the internet.
  • lan and lan,ts use the DHCP gateway and DNS from the LAN or VLAN. Check that network's router, DHCP lease, and ACLs.
  • Plain ts is tailnet-only unless you configure a Tailscale exit node. Add svc, use --ts-exit=<exit-node>, or run yeet tailscale <svc> -- set --exit-node=<exit-node>.

Yeet DNS names do not resolve

Yeet DNS only publishes names for services and VMs using svc networking. Without a svc address, there is no record to publish.

Check the resolver from a service or VM on the service network:

dig @192.168.100.1 <svc> A +short
dig @192.168.100.1 <svc>.yeet.internal A +short

If the query returns nothing, confirm yeet info <svc> shows a service-network IP. See DNS for records and resolver behavior.

VM payloads do not start

VMs require a KVM-capable Linux catch host. Check the host:

yeet ssh -- sh -lc '
test -e /dev/kvm && echo kvm=yes || echo kvm=no
test -e /dev/net/tun && echo tun=yes || echo tun=no
for c in qemu-img zstd e2fsck resize2fs mount umount ip; do
  command -v "$c" >/dev/null && echo "$c=yes" || echo "$c=no"
done
'

If KVM is missing, use containers, binaries, scripts, and cron jobs on that host. If KVM and TUN/TAP exist but tools are missing on Debian/Ubuntu, interactive yeet init can ask before installing them:

yeet init root@<machine-host>

See VMs for image cache and console diagnostics.

Binary architecture mismatch

Build binaries for the catch host architecture. If the CPU cannot execute the file, the service cannot start it:

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

Use the host's actual architecture when it is not amd64.

edit fails without a TTY

yeet edit and yeet env edit launch an editor. Run them from an interactive shell.

Staged changes are confusing

Inspect the staged payload:

yeet stage <svc> show

Apply it:

yeet stage <svc> commit

Or discard it:

yeet stage <svc> clear

copy destination errors

One side of a copy must be remote, such as <svc>:path or <vm>:path.

Examples:

yeet copy ./config.yml <svc>:config/config.yml
yeet copy ./configs/ <svc>:config/
yeet copy <vm>:"/var/log/*.log" ./logs/

Remote service paths are relative to the service data directory.

Service type mismatch

Yeet fixes a service type after the first install. If you installed a service as Compose, you cannot later replace it with a binary using the same service name.

Choose a new service name, or remove the old service first:

yeet rm <svc>

Use --clean only when you also want to delete the service data and remove the local yeet.toml entry. Data deletion is separate from retrying a remove.