Your Brand New VM Is Terrible at Generating Secrets
You just spun up a fresh VM, ran ssh-keygen, and it finished instantly. That seems great. It’s not great.
Generating a cryptographic key requires high-quality randomness. Real randomness — not pseudo-random patterns, not predictable sequences. The Linux kernel collects “entropy” (unpredictability) from physical hardware events: keyboard interrupts, disk seeks, network packet timing, hardware noise. On a headless VM with no keyboard, no spinning disk, and deterministic network timing? The entropy pool is nearly empty when you boot.
This matters because a weak random number generator produces predictable keys. Predictable keys get broken. That’s not a theoretical concern — it’s been demonstrated in practice with real servers.
Let’s understand what’s actually happening and how to fix it.
The Entropy Pool: What It Is
The Linux kernel maintains an entropy pool — a large internal state that mixes in unpredictable data from hardware events. Every interrupt, every disk seek timing irregularity, every USB transaction jitter contributes bits of entropy.
Check your current entropy pool size:
cat /proc/sys/kernel/random/entropy_avail# Should be a number between 0 and 4096 on older kernels# On 5.x+ kernels it's less meaningful (read on)
cat /proc/sys/kernel/random/poolsize# Typically 4096 bitsOn a fresh VM, you might see entropy_avail hovering around 200-500 at boot. On a busy server, it’ll be higher. On a hardware server with real I/O, it typically stays well-stocked.
/dev/random vs /dev/urandom: The Eternal Debate
This distinction confused people for decades and caused a lot of bad decisions.
Historical behavior (pre-kernel 5.6):
/dev/random: blocking. Would block if entropy pool fell below a threshold. Cryptographically strong./dev/urandom: non-blocking. Would fall back to a CSPRNG (cryptographically secure pseudorandom number generator) when entropy was low. Also cryptographically strong, but old docs warned it wasn’t “truly random.”
Modern behavior (kernel 5.6+):
/dev/randomand/dev/urandomare functionally identical (both non-blocking)/dev/randomno longer blocks after the initial seeding is complete- Both use the same CSPRNG (ChaCha20-based) under the hood
getrandom()syscall is the preferred interface anyway
The practical takeaway: use /dev/urandom or getrandom(). Don’t use /dev/random unless you’re on a very old kernel and have a specific reason. Modern cryptography experts are unanimous on this.
# Generate 32 bytes of random datadd if=/dev/urandom bs=32 count=1 | xxd
# Using getrandom in Python (preferred for applications)python3 -c "import secrets; print(secrets.token_hex(32))"
# Or the direct syscallpython3 -c "import os; print(os.urandom(32).hex())"Why VMs and Containers Have Low Entropy
Physical servers collect entropy from:
- Keyboard and mouse interrupts (timing jitter)
- Disk seeks and rotational latency (not present on SSDs, but timing still varies)
- Network packet arrival timing
- CPU performance counter jitter
- Hardware noise amplified through voltage sensors
VMs don’t have most of this. They have:
- Virtual disk I/O with deterministic timing
- Emulated NICs
- No physical keyboard in typical headless deployments
- Hypervisor-controlled scheduling that can remove timing jitter
The result: VMs boot with low entropy and accumulate it slowly. In the worst case, blocking calls to /dev/random could stall for minutes on early boots.
Containers are even worse — they share the host kernel’s entropy pool, and if the host is entropy-starved, every container on it is.
Checking Your Entropy in Real Time
# Watch entropy level change over timewatch -n 1 cat /proc/sys/kernel/random/entropy_avail
# Generate some I/O to see entropy risefind / -type f > /dev/null 2>&1 &# Watch the entropy counter
# More accurate: use rngtest to test qualitysudo apt install rng-toolscat /dev/urandom | rngtest -c 1000# Should show "FIPS 140-2 successes: 1000" or similarThe Fix: haveged
Haveged (HArdware Volatile Entropy Gathering and Expansion Daemon) is the classic solution for entropy-starved VMs. It uses the HAVEGE algorithm, which exploits unpredictability in CPU execution (branch predictor state, cache behavior, instruction timing) to generate entropy.
# Installsudo apt install haveged # Debian/Ubuntusudo dnf install haveged # RHEL/Fedora
# Enable and startsudo systemctl enable --now haveged
# Verify it's feeding entropywatch -n 1 cat /proc/sys/kernel/random/entropy_avail# Should stay comfortably high (2000+)Haveged works well and is widely deployed. There are occasional academic critiques about the quality of HAVEGE entropy, but it’s considered good enough for production use and dramatically better than nothing.
virtio-rng: The Right Answer for KVM VMs
If you’re running KVM/QEMU VMs, there’s a better solution: virtio-rng. This passes entropy from the hypervisor host directly to the guest VM through a virtual RNG device.
On the host side, make sure the VM is configured with a virtio-rng device. In a libvirt/QEMU config:
<!-- In your VM's XML definition --><devices> <rng model="virtio"> <backend model="random">/dev/urandom</backend> <rate period="2000" bytes="1024"/> </rng></devices>Or with qemu directly:
qemu-system-x86_64 ... -object rng-random,filename=/dev/urandom,id=rng0 \ -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=2000On the guest:
# Check if virtio-rng device is presentls /sys/bus/virtio/drivers/virtio_rng/lsmod | grep virtio_rng
# Load the module if not loadedsudo modprobe virtio-rng
# Verify it's registered as a hw_random sourcecat /sys/class/misc/hw_random/rng_currentThen configure rng-tools to feed it into the kernel pool:
sudo apt install rng-tools# Enable the rngd servicesudo systemctl enable --now rng-toolsHardware RNG: /dev/hwrng
Some systems have dedicated hardware random number generators. Modern Intel/AMD CPUs include RDRAND and RDSEED instructions. TPM chips include RNG capabilities. Some embedded boards have dedicated entropy sources.
# Check if hardware RNG is availablels /dev/hwrng# If it exists, you have hardware RNG
# Check which driver is backing itcat /sys/class/misc/hw_random/rng_availablecat /sys/class/misc/hw_random/rng_current
# Test read speed (hardware RNG should be fast)dd if=/dev/hwrng of=/dev/null bs=512 count=100The rngd daemon (from rng-tools) automatically reads from /dev/hwrng and feeds it to the kernel entropy pool:
sudo apt install rng-toolssudo systemctl enable --now rng-tools
# Manually specify the source if neededsudo rngd -r /dev/hwrng -o /dev/random -fFor AWS EC2 instances, they provide a hardware RNG via the virtio-rng interface automatically. GCP and Azure also provision entropy sources for VMs.
Practical: Checking Before Key Generation
Here’s a quick check to run before generating critical keys:
#!/bin/bashENTROPY=$(cat /proc/sys/kernel/random/entropy_avail)echo "Current entropy: $ENTROPY bits"
if [ $ENTROPY -lt 1000 ]; then echo "WARNING: Low entropy. Consider installing haveged." echo "Run: sudo apt install haveged && sudo systemctl enable --now haveged"else echo "Entropy looks healthy."fi
# Show available RNG sourcesecho ""echo "Available RNG sources:"cat /sys/class/misc/hw_random/rng_available 2>/dev/null || echo "No hw_random device"The Container Situation
Containers share the host kernel’s entropy pool. A host with good entropy = all containers get good entropy. A starved host = every container suffers.
For container workloads:
- Make sure the host has haveged or virtio-rng running
- Don’t try to run haveged inside the container itself (it needs hardware access)
- Consider mounting the host’s
/dev/urandominto the container if you need fine-grained control
services: app: image: myapp devices: - /dev/urandom:/dev/urandomDoes This Matter for Modern Kernels?
Sort of. On kernel 5.6+, /dev/urandom is non-blocking and uses a CSPRNG that’s seeded once at boot. After initial seeding, it doesn’t deplete. So the “block forever waiting for entropy” problem largely doesn’t exist on modern kernels.
But: if your VM boots with very low entropy and you generate keys before the initial seeding completes, you can still have a problem. The kernel now waits until it has enough entropy for that initial seed before unlocking the RNG, so very early key generation can still stall.
The solution is the same: get more entropy sources, use haveged or virtio-rng, don’t generate critical keys immediately at first boot before the system has had time to collect entropy.
For anything touching cryptography — SSH keys, TLS certs, GPG keys, session tokens — a few seconds checking your entropy situation is worth it. Your 2 AM self debugging “why did this key fail to verify” will agree.