Current behavior
--force means two different things on stop and rm:
vm stop --force maps to StopTimeoutSeconds = -1 and terminates the VMM immediately (SIGTERM → grace → SIGKILL) — see the Stop handler in cmd/vm/lifecycle.go.
vm rm --force only means "allowed to delete a running VM": DeleteAll calls the backend's default stopOne, which runs the graceful path with the default 30s window (hypervisor/stop.go DeleteAll → hypervisor/firecracker/stop.go stopOne → gracefulStop). The RM handler never applies the -1 mapping.
Why this hurts
On Firecracker the only graceful mechanism is SendCtrlAltDel, delivered through the emulated i8042 controller. A guest kernel built without CONFIG_SERIO_I8042 / CONFIG_INPUT_KEYBOARD (typical for minimal or sandbox-oriented guests) can never observe it, so every vm rm --force of a running FC VM burns the full 30s before escalating:
WRN VM ... did not shut down within 30s, escalating func=firecracker.GracefulStop
INF deleted: ... func=cmd.vm.rm
Cloud Hypervisor is mostly unaffected: its graceful stop is an ACPI power button, which minimal guests do handle.
Ecosystem convention is also that force means "now": docker rm -f kills immediately, virsh destroy is a hard power-off. Callers wanting fast teardown currently need vm stop --force <vm> && vm rm --force <vm> as a workaround (two invocations).
Proposal
Make the RM handler apply the same mapping as Stop: with --force, set StopTimeoutSeconds = -1 so the stop-before-delete goes straight to forceTerminate.
- Default
vm rm (refusing running VMs) stays unchanged.
- Users who relied on
rm --force performing a guest-graceful shutdown first can run vm stop --timeout N && vm rm; worth a changelog line since observable behavior changes.
Alternative considered: a --timeout flag on rm (like stop). Works, but leaves the --force vocabulary inconsistency in place.
Related observation (can split into its own issue)
While testing teardown inside a container whose PID 1 does not reap orphans, an escalated SIGKILL left the VMM as a zombie; the stop path's liveness check kept treating it as alive (stop before delete: timeout after 5s) and the delete could never complete. Treating state-Z processes as dead in the liveness check would make teardown robust to non-reaping supervisors. (Environment workaround: run with an init that reaps, e.g. docker run --init.)
Current behavior
--forcemeans two different things onstopandrm:vm stop --forcemaps toStopTimeoutSeconds = -1and terminates the VMM immediately (SIGTERM → grace → SIGKILL) — see the Stop handler incmd/vm/lifecycle.go.vm rm --forceonly means "allowed to delete a running VM":DeleteAllcalls the backend's defaultstopOne, which runs the graceful path with the default 30s window (hypervisor/stop.goDeleteAll→hypervisor/firecracker/stop.gostopOne→gracefulStop). The RM handler never applies the-1mapping.Why this hurts
On Firecracker the only graceful mechanism is
SendCtrlAltDel, delivered through the emulated i8042 controller. A guest kernel built withoutCONFIG_SERIO_I8042/CONFIG_INPUT_KEYBOARD(typical for minimal or sandbox-oriented guests) can never observe it, so everyvm rm --forceof a running FC VM burns the full 30s before escalating:Cloud Hypervisor is mostly unaffected: its graceful stop is an ACPI power button, which minimal guests do handle.
Ecosystem convention is also that force means "now":
docker rm -fkills immediately,virsh destroyis a hard power-off. Callers wanting fast teardown currently needvm stop --force <vm> && vm rm --force <vm>as a workaround (two invocations).Proposal
Make the RM handler apply the same mapping as Stop: with
--force, setStopTimeoutSeconds = -1so the stop-before-delete goes straight toforceTerminate.vm rm(refusing running VMs) stays unchanged.rm --forceperforming a guest-graceful shutdown first can runvm stop --timeout N && vm rm; worth a changelog line since observable behavior changes.Alternative considered: a
--timeoutflag onrm(likestop). Works, but leaves the--forcevocabulary inconsistency in place.Related observation (can split into its own issue)
While testing teardown inside a container whose PID 1 does not reap orphans, an escalated SIGKILL left the VMM as a zombie; the stop path's liveness check kept treating it as alive (
stop before delete: timeout after 5s) and the delete could never complete. Treating state-Z processes as dead in the liveness check would make teardown robust to non-reaping supervisors. (Environment workaround: run with an init that reaps, e.g.docker run --init.)