ZNetLab
Three parts, one server: building labs on it, what it does behind every button, and how to stand one up on Linux, Windows, AWS or Azure.
Part I
For the account that places and runs machines. Everything here works without ever touching the server itself — and the one thing it cannot do has a section of its own.
You can build labs, place machines, cable them, start them, open their consoles, capture traffic and save the result. You cannot put a new image on the server. That is not a limit on your plan — no plan has it. It belongs to the administrator role, because putting a vendor image on a server is a licensing act, not a lab action.
Your account is on the Free plan. This is what that decides — read out of the same table the server checks:
| Feature | What it is | Free |
|---|---|---|
| Built-in devices | Virtual PC, LAN Switch and NAT Cloud — no image or licence needed | yes |
| Vendor images | Any image the administrator has registered | yes |
| Packet capture | Capture any link and read it layer by layer | yes |
| Export .pcap | Save a capture and open it in your own analyser | yes |
| Link conditioning | Bandwidth, delay, jitter and loss per link | yes |
| External terminals | Consoles in your own terminal client over telnet | yes |
| Lab snapshots | Save the running configuration of every device at once | yes |
| Configuration manager | Export and import every startup config together | yes |
| Send to all consoles | Type once, apply to every device | yes |
| Shared labs | Hand a lab to a colleague or a class | yes |
| Upload images | Add network OS images to the server | no |
| Manage accounts | Create, disable and remove users | no |
Nothing is installed on your machine — no hypervisor, no VirtualBox, no agent. You have a browser. Everything else lives on a Linux server your account was pointed at when you signed in, and you never typed its address: the server reads your plan and issues the machine that licence covers. It is shown, read-only, on the Server tab.
Your ceilings on Free:
| Limit | Your plan |
|---|---|
| Users included | 1 |
| Image devices per lab | 2 |
| Saved labs | 2 |
| Labs running at once | 1 |
| Frames held per capture | 2000 |
| Interfaces per device | 8 |
The shelf has two parts, and it matters which is which.
You can open the Images tab and read the whole library — what is installed, which platform it is, which engine runs it. Only the upload area is closed, and it tells you so rather than being hidden.
This works on any account, on any plan, with no image at all — it uses only the built-ins.
Everything the rest of this guide describes — cabling, starting, consoles, capture, saving — behaves the same whether the device is a built-in or a real router image. The only difference is how long it takes to boot.
Short of time? Press Examples. Those labs are already drawn, and the ones built from built-ins start on any plan.
Until you press Start, a lab is a document. Nothing exists on the server, and nothing counts against you. Press Start lab and the server checks your plan before it builds anything.
If you are over a ceiling it says so with the numbers, not with a shrug:
Three ways forward, and the first two are usually the right ones: remove a device you are not using, stop another lab that is still running, or move up a plan. A stopped lab keeps everything — the topology and the configurations — so stopping one to start another costs you nothing.
One lab runs at a time, on every plan, and a lab belongs to the window that opened it. Open the canvas in a second tab or a second browser and that one is refused — it will not let you build anything until you close it or carry on in the first window. That is on your side: two windows on one account would both be spending the same allowance, and neither would tell you. A team that needs several labs running together needs an account each.
Booting takes a moment. A built-in is instant; a real router image spends a minute or two at starting before it turns running. If a device goes red, click it — the reason is on the device, and it is usually memory on a shared server rather than anything you did.
A click on a device that is not running only selects it. A terminal onto something that has not booted is a window with nothing in it, and laying out a topology should not leave a stack of them behind. Double-click, or the Console button in the device panel, opens one anyway — which is how you write a startup configuration before anything has started.
Click the front panel drawing in a device’s properties to see what the real hardware looks like — where the console port is, how the ports are grouped, and which socket is the interface you are configuring.
Some of what surrounds those three is decided by your plan rather than by the lab. On Free:
| Tool | You |
|---|---|
| Packet capture | yes — Capture any link and read it layer by layer |
| Export .pcap | yes — Save a capture and open it in your own analyser |
| External terminals | yes — Consoles in your own terminal client over telnet |
| Link conditioning | yes — Bandwidth, delay, jitter and loss per link |
| Lab snapshots | yes — Save the running configuration of every device at once |
| Configuration manager | yes — Export and import every startup config together |
| Send to all consoles | yes — Type once, apply to every device |
| Shared labs | yes — Hand a lab to a colleague or a class |
Where something is not included, ZNetLab names the plan that includes it rather than greying a button out and leaving you to guess.
This is the one thing you genuinely cannot solve yourself, so here is how to solve it in one message instead of three.
First check it is really missing: open Images and read the list. A platform can be installed under a name you did not expect — viosl2 is the IOSv switch, csr1000vng is the CSR.
If it is not there, ask for it from the plan window: your profile, or the Plans page, then See what you can run. It lists every platform ZNetLab recognises that this server has not got, and the form asks for exactly the three things an administrator needs — which platform, which release, and what it is for — so nobody has to come back and ask. Asking carries a small one-time charge for the work of sourcing, checking and filing the image; it is shown before you agree to it, and it is not a charge for the software, which ZNetLab does not supply and cannot license to you.
A message to a human still works, of course. If you send one, send this:
While you wait, the lab is rarely blocked outright. The built-in LAN Switch covers VLANs, trunking and access ports; Virtual PC covers addressing, routing, DHCP and NAT through the NAT Cloud. Draw the topology now and swap the real image in when it lands — a saved lab does not care which device type sits at a position until you start it.
| Not on your screen | Why |
|---|---|
| The upload area | Putting a vendor image on a server is a licensing act. It sits with whoever holds the licences, on every plan. |
| The Admin portal tab | Accounts, plans and everything running across the whole server. The tab is not merely disabled — it is not there. |
| Other people’s labs | Your lab space is yours. Sharing is on every plan, and it is opt-in per lab, never automatic. |
| The server’s address to type | It is issued to your account from your plan. Being unable to type one is what stops anybody pointing themselves at a machine their licence does not cover. |
None of this is hidden from you as a matter of secrecy. Where a thing is out of reach, ZNetLab says which role or plan reaches it — so you always know what you are looking at, and who to ask.
Part II
What happens behind each button: how an image is identified and filed, how a drawn cable becomes a Linux bridge, which of the four engines boots what, and what every icon means.
You draw a topology in this browser. A Linux server boots the actual vendor images and wires them together with kernel networking. The consoles and the packet capture come back here over one WebSocket. Nothing is installed on the machine you are reading this on — no hypervisor, no VirtualBox, no agent. The browser is the whole client.
Real labs is not reachable signed out. One account covers the whole product: the lessons, the free simulator and this lab server.
The address is a property of the account, not of the browser. When you sign in, the server reads the plan on your account and issues the lab machine that licence buys — a free account lands on the shared machine, a paid one on the machine it pays for. It appears read-only on the Server tab. Change the plan and the account moves with it; there is nothing to retype and no way to point yourself at a machine your licence does not cover.
The same plan sets the ceilings the lab enforces at Start lab — how many devices, how much memory, how many labs may run at once.
Done once, by an administrator. ZNetLab ships no vendor software — you supply images you are licensed to run. Two ways in, and they end in the same place.
Identity lives in the directory name, not the file name, because a QEMU library image is a folder of disks. That is also why two builds of the same platform never collide: they are two directories, and both stay.
Open the Lab tab. Four areas, and that is the whole screen.
| Area | What it holds |
|---|---|
| 1 · Shelf | Every device you may place. Three built-ins need no image at all — Virtual PC, LAN Switch, NAT Cloud. Everything an administrator imported appears below them. |
| 2 · Toolbar | Select · Delete, then Start lab · Stop lab, Console · Capture, Save · Open · Examples. There is no Link tool: cabling lives on the device itself, on the plug at its corner. |
| 3 · Canvas | The topology. Drag from the shelf to place; drag the plug at a device’s corner to cable it; the ⏻ on the other corner powers that one device. Click a running device for its console. |
| 4 · Dock | Everything about what is selected — live state, interfaces and what they are wired to, the startup configuration, and the front panel of the real hardware. With nothing selected, the lab’s own counts. |
A device is drawn by its kind, not its brand — a Cisco router and a Juniper router share a drawing because on a diagram they do the same job. The registry maps each platform to one of these, and a few platforms are named exactly where the generic drawing would tell you nothing (a wireless controller is not a router).
An icon says what a device does. It says nothing about what you would be standing in front of with a console cable in your hand — which is the other half of learning this, and the half a simulator usually skips.
So every device also has a front panel in its properties: the chassis, the ports laid out the way that family lays them out, the console port picked out in its own colour, the status LEDs — and each socket carrying the same interface name you are typing into the CLI. That last part is the point. Gi0/1 stops being a string in a configuration and becomes the second port from the left. Click the drawing to open it at a size where the labels can be read.
| Family | What its panel teaches |
|---|---|
| Router | Onboard interfaces versus the ones on cards — which is what the middle number in Gi0/1/0 counts — plus console, aux and the slots themselves. |
| Switch | Ports grouped in fours the way the metal groups them, SFP uplinks at the end, and the MODE button that changes what the port LEDs mean. |
| Firewall and appliances | A row of identical ports, and a separate management port — because which interface is inside and which is outside is a decision you make, not something the box knows. |
| Server | Drive bays on the front, network at the back, two NICs on purpose, and an out-of-band port that works when the operating system does not. |
| Host | One port, one address, one default gateway — and the link light no amount of configuration will fix. |
The label under a device carries its state, and links are coloured the same way:
| Colour | Device | Link |
|---|---|---|
| grey | Placed, not started | Drawn, not built yet |
| amber | Starting — a real router takes a minute or two | Cable up, protocol down |
| green | Running; the console will open | Up, and carrying traffic |
| red | Did not start; click it for the reason | Broken or removed underneath |
Either way you are asked two things: which cable, and which interface at each end. Seven media are offered — straight-through, cross-over, fibre, serial, console, coax and wireless — and the one that fits the two devices is already selected, so the usual answer is to press Connect. The interfaces that suit the cable are listed first; ones already carrying a link are shown as in use rather than hidden. If neither device has the kind of interface the cable wants — a serial or a radio — the picker offers to add one, named the way the platform names it.
The medium is a property of the drawing: on the canvas a serial WAN link, a fibre uplink and a wireless association each look like themselves, and a key in the bottom-left corner names the ones this lab uses — only those, so a diagram with one kind of cable does not get a seven-row legend. Underneath, all of them are the same Linux bridge. Change it later from the link’s panel — no restart, because nothing about the datapath changed.
Nothing has been created on the server yet. Until you press Start, a lab is a document — devices, positions, links and the ports they land on. That is deliberate: you can draw the whole thing offline, save it, and start it later, or on another server.
In a hurry, press Examples and open Two PCs on a wire. It is already built and it starts on any plan, because it uses only built-ins.
This is the moment the document becomes running software. Seconds for built-ins; a minute or two for a real router image.
| Engine | Runs | What it costs |
|---|---|---|
| Namespace | Virtual PC, LAN Switch, NAT Cloud | Kilobytes. Starts instantly — it is kernel networking, not a machine. |
| Container | FRRouting, Nokia SR Linux, Arista cEOS, any Linux image | Tens of megabytes, up in a second or two. No emulated CPU and no virtual machine, so it does not care whether the host has hardware virtualisation. ZNetLab gives the container its interfaces from the topology, so it forwards like anything else on the canvas. |
| IOL | L2/L3 IOS as a native Linux process | Tens of megabytes. Needs a NETMAP for the wiring and an iourc licence file — beside the image, or once at the top of the library for all of them. |
| Dynamips | Classic IOS — 3725, 7200 and friends | A software CPU, so it is the slow one. Slots and cards are planned for you. |
| QEMU + KVM | Full disk images — CSR, IOSv, ASAv, vMX, vEOS | Real RAM, the amount the platform asks for. Uses /dev/kvm, already in the kernel. |
Start lab boots everything. Afterwards each device has its own power — the ⏻ on its top-left corner, or Power on / Power off in its panel.
That is not a convenience, it is the experiment. Pull one router and watch the protocol reconverge; put it back and watch it settle. Stopping the whole lab to do that would take every other console and every running capture down with it. The links stay in place while a device is off, so it rejoins the topology it left.
This is why there is no hypervisor to install. Three of the four engines are not virtual machines at all. Only full disk images need virtualization, and they use KVM — which is part of Linux. A modest server therefore runs far more devices than one-VM-per-router ever could.
Clicking a device that has not booted only selects it, and moving one opens nothing — so laying out a topology does not bury the screen in terminals. To write a configuration before anything is running, double-click instead, or press Console in the device panel: what you type there is kept and pasted in the moment the device boots.
Prefer your own terminal client? Set it as your default console from the ⚙ in the window’s title bar, and a click on a device opens the device there instead — or in both at once. Section 8 explains how that works and what it cannot do.
Output is coloured as it arrives, keyword by keyword: an interface going up or down, a rejected command, a ping that answered or did not, interface names, addresses and the IOS facility on a log message each read differently. A console scrolls faster than anyone reads, and the point is to make what you are watching for findable without reading every line. Real ANSI colour from the device is honoured too, rather than printed as escape codes.
Log gives you the transcript: save it to a file, copy the lot, or turn on a timestamp against every line. The file is written in the usual session-log shape — opened and closed lines around the session, and no escape codes in it.
It is the vendor’s own command line, because it is the vendor’s own software booted from your image. Nothing is reimplemented. Every device also gets a TCP port in the 23000–23999 range, so your own terminal can attach to the same console at the same time — useful when two people are looking at one problem.
You can make that the default. The ⚙ in the console window’s title bar — and the Default console card on the Server tab — set what a click on a device does:
| Choice | What happens |
|---|---|
| In this window | ZNetLab’s own console. The default. |
| In my telnet client | ZNetLab hands the address to telnet:// and your registered client opens the device. |
| In both | Both windows, on one session — what you type in either is seen in both. |
The setting belongs to the browser, not the account — a client is wanted on the machine it is installed on. External terminals are a plan feature; where yours does not include them, the click still opens the console in the page and says why.
The window itself behaves the way a terminal does anywhere else, because it is one: every key goes to the device as you press it and the device echoes, exactly as in any terminal. So history on the arrow keys is the device’s own history, Tab and ? are answered by the device, a password prompt hides what you type, --More-- pages on a space, Ctrl-C really does break a running ping, and Ctrl-Shift-6 sends the Cisco break. Ctrl-Shift-C/V for copy and paste (plain Ctrl-C has to stay break), Shift-PgUp/PgDn to scroll, Ctrl-Shift-F to search the scrollback, and A− / A+ for the text size. Scroll up and it stops following, so output cannot yank the view away while you are reading — the status bar says scroll lock while that is true, alongside the connection, the console address, the measured size, how long the session has been open and how much scrollback is behind you.
| Colour | What it marks |
|---|---|
| R1(config)# | The prompt |
| green | up, !!!!!, Success, established, permit |
| red | down, denied, err-disabled, timeouts — and a rejected command in a brighter red |
| purple | The IOS facility on a log line — %LINK-3-UPDOWN: |
| cyan | Interface names, long or short |
| blue | Addresses and masks |
| grey | Device timestamps and MAC addresses |
It is deliberately conservative. Colour that is wrong is worse than colour that is absent, because it teaches you to distrust all of it. Where the device sends real ANSI colour of its own, that wins — nothing is repainted over it.
Log opens a small menu: Save session log writes a file named for the device and the minute, Copy everything puts the transcript on the clipboard, and Timestamp every line prefixes each line with the time it actually arrived — captured when it was written, not invented at save time. The file carries the usual opened and closed session-log lines, and no escape codes.
A topology is a drawing somebody else has to read, and a cable is only half of what it says. Draw ▾ in the toolbar adds the other half: a note for text, a rectangle or ellipse to box a group — drawn behind the devices, so what you drew around is still visible and still clickable — and a line for a boundary of your own.
Ctrl+A selects every device, a box dragged on empty canvas selects what it covers, and shift-click adds one. They move together and delete together, and deleting asks first. Escape stops drawing and lets a selection go. All of it is saved with the lab and travels with it to the portal; none of it is a device.
A real 7200 has no FastEthernet1/0 until somebody puts a PA-2FE-TX in slot 1. ZNetLab fits the cards for you by default, but the card decides what the port is — an NM-4T makes Serial1/0..1/3 where an NM-1FE-TX makes FastEthernet1/0. Select a device with card slots and the panel lists them; choose a card and the interfaces are rebuilt from it.
Slot 0 is onboard on the integrated routers and cannot be changed — the emulator fits it itself and aborts if asked for it twice. An empty slot stays empty. A slot will not change while a cable is attached to a port it would remove, and a running device keeps its hardware until it next starts.
Two ways in, and they are the same capture. Click a link and press Capture to watch that cable; click a device and press Capture to watch that machine — with one cable it starts at once, with several it asks which interface and names what is on the far end. An interface is a link, so nothing is recorded twice.
Protocols answers the three questions a capture usually gets asked. Which packets should be here — read out of the startup configuration of the two devices on this cable, so an OSPF that is configured and silent is a red row rather than a mystery. Are they on time — the measured interval beside the protocol’s own default, and beside the interval the packets themselves advertise, because OSPF, HSRP, STP and BGP all carry their timers in the frame. If the two ends advertise different timers the adjacency will never form, and nothing else in any product tells you that. And which ones cause trouble — an ARP nobody answered, two devices claiming one address, an unreachable, a reset, a Discover with no Offer, a broadcast storm, and the IOS Ethernet keepalive that flaps every emulated link. There is a table of every default timer at the bottom, worth reading before you capture anything.
The pane opens on Overview: the rate, the protocol mix, who is talking and the conversations, as figures rather than tables — and every bar is a filter, so clicking one takes you to the packet list with that protocol or that address already applied. Packets is the list with the layer-by-layer detail and the bytes; Flow is the time-ordered graph of who sent what to whom.
Open externally pipes the live stream into your own analyser. The server is already running tcpdump; this hands you those bytes untouched, so it is treated as a live interface instead of a file that stopped being true when you saved it. The command is written for you, with the address, the capture id and your session token in it — which is why it must not be pasted into a ticket. A capture belongs to whoever started it; nobody else can read that stream, administrators included.
Pick a cable in that window and the capture is started for you, and what you get back is an address your analyser can open on its own — no token to paste and no curl to get right, because the key is in the address. There is also a launcher to download: a small script you double-click, which opens your analyser on the live stream. The key opens that one capture and nothing else, it is yours, and it stops working after fifteen minutes or when the capture stops.
Frames appear as they cross the bridge, and Export .pcap hands you a file any analyser opens. These are real frames off a real interface, not a rendering of what the traffic ought to look like — which is the whole difference between a lab and a simulator.
A link also takes conditioning: add delay, jitter or loss to a cable and watch what the protocol does about it.
Live, next to Capture on the toolbar, draws every captured frame on the cable it crossed — a coloured marker travelling from the device that sent it to the one that received it, with a running count above each device. Hover a packet to hold it still and click it to open that frame in the analyser, with its layers and its bytes. Each device’s own panel grows a Live traffic list of the last frames in and out of its interfaces, and every row opens the frame too. It draws what a capture reads and nothing else: switch it on with nothing being captured and it offers to capture the cables for you.
Each cable also keeps what last crossed it on a small label under the wire, so a cable says what is on it even when you were not watching for the marker. Right-click the Live button for two settings: the addresses on each packet, and slow motion — which is what to use when a short cable makes a packet a flicker.
Which way a packet was going is not in a capture — tcpdump on a bridge reports the frame and not the port it came in on — so ZNetLab reads the bridge’s own address table instead of guessing. Until that answer arrives a marker is drawn hollow and says so.
Everything the workspace tells you is kept. The Notifications tab in the bottom-right corner carries a count and the colour of the worst thing in it you have not read; click it for every notice with the time it arrived, filtered by kind if you want. Which ones also appear as a card over the drawing is a setting in that list — errors only, by default. Nothing is lost and nothing sits on top of your topology.
Operations · NOC, under the toolbar’s tools menu and in your account menu, is the other half of watching a lab. Give each device a Site in its panel — HQ, Branch-2, DC-East — and that screen groups them into places and says which place is down, which is short of devices, and what raised every alarm, with what to do about it. It also reads each device’s TACACS+ and RADIUS configuration: which server it points at, whether a shared key is set, whether that server is a device in this lab and whether it is running — and, when a capture is going, whether those frames are really crossing the wire.
A saved lab keeps the devices, their positions, the links and the configurations you wrote. Stopping a lab frees the server’s memory without losing any of that, so tomorrow you press Open and then Start, and it comes back as you left it.
| What you see | Where to look |
|---|---|
| The image never reaches the shelf | It is in unidentified/. Rename it the way section 3 shows and drop it again. |
| Start refuses | A plan ceiling. The message names the number — devices, memory, or labs already running. |
| A device goes red | Click it. The engine’s own error is on the device, not buried in a log. |
| The header says not connected | The Server tab: it shows the address your licence issued and what the server last reported. |
| A link stays amber | The cable is built; the protocol is not up. That is a configuration answer, and it is the point of the lab. |
All three boot the same images the same way: one qemu-system-x86_64 per device, with a per-device copy-on-write disk in front of a read-only library file. They differ in three places, and only three: where the knowledge of which flags an image needs is kept, how the console is carried back to you, and how the cables are made. Everything that looks like a deep architectural difference comes out of one of those.
None of the three works it out by looking at the file. Each keeps a written description per product, and the three formats hold almost the same fields under different names. GNS3 keeps a .gns3a appliance file per product, written by the vendor or the community. EVE-NG keeps a template per product under /opt/unetlab/html/templates/, where the whole QEMU incantation is one qemu_options string, and pays for that simplicity with a naming rule: the image must sit in a folder named for the template and be called hda.qcow2 or virtioa.qcow2. ZNetLab keeps a platform table that both the server and this page read, with a pattern per platform that recognises an image by its filename.
On this server there are 228 appliance files describing 1,115 versions between them, and 352 platforms in the table, 124 of them ZNetLab own (109 QEMU, 6 Dynamips, 2 IOL, 4 Docker and 3 built-ins that need no image). A built-in is never replaced by an imported file, which is why 27 appliance files are read at every start, found to clash, and set aside.
| What a version can name | How many of the 1,115 do |
|---|---|
| a main disk | 1,090 |
| a second disk | 330 |
| a CD-ROM | 179 |
| a BIOS or firmware file | 31 |
| a kernel and an initrd | 4 and 2 |
All of those are carried through to the command line here, along with the boot priority, the guest architecture and the appliance own extra QEMU arguments. Every disk interface the 228 files ask for is built: virtio (107), ide (92), sata (14) and scsi (10).
A QEMU guest talks either down a serial line or onto a screen, and which one it uses is a property of the image, not a preference. The appliance files say so, and on this server they say it like this:
| The console the appliance asks for | How many of the 228 |
|---|---|
| telnet — a serial line | 129 |
| vnc — a graphical screen | 64 |
| spice — a graphical screen | 5 |
| http — a web page inside the device | 3 |
| not stated | 27 |
All three read that field and act on it. A serial appliance gets a terminal; a graphical one gets a screen. That is new here, and it is worth saying what it replaced: ZNetLab used to read vnc, write down no console, and start the machine with a serial line it would never write a byte to. 69 of the 228 appliances are of that kind — Cisco ASAv, ISE, vWLC, DCNM, Aruba VMC, Kali, FreeBSD, CentOS and every Windows desktop — and each of them booted perfectly, held its memory, answered on the network, and looked from the only window you had on it exactly like an image that did not boot.
What happens instead now: a device whose platform says its console is a screen is started with QEMU’s own VNC server on the loopback rather than -nographic, and its console window gains a Screen button beside the terminal. The picture comes over a WebSocket this server authenticates with a one-use ticket that dies in thirty seconds — so nothing about the screen is reachable from outside the host, and no credential is ever written into a URL. Keyboard and mouse go back the same way, and Ctrl+Alt+Delete has a button of its own because no browser will hand that chord over.
KVM is not a feature any of the three implements. It is a kernel interface onto the processor own virtualisation instructions; with it QEMU hands guest code to the CPU to run directly, and without it QEMU translates every guest instruction, which is roughly ten times slower and otherwise the same machine. A server that is itself a virtual machine, on a host that does not pass virtualisation through, cannot have it — and nothing inside the guest can change that. GNS3 or EVE-NG installed on such a host hits the identical wall; neither avoids KVM, they are simply normally run on a machine that has it.
Of the 228 appliance files, 116 say they require it, 62 say they allow it, 48 say nothing and 2 say to disable it. Require is mostly a wish, though, not a fact: it usually means or it will be slow. Alpine Cloud Guest says require and boots on a host with no KVM in about four minutes. There are exactly two ways a guest genuinely fails without it, and they look identical from the lab — an open console with nothing in it:
| What goes wrong | What it looks like | What answers it |
|---|---|---|
| The loader never finishes | The BIOS hands over and nothing further happens. Measured on a Nexus 9000v: fifteen minutes, not one byte on the console, 69 MB of 8,192 ever touched. | Nothing, on this host. ZNetLab refuses such a platform before it allocates memory, a tap, a console port or a disk, and says why. |
| The guest idles on HLT and the clock stops with it | The kernel loads, prints its last line, and the QEMU process drops to 0% CPU and stays there. Measured on the classic Cisco ASA. | -icount auto, which the platform now carries. The same image reaches its banner in about two minutes. |
It resumes a booted machine instead of booting it again. Once a device has reached a prompt, ZNetLab tells QEMU to write the whole machine — memory and devices — to a file, and the next start of a machine of that exact shape loads it and comes up already booted. The file is keyed by everything that would make it the wrong state to load: image, RAM, CPUs, NIC model, port count and accelerator. Neither GNS3 nor EVE-NG has anything of the kind; both boot the guest every time.
It refuses with a reason, before it spends anything, where the other two leave you with a QEMU error or a setting that says no. It measures this host rather than guessing, so the countdown on a booting device is this machine own figure for that platform. It reads the console and names what it sees — three line-protocol drops inside two minutes is the Ethernet keepalive failing on a bridged interface, and it says so once per device rather than leaving somebody to work out why pings are dropping. And an image becomes a device by being dropped in a directory: no folder to name, no appliance to pick first.
This list is not an opinion. It is what the appliance importer writes down, per file, when it reads something it cannot honour:
| What it cannot do | Appliances affected |
|---|---|
| Draw a SPICE console as SPICE rather than as VNC | 5 |
| Give individual ports their own NIC model | 10 |
| Supply a Dynamips idle-PC the appliance does not name | 9 |
| Proxy a console that is a web page inside the device | 3 |
| Mount volumes inside a container | 2 |
| Fit WIC cards named by a Dynamips appliance | 1 |
| Apply a CPU-throttling setting | 1 |
The line that used to head this table — draw a graphical console, 69 appliances, 30% of the library — is gone from it, because it is built. What is left is small: a SPICE appliance is run with a VNC screen instead, which shows the machine but carries neither the shared clipboard nor the USB redirection SPICE would have. Thirty appliances also try to pass -nographic and fifteen -smp; those are dropped on purpose, because ZNetLab sets them itself and QEMU takes the last of a repeated flag.
Two problems, and they pull in opposite directions: a host with no hardware assist translates every instruction, so running machines are expensive; and a third of the library cannot be looked at. Seven things would move both, and they are listed by what they return rather than by how interesting they are. None of them needs hardware this server does not already have, except the last.
| What to build | What it does | What it buys |
|---|---|---|
| 1 · A screen in the browser — built | A graphical appliance is started with QEMU’s own VNC server on the loopback, and the picture is carried to a canvas in the console window over a ticketed WebSocket. One button switches between the screen and the serial line; Ctrl+Alt+Delete, a redraw and a screenshot sit beside it. | 69 appliances stopped looking dead, including every GUI Linux desktop, the ASAv, ISE, vWLC and the rest — 30% of the library, and it needed no KVM. |
| 2 · Notice the halted guest | A QEMU process that has printed its kernel banner and then sits at about 0% CPU with nothing more on its console has the ASA disease: it idled on HLT and the virtual clock stopped with it. The server can read that from the process itself, say so on the console, and offer to start it again with -icount auto. | Turns the commonest silent failure into a named one with a button. It generalises the fix the ASA already carries to every image with the same disease, instead of waiting for somebody to find each one by hand. |
| 3 · A queue for starting | The load is the boot, not the running: a booted guest that is idle costs almost nothing, while a booting one translates its whole startup. Start at most a few devices at a time and let the rest wait, with the canvas saying so. | Flattens the spike that eight devices started together make, and usually finishes the whole lab sooner, because TCG threads stop fighting each other for the same cores. |
| 4 · Warm start, used harder | Already built, and the single largest saving on a host like this: the state of a booted machine is written once and loaded on the next start, so the boot — the expensive part — does not happen twice. Worth keeping states for more shapes and discarding them less eagerly. | A device that resumes skips the whole of what TCG is slow at. It is the difference between minutes and seconds, per device, every time after the first. |
| 5 · Honour the throttle the appliance asks for | Ten appliance files name a process priority and one a CPU throttle, and both are read and ignored today. Running QEMU at a lower priority, and capping the greedy ones, is a few lines where the process is spawned. | Stops one badly behaved guest making every other device on the server feel broken. It does not make anything faster; it makes the slowness fair. |
| 6 · Turn on page merging | Ten Linux guests from one image hold ten copies of the same kernel and libraries. The kernel can merge identical pages between them, and it is switched off on this host. | Memory, not CPU — but memory is what decides how many devices fit, and the saving across guests of one family is large. |
| 7 · A second lab server, where KVM exists | The only answer to an image that genuinely cannot boot without hardware assist. ZNetLab is already client and server, with the address issued to an account rather than typed, so a second machine with nested virtualisation enabled can carry the handful of platforms that need it. | The last gap closes. Everything else on this list works on the host you have. |
The first is done. The second changes what a reader sees the next time an image hangs; the next four change what the server feels like under a full lab; and only the seventh needs somebody to buy a machine.
Cisco, IOS, Catalyst, Meraki, Packet Tracer, CCNA, CCNP, CCIE, Juniper, Junos, Arista, EVE-NG, GNS3, Wireshark, SecureCRT, PuTTY and other names are trademarks of their respective owners. ZNetLab is an independent product and is not affiliated with or endorsed by them. ZNetLab ships no vendor software. Terms · Privacy · Licences
Part III
Administrator work. One Linux machine holds the images, boots them and answers a socket — here is how to build it on each of the five platforms, and how to get your images onto it.
One Linux machine. It holds the images, boots them, wires them together with kernel networking, and answers a WebSocket. Everybody else needs only a browser — there is no client to install, roll out, or keep in step.
| Thing | Minimum | Why |
|---|---|---|
| Linux | Ubuntu 22.04+ / Debian 12 / RHEL 9 | Namespaces, veth and bridges are the cabling. Nothing else has them. |
| Root | required | Bridges, tap devices and namespaces are root-only. This is not avoidable. |
| Node.js | 16+ | The server itself. deploy.sh installs 20 if yours is older. |
| RAM | 8 GB, and more per user | Roughly 512 MB per IOSv node and 4 GB per CSR or Nexus. Namespaces cost kilobytes. |
| Disk | 100 GB+ | Images are 1–4 GB each and you will keep several versions. |
| /dev/net/tun | required | Without it QEMU nodes cannot be wired at all. |
| /dev/kvm | strongly wanted | Without it QEMU images still run — about ten times slower. Dynamips, IOL and the built-ins do not care. |
That last row is the only thing that really differs between the platforms, so here it is in one place:
Pick where it is going. The steps change; what you end up with does not.
A clean Ubuntu or Debian box to a running lab server in one command. The script installs the packages, makes the directories, asks for the administrator password, starts the service — and then proves the result rather than announcing it.
What it does, in the order it does it:
| Step | What it means |
|---|---|
| Packages | iproute2 bridge-utils tcpdump iptables qemu-kvm qemu-utils dynamips unzip xz-utils — installed one at a time on purpose, so one missing from your mirror does not stop the rest. |
| Node.js | 16 or newer. It installs Node 20 from NodeSource if yours is older or absent. |
| Kernel | Loads bridge, veth and tun, checks /dev/net/tun and /dev/kvm, and turns off bridge-nf-call-iptables — bridged frames walking the firewall is a cost for nothing and a way for packets to vanish invisibly. |
| Directories | images/{incoming,library,unidentified} at 755, and /var/lib/znetlab at 700 — accounts and the activity trail live there. |
| Administrator | Asks for a password, twice, not echoed, 8 characters minimum. If accounts already exist it leaves them alone. |
| Service | Writes znetlab.service for systemd. It runs as root because bridges, taps and namespaces cannot be made without it. |
| Verify | verify.sh checks the kernel before starting anything, then verify-lab.js drives the running server and checks the kernel agrees with it. |
ZNetLab needs Linux networking — namespaces, veth pairs, bridges. Windows does not have those, so on Windows the server runs inside WSL2, which is a real Linux kernel. You do not have to learn WSL: one script does the whole thing.
It registers the distribution, installs the packages inside it, verifies the Linux datapath, starts the server, proves it with a real lab, and prints the address, the username admin and a generated password — copy that password before you close the window.
Two Windows-only conveniences worth knowing:
| What | Where |
|---|---|
| The drop folder, in Explorer | Paste \\wsl$\znetlab\opt\znetlab\images\incoming into the address bar and drag images in like any folder. |
| The log | wsl -d znetlab -u root -- tail -f /var/log/znetlab.log |
| Stop it | wsl -d znetlab -u root -- pkill -f server.js |
KVM works: WSL2 supports nested virtualization, so QEMU images run at full speed. This is a genuinely good way to run a personal lab server on a laptop.
Every other platform on this list either has hardware virtualisation or does not. This one hands it out: a desktop with VT-x or AMD-V can pass that through to a Linux virtual machine, which is exactly what a rented VPS usually refuses to do. So if the QEMU images are the point — IOSv, CSR1000v, ASAv, vSRX — a Linux VM on a machine you own beats a cloud instance that bills you for software emulation.
If both of those answer, the rest is the Linux page above, unchanged — this is an ordinary Ubuntu server that happens to live in a window:
The four settings that actually matter, and why:
| Setting | Value | Why |
|---|---|---|
| Virtualization engine | VT-x/EPT or AMD-V/RVI, ticked | The only reason to use this platform. Everything else here is ordinary sizing. |
| Processors | 4 or more | Each QEMU device wants a core of its own while it boots, and the host keeps one. |
| Memory | 8 GB, more per user | Roughly 512 MB per IOSv and 4 GB per CSR or Nexus — the guest cannot hand out what it was not given. |
| Network adapter | Bridged, not NAT | Accounts are issued the server address from their plan. Behind NAT that address is reachable from the host and nowhere else, which looks like a broken socket rather than a network choice. |
Worth being plain about the shape of this: the VM is the server, not a device. ZNetLab does not drive Workstation and cannot boot a lab device as a Workstation VM — the cabling in a topology is Linux bridges, veth pairs and namespaces inside that one guest, and a vmnet switch on the host has no way into them. Workstation is here because it is the cheapest way to get a kernel that owns /dev/kvm.
| Instance | KVM | Good for |
|---|---|---|
| c6i.metal, m5.metal | yes | A real shared lab server. This is the one to pick. |
| t3.large, m5.xlarge | no | Fine for built-ins, IOL and Dynamips. Painful for CSR or ASAv. |
Two things worth doing on day one, both about the images rather than the server:
| Do this | Why |
|---|---|
| A separate EBS volume for /opt/znetlab/images | Images outlive instances. Detach it, attach it to the replacement, and the library survives a rebuild. |
| A private S3 bucket as the archive | Keep the licensed originals there and pull them down with aws s3 cp instead of re-uploading over ssh every time. |
| An Elastic IP | The lab address is issued to accounts from their plan. A public IP that changes on every stop breaks every one of them. |
| Size | KVM | Good for |
|---|---|---|
| D4s_v5, D8s_v5, E8s_v5 | yes | Nested virtualization. The normal choice. |
| B-series (burstable) | no | Avoid. No nesting, and the CPU credits run out mid-boot. |
| Do this | Why |
|---|---|
| A managed data disk for /opt/znetlab/images | Premium SSD, 256 GB or more. It detaches and reattaches, so the library survives a rebuilt VM. |
| A private Blob container as the archive | Push the licensed originals up once and pull them with azcopy copy straight into the drop folder. |
| A static public IP | Same reason as anywhere: accounts are issued this address from their plan, and a dynamic one breaks them on every deallocate. |
| Auto-shutdown | Built into the VM blade. A lab server nobody is using overnight is the easiest money to stop spending. |
The installer prints an address, a username and a password. That account is the only administrator until you make another, and ZNetLab seeds no demo accounts — there is nothing with a default password to forget about.
Open the ZNetLab site, sign in with that, and the Accounts tab, the Admin portal and the Images upload appear. Then, in this order:
ZNetLab ships no vendor software, and it never will — those images are licensed by their vendors. You put in the ones you are entitled to run. There is one destination and four ways to reach it.
Naming is the only thing ZNetLab asks of you, and only when it cannot work the name out itself. Get it right and there is nothing else to configure — RAM, interface naming and which engine runs it all come from the platform it recognises.
Want to prove the whole thing before you touch a licensed image? Several genuinely free ones need no licence at all — FRRouting, VyOS, Cumulus VX, Arista vEOS, pfSense CE, OPNsense, Alpine and Ubuntu Server. Drop one in and watch it come out on the shelf.
A lab server that looks up but silently does not forward traffic is the worst outcome, so ZNetLab ships two checks and the installer runs both. Run them yourself whenever you change the host.
Then the human check, which takes a minute: build two Virtual PCs and a LAN Switch, start it, and ping across. No image, no licence — if that works, the datapath works.
| Want | Do |
|---|---|
| Start at boot | systemctl enable --now znetlab — the unit is written for you. Not available under WSL2, which has no systemd. |
| Watch it | journalctl -u znetlab -f, or tail -f /var/log/znetlab.log |
| See what is running | The Admin portal — every lab, whose it is, and how much it is using. |
| Back it up | /var/lib/znetlab. Accounts, saved labs and the activity trail live there and cannot be recreated. Images can always be copied again. |
| Move the library | Point --images at another disk. Put it on its own volume from the start and a rebuild costs you nothing. |
| Do | Why |
|---|---|
| Do not open 8080 to the internet | Restrict the security group, NSG or firewall to the addresses that need it. This is the single most important line on this page. |
| Put TLS in front of it | nginx or Caddy terminating wss://, or a VPN. Credentials and console traffic both cross this socket. |
| Change the first password | The installer’s password was printed on a terminal, and on WSL2 it was generated into a scrollback buffer. |
| Keep /var/lib/znetlab at 700 | deploy.sh sets it. Password hashes and the activity trail are in there. |
| Give people the plan they need | Plan ceilings are enforced on the server, not in the browser. They are what stops one account exhausting the memory everybody shares. |
| Keep an eye on the licences | ZNetLab counts nothing for you here. The images are yours, and so is the entitlement to run them. |
Generated by tools/guide-pdf.js from the guide functions in lab/assets/app.js, the icon set in assets/icons.js and the plan table in assets/plans.js — the same plan table the server enforces. Every screen, button, path, port, command and ceiling named above is read out of the running product rather than transcribed, so this document goes stale only when the product does. Part I's plan tables are shown for the Free plan, which carries the tightest ceilings of any.