A guided path from "what is a VM?" to planning a migration: how virtualization and its networking work, where it goes wrong, and how Zaryn and zev show every VM, the switch port it really uses, and what breaks before it does. Pictures first, every step explained.
This tutorial takes you from the very beginning to the advanced features, one chapter at a time. The first stage explains virtualization itself, so skip ahead if you know it already: the roadmap above jumps to any chapter, and the bar shows where you are. The full reference is [Virtualization](/docs/en/virtualization).
## 1. What virtualization is
```diagram
type: graph
title: One server, many machines
alt: A physical server runs a hypervisor, which runs several virtual machines and a container, each with its own CPUs, memory, disks and network cards carved out of the server's
start: A physical server
height: 52
---
box host pve1: one physical server | 2 2 96 48 | accent
node hw | CPUs, memory, disks, NICs | 50 42 | server | | The real hardware: the hypervisor shares it out
node hv | hypervisor | 50 26 | collector | accent | Proxmox VE, ESXi, Hyper-V, KVM, AHV…: runs the guests and keeps them apart
node web | web1 | 14 10 | vm | dim | A virtual machine: its own operating system, vCPUs, memory, disks and network cards
node db | db1 | 38 10 | vm | dim | Another VM: it can't see web1's memory or disks
node erp | erp | 62 10 | vm | dim |
node ct | ct-cache | 86 10 | host | dim | A container (LXC): lighter, shares the host's kernel
link hw hv | | path
link hv web | | dim
link hv db | | dim
link hv erp | | dim
link hv ct | | dim
step Guests start | The hypervisor gives each guest a slice of the server: 4 vCPUs here, 8 GB there. Ten small servers now fit in one box.
node web | normal
node db | normal
node erp | normal
node ct | normal
link hv web | new
link hv db | new
link hv erp | new
link hv ct | new
```
A **hypervisor** turns one physical server (a **host**) into many isolated **virtual machines** (VMs), each with its own operating system and its own virtual CPUs, memory, disks and network cards. **Containers** do the same more lightly. Several hosts form a **cluster**, so a VM can move to another host while it runs (**live migration**) or restart there when its host fails (**HA**).
```diagram
type: cards
title: The platforms Zaryn reads
---
🟧 | Proxmox VE | Open source, KVM and LXC, popular since VMware's licensing changes |
🟦 | VMware vSphere | ESXi hosts managed by vCenter |
🐧 | KVM (libvirt) | Plain Linux hosts with virsh |
🪟 | Hyper-V | Windows Server, alone or in a failover cluster |
🔶 | oVirt / OLVM | Red Hat's and Oracle's KVM managers |
🟩 | Nutanix AHV | Hyperconverged, through Prism Central |
☁️ | OpenStack | A private cloud: Nova, Neutron, Cinder |
```
## 2. How a VM reaches the network
```diagram
type: graph
title: Four hops to the real network
alt: web1's card is on the bridge vmbr0 with VLAN 20; the bridge uses the host's uplink eno1, cabled to switch port Gi1/0/5, which must allow VLAN 20
legend: path=web1's frames; down=Dropped
start: Working
height: 40
---
box host pve1 | 2 4 56 32 | accent
node web | web1 | 9 20 | vm | accent | Its card eth0 is plugged into a virtual switch, not a cable
node br | vmbr0 | 27 20 | switch | | The bridge (vSwitch, port group): tags web1's frames with VLAN 20
node up | eno1 | 47 20 | dot | | The host's physical port: its uplink
node sw | sw1 Gi1/0/5 | 82 20 | switch | good | A trunk port: it must allow VLAN 20
link web br | VLAN 20 | path
link br up | | path
link up sw | trunk 1,20 | path
step The port doesn't carry VLAN 20 | Someone removes VLAN 20 from the trunk, or web1 moves to a host cabled to a port without it. web1 is "running", the hypervisor shows green, and nothing reaches it.
link up sw | down | trunk 1,30
node sw | bad
node web | bad
```
A VM's traffic goes **VM → virtual switch → the host's uplink → a physical switch port**. On the way it's usually tagged with a **VLAN** number that keeps networks apart. The last hop is where trouble hides: a **trunk** port only passes the VLANs in its allowed list. The hypervisor can't see that list, and the switch can't see the VM.
## 3. Where it goes wrong
```diagram
type: cards
title: Five problems almost every virtualized site has
---
👥 | Two teams, two views | The network team sees MACs and ports, the virtualization team sees VM names. "Which port does erp-db use?" takes a meeting | warn
🚚 | VMs move, switch configs don't | After a live migration or HA the new host's port may not carry the VLAN | bad
🔌 | Redundancy that isn't | Two cables in a bond, both to one switch: one reboot takes every VM down | bad
📈 | Capacity surprises | Thin disks and overcommitted memory fill up a little every day | warn
🧩 | Migrations without a map | Moving VMs without knowing who talks to whom splits applications | warn
```
None of these show up in a hypervisor's console, because each lives **between** the hypervisor and the network. That gap is what Zaryn fills.
## 4. What Zaryn and zev give you
```diagram
type: graph
title: Two halves, joined
alt: The collector reads the hypervisors' APIs and the switches' configs and neighbors with read-only logins, joins them, and sends the result to Zaryn; you see every VM with its switch port and the checks
legend: path=Outgoing HTTPS; ghost=Read-only
height: 48
---
box Your site | 2 2 62 44 | good
node hv | hypervisors | 12 12 | host | | Read through their APIs with a login that can only look
node sw | switches | 12 36 | switch | | Configs and LLDP/CDP: which host port is cabled to which switch port, and the VLANs it allows
node col | zev collector | 46 24 | collector | accent | One Linux machine running zev: keeps the passwords, joins VMs to switch ports
node z | Zaryn | 80 12 | cloud | accent | Runs the checks: VLAN reach, migrations, redundancy, storage
node you | you | 80 38 | user | good | The Virtual machines tab, and alerts for what needs attention
link hv col | read-only | ghost
link sw col | read-only | ghost
link col z | HTTPS out | path
link z you | | path2
```
**zev** is the program on your collector: it reads, joins and sends. **Zaryn** keeps the history and works out the answers:
```diagram
type: cards
---
🔗 | Every VM's way out | `net0 → vmbr0 VLAN 20 → bond0 → eno1 → sw1 Gi1/0/5 ✓` | accent
🚦 | VLAN reach | Every port a VM's traffic crosses carries its VLAN, or why not | good
🧭 | Where a VM can move | Which hosts keep its network and disks intact | good
💥 | If a switch goes down | The hosts cut off and the VMs that go dark | warn
💾 | Storage | Fill levels, thin disks past their size, "full in about 9 days" | warn
🌊 | Migration waves | Groups of VMs that talk to each other, moved together | accent
```
## 5. What it doesn't do
```diagram
type: cards
title: Honest limits, up front
---
👁 | It only reads | Zaryn never starts, stops or moves a VM. Logins can only look | good
🔎 | Switch checks need LLDP or CDP | Without them, a host's uplinks show as `?` and Zaryn won't guess | warn
🧱 | VLAN lists from Cisco and Huawei | Other switches' allowed-VLAN checks show as unknown | warn
📏 | Up to 2,000 guests per hypervisor | Per device you add: add more devices for bigger estates |
⏳ | Forecasts need a day | Storage trends start after about a day of hourly samples |
```
Exact versions per platform are in [Limits](/docs/en/virtualization#limits). Everything in this tutorial works on the Free plan: a site holds up to 10 devices, and a whole cluster counts as one.
## 6. Make a site and start the collector
```diagram
type: flow
title: Ten minutes to a running collector
---
Make a site | zaryn.xyz/sites → New site, e.g. `hq` | | 🏢
~ Collector and settings
Join | `zev site join zsite_…` on the collector machine | accent | 🔑
~ sudo zev site install
Running | A service: checks in every 30 s, reads every 15 min | good | ▶
```
1. On [zaryn.xyz/sites](https://zaryn.xyz/sites), create a site, for example `hq`.
2. Pick a Linux machine at the site that can reach the hypervisors and switches ([install zev](/docs/en/installation) on it).
3. On the site's **Collector and settings** tab, choose **Set up the collector** and copy the token. Then:
```bash
zev site join zsite_… --server https://api.zaryn.xyz
sudo zev site install
```
The collector only makes **outgoing** connections: no port to open towards it. More in [Network sites](/docs/en/network-sites#2-start-the-collector).
## 7. Give it a read-only login
Every platform works the same way: a login that can only look, stored on the collector, tried once, then added as a device. With Proxmox VE:
```bash
# on a Proxmox node: a user with the built-in read-only role, and a token
pveum user add zev@pve
pveum acl modify / --users zev@pve --roles PVEAuditor
pveum user token add zev@pve zaryn --privsep 0
# on the collector: keep the token, and try it
zev site cred add pve --user 'zev@pve!zaryn' --password-stdin
zev site test 10.0.0.11 --cred pve --vendor proxmox
```
Then, on the site's **Devices** tab, add any node's address with vendor **Proxmox VE** and credential `pve`. One device is the whole cluster.
```diagram
type: cards
title: The login for each platform
---
🟧 | Proxmox VE | API token with PVEAuditor, port 8006 |
🟦 | vSphere | A read-only vCenter role, port 443 |
🐧 | KVM | An SSH user; `virsh -r` only |
🪟 | Hyper-V | An SSH user in Hyper-V Administrators |
🔶 | oVirt / OLVM | ReadOnlyAdmin on the engine |
🟩 | Nutanix | Prism Viewer on Prism Central, port 9440 |
☁️ | OpenStack | The reader role in Keystone, port 5000 |
```
Each platform's exact steps: [Add a hypervisor](/docs/en/virtualization#add-a-hypervisor). The secret never leaves the collector, and [every call it makes](/docs/en/virtualization#what-the-collector-sends) is listed.
## 8. Let the switches see the hosts
```diagram
type: flow
title: Who is cabled to whom
---
Hosts announce | LLDP on each host: "I'm pve1, port eno1" | | 📣
~ the switch hears it
Switch records | sw1 Gi1/0/5 has pve1 eno1 behind it | | 📋
~ the collector reads it
Joined | web1 → … → eno1 → sw1 Gi1/0/5, with its VLANs checked | good | 🔗
```
Add your switches to the site too (with their own read-only logins), and turn on LLDP where it's off:
```bash
apt install lldpd # Proxmox, KVM, oVirt and OpenStack hosts
```
On ESXi, enable LLDP or CDP on the vSwitch or distributed switch. On Windows: `Install-WindowsFeature Data-Center-Bridging`, then `Enable-NetLldpAgent`.
## 9. Your first look
Within a few minutes of adding the hypervisor, the site gets a **Virtual machines** tab:
```diagram
type: stats
title: The top of the tab, at a glance
---
3 | hosts | pve1, pve2, pve3 | |
48 | guests running | of 52 | 92 | good
2.4 : 1 | vCPUs per physical CPU | overcommit | 60 | warn
71% | memory given out | to running guests | 71 |
```
```diagram
type: cards
---
⚠️ | What needs attention | Every risk, worst first. Empty is the goal | warn
🖥 | Hosts | CPU, memory, guests, uplinks and a redundancy badge |
📦 | Guests | Every VM with host, addresses, VLANs; open one for its way out | accent
💾 | Storage | Fill levels and forecasts |
```
Open a VM to see each network card's whole path, for example `net0 → vmbr0 VLAN 20 → bond0 → eno1 → sw1 Gi1/0/5 ✓`. That one line answers the meeting from chapter 3.
## 10. Before you move a VM
```diagram
type: graph
title: Where can erp-db go?
alt: erp-db runs on pve1; pve2 is a safe target; pve3 isn't, because its switch port lacks VLAN 20 and it can't see the Ceph storage
legend: new=Safe; down=Would break
height: 40
---
node vm | erp-db | 12 20 | vm | accent | VLAN 20, disks on Ceph
node p1 | pve1 (now) | 40 20 | host | |
node p2 | pve2 | 80 8 | host | good | Same bridge, VLAN 20 on its switch port, sees Ceph: ✓
node p3 | pve3 | 80 32 | host | bad | ✗ VLAN 20 not on sw1 Gi1/0/6; ceph isn't on pve3
link vm p1 | runs on | path
link p1 p2 | ✓ safe | new
link p1 p3 | ✗ would break | down
```
For every VM, Zaryn checks each other host of the cluster as if the VM had just moved there: the **network** (same bridge, every switch port carrying its VLANs) and the **disks** (shared storage seen, or room for local disks). You get ✓, ✗ with the reasons, or `?` when something isn't known. Run it before a maintenance window or before turning on DRS or HA. [In depth](/docs/en/virtualization#where-a-vm-can-move).
## 11. If a switch goes down
```diagram
type: graph
title: The blast radius, before the reboot
alt: Host A has a cable to each switch and keeps going; host B has two cables to sw1 and goes dark when sw1 reboots
legend: new=Redundant; warn=One switch; down=Down
start: Today
height: 52
---
node sw1 | sw1 | 30 10 | switch | | Planned reboot for an upgrade
node sw2 | sw2 | 70 10 | switch
node a | host A | 20 40 | host | good | One cable to each switch
node b | host B | 60 40 | host | warn | Two cables, both to sw1
link a sw1 | | new
link a sw2 | | new
link b sw1 | | warn
link b sw1 | | warn
step sw1 reboots | Host A keeps going over sw2. Host B is cut off with every VM on it. Zaryn lists exactly this for each switch, so the change board gets its answer in seconds.
node sw1 | bad
link a sw1 | down
link b sw1 | down
node b | bad
```
Each host's uplinks get a verdict: **redundant**, **one switch**, **one port** or **unknown**. **If a switch goes down** lists, per switch, the hosts it would cut off and the running VMs that go with them. [In depth](/docs/en/virtualization#if-a-switch-goes-down).
## 12. Storage that fills up
```diagram
type: stats
title: A datastore, as Zaryn shows it
---
76% | full | ds-ssd, 6 TiB | 76 | warn
26 days | until it's full | at 60 GiB a day | | bad
300% | of its size given to VM disks | thin disks: fine until they fill | | warn
```
Zaryn samples every storage about once an hour and fits the last 30 days, so it can say **full in about N days**, and which VMs are filling it fastest. Thin disks past the storage's size are flagged before they all fill at once. [In depth](/docs/en/virtualization#storage-forecasts).
## 13. Who talks to whom
```diagram
type: flow
title: From traffic to a dependency map
---
Export | Switches and hosts send NetFlow, IPFIX or sFlow | | 📡
~ to the collector
Summarise | `zev site flows on`: conversations, not packets | accent | 📊
~ to Zaryn
Map | Each VM's "talks to", and the site's Dependencies tab | good | 🕸
```
```bash
sudo zev site flows on # NetFlow/IPFIX on UDP 2055, sFlow on UDP 6343
sudo systemctl restart zev-site
```
Point your routers' or hosts' flow export at the collector (`softflowd -i eth0 -n <collector>:2055 -v 10` on a Linux host). See [Flows and dependencies](/docs/en/network-flows).
## 14. Plan a migration in waves
```diagram
type: flow
title: Leaving a platform without splitting applications
loop: check each wave before the next
---
Flows | A week of who talked to whom | | 📊
~ grouped
Groups | Keep-together sets, then the chattiest pairs | accent | 🔗
~ packed
Waves | At most N VMs each, quietest first | warn | 🌊
~ one at a time
Pilot | Move, check, keep | good | ✓
```
**Plan a migration** at the bottom of the Virtual machines tab turns a week of flows into waves. Each wave lists the networks the target must have first, the traffic that stays split while it moves, and what it needs outside the hypervisor. **Download as Markdown** gives a document for the change ticket. [In depth](/docs/en/virtualization#planning-a-migration-in-waves).
## 15. History, alerts and AI agents
```diagram
type: cards
---
🕰 | Every config, versioned | Each VM's and host's config kept like a router's: diffs and drift | accent
🔔 | Changes | A VM appears, moves (vMotion, HA), starts, stops; a host goes offline |
🤖 | AI agents | `zaryn_site_virtual` in `zev mcp`: VMs, VLANs and switch ports, in few tokens | good
🗺 | On the map | Hypervisors next to the switches they're cabled to |
```
You've gone from what a VM is to planning a migration. From here, the [Virtualization](/docs/en/virtualization) reference has every check in depth, and [Digital twin](/docs/en/network-twin) tests a switch change against your VMs before it's made.