Operate a Metal host
Use the VM ID and operation ID to connect API responses, records, logs, units, namespaces, and datasets.
Before you retry
Read GET /v1/vms/{id}. Compare desired and observed generations, then inspect the phase and error. A timeout or stale Atlas view does not prove rejection.
Correct the host fault and let reconciliation retry. Keep record files, saved state, and migration data intact. Do not create duplicate units, namespaces, or datasets, or print private keys and signed URLs.
Choose a check
| Symptom | Start with |
|---|---|
| Guest state or console | metal-vm@<id>.service, its journal, Firecracker socket, console adoption. |
| Network | Namespace links, routes, firewall, and mesh health. |
| Storage or sync | zpool status, zfs list, configured pool, Metal journal. |
| Snapshot or migration | Status, checkpoint, free storage, and both sides' logs. |
Network setup fails
A VM stays failed or has no expected connectivity.
Check
- Read
GET /v1/vms/{id}andobserved.error. - Run
ip netns list,ip -n metal-<id> link, andjournalctl -u metal.service --since "15 minutes ago".
Expected: Logs name the VM, operation, and failed network step. The namespace and links show the partial state.
Recover: Correct the host interface, route, or firewall fault. Let reconciliation call Network.Ensure again.
Do not create an alternate namespace for the same VM. Do not change stored desired network JSON by hand.
Firecracker and systemd report different states
Metal reports a state that does not match metal-vm@<id>.service.
Check
- Run
systemctl status metal-vm@<id>.service,systemctl show metal-vm@<id>.service, andjournalctl -u metal-vm@<id>.service. - Check the Firecracker socket link under
/run/metal.
Expected: Metal inspection records each partial fault. Systemd remains the process owner.
Recover: Correct the unit or runtime-file fault. Wake reconciliation with the same desired request when required.
Do not start Firecracker directly. Do not remove a running unit's files.
Serial console is blank or unavailable
The console closes, or it has no output.
Check
- Confirm
/run/metal/consoles/<id>and theTTYPath,StandardInput, andStandardOutputsettings. - Read
NotifyAccess,FileDescriptorStoreMax, andFileDescriptorStorePreserveformetal.service. - Run
metald version. Confirm that the build supports the descriptor store.
Recover: Correct the unit settings, run systemctl daemon-reload, and restart the VM through the API.
Do not restart metal-vm@<id>.service or remove a running guest's console link.
VMs stop when metald stops
Every VM on the host fails with exit code 156 when metal.service stops or restarts.
Firecracker holds the PTY slave as its controlling terminal. A closed PTY master sends SIGHUP to Firecracker. systemd keeps the master in the descriptor store, so the PTY survives a metald exit.
Check
- Run
systemctl show metal.service -p NFileDescriptorStore. Expect one descriptor per running VM.0means no protected consoles. - Read the startup log. Expect
descriptor_store_available=trueandadopted_console_countequal torunning_unit_count. - Confirm
NotifyAccess=main,FileDescriptorStoreMax, andFileDescriptorStorePreserve=yesin the unit.
If descriptors disappear, use this diagnostic sequence:
- Run
systemd-analyze log-level debug. - Start the VM through the API.
- Read the journal for
Added fdandReceived EPOLLHUP on stored fd. - Restore the log level with
systemd-analyze log-level info.
Recover: Correct the unit settings and run systemctl daemon-reload. Start each VM again through the API. A running VM keeps its unprotected console until it starts again.
Do not start or restart metal-vm@<id>.service by hand.
Disk usage or ZFS inspection fails
/v1/sync fails or a VM operation reports storage inspection failure.
Check
- Run
zpool status,zfs list, andjournalctl -u metal.service --since "15 minutes ago". - Check the configured pool name.
Expected: Metal returns no invented capacity value. Logs identify the failed inspection.
Recover: Restore the pool and retry synchronization or reconciliation.
Do not publish a guessed capacity. Do not destroy a dataset until ownership is confirmed.
Snapshot upload stays pending or completing
Atlas does not receive completed artifact hashes, or multipart completion does not finish.
Check
- Read
GET /v1/snapshots/{id}. - Check the snapshot activity time and Metal logs.
- In Atlas, check image status and upload IDs.
Expected: Metal reports pending, uploading, completed, or failed with progress. Atlas keeps retry identifiers.
Recover: Correct network or object storage access. Retry from Atlas. The Metal start request and the multipart completion are safe to repeat.
Do not log signed URLs. Do not remove staging during an active retry.
Machine image cleanup fails
An image is Available or Cleaning, but Metal staging remains.
Check
- Read the Atlas image status.
- Call
GET /v1/snapshots/{id}. - Check Metal cleanup logs and ZFS state.
Expected: The public image objects are complete before Atlas requests staging deletion.
Recover: Send the same DELETE /v1/snapshots/{id} request or wait for stale-staging cleanup.
Do not delete public image objects. Do not remove a ZFS snapshot that another clone uses.
Metal cannot complete graceful shutdown
metald does not stop within the configured shutdown bound.
Check
- Read
journalctl -u metal.service. - Check shutdown, upload cancellation, and wait-group log entries.
- Check whether the process still accepts requests.
Expected: Shutdown cancels upload roots, stops background owners, and waits for their bounded completion. Guest systemd units remain active.
Recover: Allow the configured bound. Stop the daemon service again after the worker exits. Use process termination only after you preserve logs.
Do not stop metal-vm@*.service units as part of daemon shutdown. Do not remove upload state while a worker is active.
Automatic idle shutdown does not stop or restore a VM
An idle VM does not reach stopped, or new IP traffic does not restore it.
Check
- Read
GET /v1/vms/{id}. - Check
desired.compute.sleep_after_idle_seconds,observed.state, andobserved.error. - Run
systemctl show -p MainPID --value metal-vm@<id>.service. An automatically stopped VM reports0. - Check
<base_dir>/machines/<id>/saved-state/metadata.json,bpftool map show,ip netns exec metal-<id> bpftool link show, and the metald journal.
Expected: Host-to-guest IPv4 or IPv6 traffic updates activity_by_user_id. When observation is active, it also writes the VM user ID to traffic_events.
Recover: Retry the connection because the first packet can be lost during restoration. Correct the timeout or the host eBPF fault and let reconciliation retry.
Do not delete a saved state that Metal must restore. Do not start Firecracker directly.
Limits and recovery
Metal logs are local to the host, and the observed VM record is only a host-side report. A clean desired/observed generation match does not prove guest application health. A daemon exit without a working systemd descriptor store can stop running guests even though the VM records remain intact.
Source code and tests
- VM reconciliation records phases, errors, and cleanup progress.
- Daemon startup reports console adoption.
- Daemon shutdown defines worker and listener cleanup.
- Network allocator repairs VM host networking.
- Migration engine owns destination and source records.