MacVisor

High availability, VM Watchdog and health checks

Have MacVisor start a VM again after a crash, restart a guest that stopped responding with VM Watchdog, or restart a VM when your health check keeps failing. Limits, what a restart does and risks, and how to tell why a VM restarted.

MacVisor can keep a VM running without you watching it, in three ways. Each is set per VM, and each is off until you turn it on.

Acts whenRestarts at mostSet in
High availabilityThe VM crashes, stops with an error, or fails to start10 times an hourVM Settings → General → Startup, mvz ha
VM WatchdogThe guest stopped responding, as MacVisor's guest agent reports it, or the guest's QEMU guest agent where MacVisor's can't run3 times a day (Normal), once (Low)Under High Availability, mvz ha --watchdog
Health checkA command of yours keeps failing: your service stopped answeringOnce a dayVM Settings → Advanced → Health Check, mvz hooks <vm> check

VM Watchdog acts only while high availability is on, and needs MacVisor's guest agent or a QEMU guest agent in the VM. A health check's restart works with or without high availability.

When to use what

You wantUse
The VM back after a crash or a failed startHigh availability
To know when a service in the VM stops answeringA health check without restart, with a When It Fails, Run command that tells you
The VM restarted when its service stays downA health check with Restart the VM when it keeps failing
A frozen guest restarted, without writing a checkVM Watchdog, with high availability
To be told when a VM stops, for any reasonAn After the VM Stops hook, or mvz events --follow
An appliance shut down its own wayA shutdown command

High availability

With high availability on, MacVisor's background service starts the VM again when:

  • it crashes: the VM's process ended while it ran;
  • it stops with an error from Virtualization;
  • it fails to start.

Turn it on in VM Settings → General → Startup → High Availability, for every VM at once in Settings → VMs, or with mvz ha <vm> on. A VM with it on carries an HA badge in the VM list and HIGH AVAILABILITY beside its name on the Overview. It's independent of Start at Login: it watches the VM from the moment it runs. Templates can't have it.

mvz ha web on
mvz ha web           # what it's set to, and what VM Watchdog is doing
mvz ha web off

Timing and limits

  • The wait before each restart is 5 seconds, then 15, 30, 60, 120 and 300 seconds, and 300 from there on.
  • A VM that ran for 10 minutes before it stopped starts the waits afresh, at 5 seconds.
  • No more than 10 restarts in an hour. After the tenth, high availability gives up and the VM stays stopped. Tasks says so: High availability gave up on "web", "web" crashed again after 10 restarts within an hour, so it stays stopped. Start it yourself once it's fixed: high availability watches it again from then. Starting it yourself counts its restarts afresh, so a crash later in that hour is restarted again.
  • The VM comes back the way it was last started, with its window or without. A suspended session is resumed.

Each restart is listed in Tasks and Task History as Restarted by high availability: "web", with the attempt and the reason: Attempt 2: it crashed. mvz events lists them as ha-restart and ha-gave-up.

When the background service restarts

High availability keeps its promise across restarts of MacVisor's background service: an app update, mvz service restart, or a crash of the service. VMs keep running meanwhile, in their own processes, and the service remembers its restarts and their count for the hour on disk:

  • a restart that was waiting its turn when the service stopped is still made, after the same wait;
  • a VM that crashed while the service was away is listed as "web" stopped unexpectedly in Tasks, and started again under the same limits.

It doesn't bring VMs back after you log out or restart the Mac, which are stops you meant. Use Start at Login for that.

Stops you ask for

High availability never starts a VM again after a stop someone meant:

  • Shut Down, Power Off, Suspend and the stop half of Restart, from the app, the menu bar, the window or mvz;
  • a shutdown from inside the guest, and a reboot from inside the guest that ends the VM;
  • reverting to a snapshot, and moving the VM to the Trash;
  • the Mac logging out or shutting down.

Any of these also calls off a restart that's waiting its turn, as does starting the VM yourself.

High availability only sees the VM's process. A guest that has frozen inside a running VM, or whose service has stopped, looks fine to it. That's what VM Watchdog and health checks are for.

VM Watchdog

VM Watchdog restarts a VM whose guest stops responding, as MacVisor's guest agent reports it, with no check to write. In a guest that can't run MacVisor's agent but has a QEMU guest agent, such as Home Assistant OS, the QEMU guest agent's answers are its heartbeats instead. It needs one of the two in the VM. It's off by default, and acts only while high availability is on.

Turn it on in VM Settings → General → Startup: VM Watchdog shows under High Availability while that's on, with a Sensitivity. Or with mvz ha:

mvz ha web on --watchdog           # Normal sensitivity
mvz ha mac on --watchdog low
mvz ha web --no-watchdog

--monitoring and --no-monitoring, its earlier names, still work.

Normal (default)Low
Not responding for this long before a restart10 minutes30 minutes
Never in the first … after a start or restore10 minutes20 minutes
Restarts at most3 in 24 hours1 in 24 hours

There's no faster setting: a restart is a last resort. AI sandboxes and templates can't have VM Watchdog.

What it watches

MacVisor watches for two signs that a guest has stopped responding. It shows them, and mvz events lists guest-not-responding and guest-responding, whether VM Watchdog is on or not.

A macOS guest's screen not responding. The MacVisor Agent checks every 30 seconds that the guest's WindowServer still answers. It reads no window names and needs no Screen Recording permission, so it raises no prompt. When WindowServer doesn't answer while the agent's heartbeats keep arriving, the guest's kernel, network and agent are fine, but its screen and apps have stopped. The Overview's Guest Tools card says Guest screen not responding since 11

AM.

A guest gone silent. For any guest with the MacVisor Agent: its heartbeats stopped, and the VM has done almost no disk or CPU work for 2 minutes since. A guest that's rebooting, updating or busy reads and writes the disk and uses CPU, so it doesn't count. The card says Guest not responding since 11

AM.

The QEMU guest agent as heartbeats. While MacVisor's agent isn't connected, each answer from a guest's QEMU guest agent counts as a heartbeat, an error too: a guest frozen for a backup refuses most commands but is alive. When it stops answering, the same rule decides. While MacVisor's agent is connected, the QEMU guest agent's answers are ignored. Before a restart, a guest that still answers a ping on its address isn't restarted: one whose QEMU guest agent simply isn't running (Home Assistant OS doesn't start it on MacVisor yet) stays up.

  • mvz show <vm> has a Guest status row that says the same, and --json a guestLiveness object.
  • A pause, a suspend, a restore, a snapshot, a gap in the heartbeats or a disconnect starts both counts again. The Mac's sleep isn't counted: a silence that began before it goes on from the wake, and a guest that doesn't answer after the wake is silent from then.
  • Before restarting a silent guest, MacVisor checks whether it answers another way: its QEMU guest agent, or a ping to its address, which MacVisor knows also while the app is closed. If either answers, it isn't restarted, and the card says VM Watchdog didn't restart it: its agent is silent, but the guest answers.
  • A guest without the MacVisor Agent gives neither sign. Use a health check on this Mac for it.

Its limits

  • Restarts per day by sensitivity, counted together with the health check's. Once the limit is reached, MacVisor only reports, until someone starts or restarts the VM from MacVisor or mvz, which lifts it. The card says VM Watchdog won't restart it again yet: the limit is one restart in 24 hours (a Start or Restart from MacVisor or mvz lifts it).
  • Not while you're using its window, as for health checks: restart postponed: you're using its window.
  • One VM at a time, one per 10 minutes across all VMs.
  • After the Mac wakes, a guest's silence is measured from the wake, so one that never answers again is restarted after the usual time, not at once.

The Overview's High Availability card says what it will do: VM Watchdog restarts it at 11

AM if its guest still isn't responding, VM Watchdog is restarting it now, and afterwards Restarted by VM Watchdog at 11
AM; next allowed after 3
PM
. mvz ha <vm> says it in words too:

High availability is on for web: it's started again when it crashes, stops with an error or fails to start; a stop you ask for is honoured.
VM Watchdog is on for web (normal sensitivity): MacVisor restarts it when its guest has stopped responding for 10 minutes, at most 3 times a day, and not while you're using its window.
VM Watchdog now: restarts it at 11:45 AM if its guest still isn't responding.

mvz ha <vm> --json has highAvailability, vmMonitoring (enabled, sensitivity) and, while the VM runs, monitoringStatus.

Evidence

Before it restarts a VM, VM Watchdog saves what it can to MacVisor's logs folder, ~/Library/Application Support/MacVisor/logs/, within a minute in all:

  • The VM's last frame, if its window is open: <VM id>-monitoring-<time>-frame.png.
  • A spindump of the guest, for a macOS guest whose screen stopped responding, over SSH with passwordless sudo: <VM id>-monitoring-<time>-spindump.txt, at most 64 MB. It's taken with sudo -n spindump -notarget 5 -stdout and written straight to the Mac, with no file in the guest. Without passwordless sudo it's skipped, and the evidence says why.
  • What the agent last reported, in the VM's hooks.log, with the names of the files.

<time> is in UTC, such as 20261102-113500. The newest six evidence files of each VM are kept. The restart's task names them: Evidence in MacVisor's logs folder: ….

Health checks

A health check is one command per VM that exits 0 while the VM is healthy. MacVisor runs it every so often while the VM runs. When it has failed for long enough, it can run a command of yours and, with Restart the VM when it keeps failing, restart the VM, with or without high availability.

Where it runs

RunsHowNeeds
On This Mac (the default)Like a hook, with MVZ_EVENT=check and MVZ_HOOK=checkNothing in the guest. Use it for appliances, and for anything you can test from outside, such as a web page.
In the VMIn the guest over SSH, as mvz exec runs a commandSSH into the VM: Remote Login on a macOS guest, an SSH server and your keys elsewhere

A check on this Mac that reaches the VM's address needs MacVisor's Local Network access, as any app does. Without it every run fails with "No route to host": see No route to host. Name the VM as <vm>.mvz, or by "$MVZ_IP", the address MacVisor last learned for it. MacVisor learns it also while the app is closed: see What a hook gets.

Timing

  • Interval: 30 to 3600 seconds, 60 by default.
  • Time limit of a run: 30 seconds, or the interval less 5 seconds if that's shorter.
  • Only while the VM runs. A run still going when the next is due is skipped.
  • After a start or a restore, the first run waits 15 minutes for a check on this Mac and 10 for a check in the VM, so a booting guest isn't judged. A resume from a pause isn't a start.

Passed, failed, or can't run

ResultWhenCounts
PassedExit 0Ends a run of failures
FailedAny other exit status, or the time limitToward the failure time
Can't runIn the VM: SSH couldn't connect or sign in, or Remote Login is off. On this Mac: the command couldn't be started. A command in the VM that exits 255, as ssh does, counts as can't run too.Neither: it starts the count again

Failures count from the first one, as long as they come one after another:

  • Any change in the VM's state starts the count again, and so does a gap between runs, such as the Mac sleeping.
  • In the VM, a failure only counts while the MacVisor Agent's heartbeats keep arriving, so the guest is otherwise up. A reboot, an update, a pause or the agent reconnecting starts the count again, and the status says why the failure wasn't counted. In a VM without the agent, failures count as on this Mac.

After 20 minutes of failures on this Mac, or 10 in the VM, the check has failed: its When It Fails, Run command runs, once, whether or not the check restarts the VM, so it can tell you. mvz events lists check-failed then, and check-recovered at the next pass.

Restarting the VM

With Restart the VM when it keeps failing on (--restart), MacVisor restarts the VM once the check has failed for those 20 or 10 minutes. High availability doesn't need to be on.

  • At most once a day per VM: one restart in 24 hours, counted together with VM Watchdog's. After that the check only reports, until someone starts or restarts the VM from MacVisor or mvz, which lifts the limit.
  • Not while you're using the VM: while its window is in front, MacVisor is the active app and the Mac has had keyboard or pointer input in the last 10 minutes, the restart is postponed, not dropped, and the status says so: restart postponed: you're using its window.
  • One VM at a time: across all VMs, MacVisor restarts one VM per 10 minutes, health checks and VM Watchdog together. A problem on the Mac's side, such as its network or a NAS the checks probe, fails every check on this Mac at once, and restarting every VM wouldn't fix it. The status says MacVisor restarted another VM a moment ago, and restarts one VM at a time.
  • Checked again right before: the VM is still running in the same boot, the check is unchanged and still set to restart, and MacVisor isn't busy with the VM.

See What a restart does.

Set it in VM Settings

VM Settings → Advanced → Health Check, above Hooks:

  • Command: with the hint "Command that exits 0 while the VM is healthy". Test runs the saved check once now, where it would run, and shows the result. It isn't counted. The status line under it says how the check is doing.
  • Runs: On This Mac or In the VM.
  • Interval: seconds between runs.
  • Restart the VM when it keeps failing: "At most once a day, and not while you're using its window."
  • When It Fails, Run: a command run on this Mac once the check has failed, with its own Test. It runs like a hook, with MVZ_EVENT=check-failed.

The fields under the command show once there's a command, or a When It Fails, Run command set with mvz, so nothing is ever hidden. Changes save by themselves. Emptying the command removes the check.

Set it with mvz hooks

mvz hooks web check set 'curl -fsS -m 10 http://web.mvz/health' --interval 60 --restart
mvz hooks db check set 'systemctl is-active --quiet postgresql' --in-guest
mvz hooks web check set --interval 120   # change only the interval
mvz hooks web check set --on-fail 'curl -fsS -m 10 -d "$MVZ_VM_NAME is unhealthy" https://ntfy.sh/my-vms'
mvz hooks web check set --no-on-fail     # run nothing when it fails
mvz hooks web check              # the check and how it's doing
mvz hooks web check test         # run it once now; not counted
mvz hooks web check remove
  • On a VM with no check, check set needs the command, and what you leave out is the default: on this Mac, every 60 seconds, no restart. Options alone are refused: "web has no health check to change, so check set needs its command: …".
  • On a VM that has a check, check set changes only what you give, a new command, --in-guest or --on-mac, --interval N, --restart or --no-restart, and keeps the rest. check set --interval 120 keeps the command and the restart, and check set --no-restart turns the restart off. With nothing to change it's refused.
  • --on-fail "<command>" sets the command run when the check has failed (the When It Fails, Run field, the check-failed hook), and --no-on-fail removes it. Given alone, they leave the check as it is.
  • --in-guest with --on-mac, or --restart with --no-restart, is refused: "Give --restart or --no-restart, not both." Put -- before a command that starts with an option.

mvz hooks <vm> check, the Hooks list and mvz show say how it's doing, for example:

check       every 60 s on this Mac, restarts it when it fails: curl -fsS -m 10 http://web.mvz/health
            failing since 1:12 PM (exit 7: curl: (7) Failed to connect to web.mvz port 80); restart postponed: you're using its window

Other states read first run at 1:27 PM, after the VM's start, passing (last run 1:30 PM), can't run: …, not counted: the guest agent's heartbeats stopped, and restarted at 1:32 PM; next allowed after Nov 3, 1:32 PM. The Overview's High Availability card, under Guest Tools, shows the same in a line such as Health check failing since 1

PM.

Example checks

Checks thatCommandRuns
A web service answerscurl -fsS -m 10 http://web.mvz/healthOn This Mac
A port is opennc -z -G 5 db.mvz 5432On This Mac
A Linux service is upsystemctl is-active --quiet postgresqlIn the VM
A process existspgrep -x nginxIn the VM
A log is still being written, so the work goes onfind /var/log/jobs/run.log -mmin -15 | grep -q .In the VM

A check of a log that must keep changing catches a guest whose work has stopped, whatever hung, on any OS. On macOS, find ~/Library/Logs/job.log -mmin -15 | grep -q . does the same.

What a restart does

A health check's and VM Watchdog's restarts go through MacVisor's own Restart:

  1. The evidence goes to the VM's hooks.log: the reason, since when, and the check's last output or the agent's last report. Tasks shows Restart "web": Waiting for the guest to shut down: its health check had failed since 1:12 PM.
  2. The guest is shut down, its own way if it can be:
    • with the VM's shutdown command, if it has one;
    • else, for a check in the VM of a Linux or FreeBSD guest (or a macOS guest whose agent isn't connected), and for a macOS guest whose screen stopped responding, with sudo -n shutdown -h now over SSH (-p on FreeBSD), which needs sudo without a password;
    • else, or if that's refused, with MacVisor's usual Shut Down.
  3. After 3 minutes, a guest that's still running is looked at:
    • a silent guest is powered off;
    • one that does almost no disk or CPU work over the next 30 seconds is taken as hung and powered off;
    • one that's still at work, such as installing updates as it shuts down, is never powered off. It gets up to 10 minutes in all, as any Restart does. If it still hasn't stopped, MacVisor leaves it running and Tasks says "web" didn't shut down within 10 minutes, so it wasn't restarted or powered off: it was still at work. Shut it down or power it off, then start it again.
  4. The VM starts again, with its window or without, as it was started. If the start fails and high availability is on, high availability tries again, within its limits.
  5. Tasks says why, and macOS shows a notification: Restarted "web": its health check had failed since 1:12 PM; it was powered off after its shutdown didn't finish. The reason reads "its screen had stopped responding" or "it had stopped responding" for VM Watchdog.

The restart isn't a stop you asked for, so high availability's own counts stay as they are. A Cancel of its task before the VM starts again leaves the VM shut down.

What it risks

A restart is meant for a VM that has stopped working anyway, but it can still cost you:

  • Power Off loses what the guest hadn't saved. Unsaved documents are gone, and files open for writing, such as a database's, may need recovery at the next start, or may be damaged. MacVisor powers off only a guest that's idle or silent after 3 minutes, never one still at work.
  • A check that tests the wrong thing restarts a healthy VM, once a day. Run a new check without Restart the VM when it keeps failing for a few days first, with a When It Fails, Run command that tells you, and turn restarting on once it only fails when it should.
  • A problem on the Mac's side fails every check on this Mac: its network, Local Network access, or a NAS a check probes. MacVisor restarts one VM per 10 minutes, so this is slow, but not stopped.
  • A guest that was about to recover is restarted anyway. VM Watchdog's waits (10 or 30 minutes, after a grace of 10 or 20) are long for that reason; choose Low for a guest that's sometimes slow.

For a VM whose data matters, such as a database server, prefer a check that only tells you, and keep snapshots and backups.

Troubleshooting

A VM restarted by itself

Find out who restarted it:

  • Tasks and Task History. Restarted by high availability: "web" with the attempt and the reason ("it crashed", "it stopped with an error", "it didn't start"), or Restarted "web" with the health check's or VM Watchdog's reason.
  • mvz events web shows ha-restart, or stopped (restart) then started for a health check's or VM Watchdog's restart, while the service remembers them.
  • The VM's hooks.log has a watchdog: restarting the VM entry with the evidence, for a health check's or VM Watchdog's.
  • The service log (mvz service logs --tail 200) has lines that start High availability:, Watchdog: or VM Watchdog:.

To stop it happening again, turn off the part that acted: mvz ha <vm> off, mvz ha <vm> --no-watchdog, or mvz hooks <vm> check set … --no-restart.

Reading hooks.log

~/Library/Application Support/MacVisor/logs/<VM id>-hooks.log has every hook run, every failed check run and the first pass after failures (passes alone aren't written), checks that couldn't run, and each restart's evidence:

2026-11-02T12:32:10.412Z watchdog: restarting the VM: its health check had failed since 2026-11-02T12:12:04.950Z
  evidence:
    …

Times are in UTC. mvz show <vm> --json | jq -r .id gives the VM's id.

Guest screen not responding

The guest's WindowServer hasn't answered the MacVisor Agent since that time, while the rest of the guest is fine: SSH, mvz exec and the network still work, but the screen and apps don't move.

  1. If you want to report it, take a spindump first: in mvz shell <vm>, run sudo spindump -notarget 5 -file /tmp/spindump.txt, and copy the file out.
  2. Restart the VM. Its Shut Down may not finish, since its apps can't quit: in mvz shell <vm>, run sudo shutdown -r now, or use Power Off if nothing in it needs saving.
  3. If it happens again, turn on VM Watchdog for the VM, so it's restarted with the evidence saved.

Guest not responding

The agent's heartbeats stopped and the VM is doing almost no work. Look at its window or its console: a guest at a kernel panic or a firmware screen looks like this. If nothing responds, Power Off and start it again.

Health check can't run

The status says why, such as Health check can't run: SSH couldn't connect to the guest or sign in, or can't run: Remote Login is off in web. Fix SSH, then try the check by hand: mvz exec <vm> '<command>', and mvz hooks <vm> check test.

A restart doesn't come

The Overview's High Availability card, mvz ha <vm> or mvz hooks <vm> check says why:

It saysWhat to do
restart postponed: you're using its windowNothing: it restarts once you've been away from the window for 10 minutes
MacVisor restarted another VM a moment ago, and restarts one VM at a timeNothing: it waits its turn
VM Watchdog restarts the VM only while High Availability is onmvz ha <vm> on
won't restart it again yet: the limit is …Restart the VM yourself, which lifts the limit
MacVisor is busy with the VM (…)Wait for that task to finish
VM Watchdog didn't restart it: its agent is silent, but the guest answersThe guest isn't hung. Look at it, or restart it yourself
MacVisor's record of watchdog restarts … can't be readFix ~/Library/Application Support/MacVisor/watchdog-restarts.json, or move it to the Trash

See also VM hooks and Always-on host.