Blog
5 min read

vsock Explained: Talking Between a VM and Its Host Without a Network

vsock is a socket family for communication between a virtual machine and its host — no IP addresses, no network interface, no firewall rules. How AF_VSOCK addressing with CIDs and ports works, vhost-vsock in QEMU, Firecracker's Unix-socket bridge, how sandboxes use it for guest agents, and the security model.

A sandbox running in a microVM needs to talk to the host: receive commands, stream back stdout, report that a process exited. The obvious approach is networking — give the VM an interface and an IP, and run an HTTP server inside. But networking brings IP allocation, routing, firewall rules, and the risk that the guest can reach things it shouldn't. (Run untrusted AI-generated code safely)

vsock (virtio socket, AF_VSOCK) is the alternative: a socket family purpose-built for host ↔ guest communication. It uses the familiar sockets API — socket, bind, listen, connect, accept — but with no network stack underneath. A VM can have vsock and no network interface at all.

Addressing: CIDs and ports

Instead of IP addresses, vsock endpoints are identified by a context ID (CID) and a 32-bit port:

CID Meaning
0 Reserved (hypervisor)
1 Local loopback (VMADDR_CID_LOCAL, Linux 5.6+)
2 The host (VMADDR_CID_HOST)
3+ Guests — each VM gets a unique CID assigned by the hypervisor
-1 VMADDR_CID_ANY for binding

From inside a guest, connecting to CID 2 reaches the host. From the host, connecting to CID 42 reaches the VM that was given CID 42. Guests generally can't reach each other — traffic only flows between a guest and its host.

A minimal example

Guest agent, listening on port 1024 (Python, which supports AF_VSOCK natively on Linux):

import socket

s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
s.bind((socket.VMADDR_CID_ANY, 1024))
s.listen()
while True:
    conn, (cid, port) = s.accept()
    cmd = conn.recv(4096)
    conn.sendall(b"ran: " + cmd)
    conn.close()

Host, connecting to the guest with CID 3 (using QEMU/vhost-vsock):

s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
s.connect((3, 1024))
s.sendall(b"echo hi")
print(s.recv(4096))

For quick tests, socat speaks vsock:

socat - VSOCK-CONNECT:3:1024        # host → guest
socat VSOCK-LISTEN:1024,fork EXEC:/bin/cat   # simple echo server in guest

SOCK_STREAM is supported everywhere; SOCK_SEQPACKET (message boundaries) is available in newer kernels and some transports.

How it's wired up

Inside the guest, vsock is a virtio device (virtio-vsock). The guest kernel's driver moves data through shared-memory virtqueues — the same efficient mechanism virtio uses for disks and network cards. (How KVM works)

On the host side, it depends on the hypervisor:

QEMU with vhost-vsock. The host kernel module vhost_vsock handles the device in-kernel, and host processes use real AF_VSOCK sockets:

sudo modprobe vhost_vsock
qemu-system-x86_64 ... -device vhost-vsock-pci,guest-cid=3

Firecracker's hybrid vsock. Firecracker implements the device in its own process and exposes it on the host as a Unix domain socket — so host code doesn't need AF_VSOCK or the vhost module:

"vsock": { "guest_cid": 3, "uds_path": "/run/fc/vm1.vsock" }
  • Host → guest: connect to /run/fc/vm1.vsock, send CONNECT 1024\n, read back OK <port>\n, then the stream is connected to the guest's port 1024.
  • Guest → host: when the guest connects to CID 2 port 5000, Firecracker connects to a Unix socket at /run/fc/vm1.vsock_5000, which your host service listens on.

That design plays nicely with the jailer: each VM's sockets are just files inside its chroot, with ordinary file permissions. (Firecracker snapshots)

Who uses it

  • Kata Containers — the host shim talks to kata-agent inside the VM over vsock to create containers, exec processes and stream I/O. (Kata Containers)
  • Firecracker-based sandboxes and serverless platforms — command channels, log streaming, and metrics out of the guest.
  • AWS Nitro Enclaves — vsock is the only channel between an enclave and its parent instance, which is the whole point of the design.
  • Confidential computing and guest agents — QEMU guest agent can use it, cloud-init alternatives, attestation flows.
  • Hyper-V has the equivalent concept (hv_sock / Hyper-V sockets), which Linux also supports through the same AF_VSOCK family.

Why it's good for sandboxes

  1. No network needed. A code-execution sandbox can have zero network access and still be fully controllable. Egress can be added deliberately later, through a proxy you control. (Agent egress control proxy)
  2. No IP management. CIDs are assigned per VM; nothing to allocate, route or NAT.
  3. Nothing to scan. A guest can't use vsock to reach your internal network, metadata services or other VMs — it can only open connections to the specific host ports something is listening on. (SSRF explained)
  4. Simple code. It's a stream socket; any RPC protocol (gRPC, JSON lines, ttrpc) runs over it.

The security model

vsock narrows the attack surface, but it is still a channel from untrusted code to a trusted host process:

  • Treat everything from the guest as hostile input. The host-side service that accepts guest connections is now part of your sandbox boundary. Validate message sizes, parse defensively, and never let the guest name files or commands on the host.
  • Know which side listens. Prefer host → guest connections for control. If the host must accept guest-initiated connections, listen only on specific ports and authenticate the guest if multiple VMs share a listener.
  • Identify the VM by the channel, not by its claims. With vhost-vsock, the peer CID comes from the hypervisor and can't be spoofed by the guest. With Firecracker, the Unix socket path itself identifies the VM.
  • Resource limits. A guest can open many connections or stream large volumes; rate-limit at the host service.
  • Keep the device surface small. vsock is one of the few virtio devices a minimal sandbox needs; drop the others. (Firecracker vs gVisor vs containers)

Limitations

  • Host ↔ guest only. For VM-to-VM traffic, use real networking or relay through the host.
  • Language and library support is uneven: Python, Go, Rust and C are easy; Node.js has no built-in AF_VSOCK (use a native module, or Firecracker's Unix-socket side on the host).
  • Live migration and snapshot/restore drop open connections; agents must reconnect.
  • Tools like tcpdump don't see it by default, so debugging needs logging at both ends (or vsockmon interfaces on supported kernels).

EasySpawn gives each server its own VM, and Claude Code runs on that server next to your app — no host channel to secure, just your machine. See how it works or join the waitlist.

Related: Kata Containers · Firecracker Snapshots · How KVM Works · Firecracker vs gVisor vs Containers

Keep reading