Deploy Firecracker on a VPS
AWS's Lambda/Fargate VMM. Sub-second microVM boots, ~5 MiB overhead per instance, jailer-wrapped isolation.
At a Glance
| Project | Firecracker (AWS) |
| License | Apache 2.0 |
| Requires | KVM (/dev/kvm) on host |
| OS | Ubuntu 22.04 / 24.04, kernel 5.10+ |
| Use cases | FaaS, hardened container isolation, per-tenant VMs |
Verify KVM Availability
lsmod | grep kvm
ls -l /dev/kvm
grep -E 'vmx|svm' /proc/cpuinfo | head -1If /dev/kvm does not exist, your VPS does not expose nested virtualization and Firecracker will not work. There is no workaround — move to a host that supports KVM.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ca-certificates iproute2 iptables net-tools bridge-utils acl jq
sudo usermod -aG kvm $USER
sudo setfacl -m u:${USER}:rw /dev/kvm
[ -r /dev/kvm ] && [ -w /dev/kvm ] && echo "OK" || echo "FAIL"Install Firecracker + Jailer
cd /tmp
ARCH=$(uname -m)
FC_VERSION=$(curl -s https://api.github.com/repos/firecracker-microvm/firecracker/releases/latest | jq -r .tag_name)
curl -fL "https://github.com/firecracker-microvm/firecracker/releases/download/${FC_VERSION}/firecracker-${FC_VERSION}-${ARCH}.tgz" -o firecracker.tgz
tar -xzf firecracker.tgz
sudo install -m 0755 "release-${FC_VERSION}-${ARCH}/firecracker-${FC_VERSION}-${ARCH}" /usr/local/bin/firecracker
sudo install -m 0755 "release-${FC_VERSION}-${ARCH}/jailer-${FC_VERSION}-${ARCH}" /usr/local/bin/jailer
firecracker --version && jailer --versionKernel + Rootfs Image
mkdir -p /var/lib/firecracker/images && cd /var/lib/firecracker/images
sudo chown $USER:$USER /var/lib/firecracker/images
ARCH=$(uname -m)
curl -L "https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/${ARCH}/vmlinux-5.10.225" -o vmlinux.bin
curl -L "https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/${ARCH}/ubuntu-24.04.squashfs" -o ubuntu-24.04.squashfsmkdir -p /tmp/sqfs-mnt /tmp/ext4-mnt
sudo mount -o loop,ro ubuntu-24.04.squashfs /tmp/sqfs-mnt
dd if=/dev/zero of=ubuntu-24.04.ext4 bs=1M count=2048
mkfs.ext4 ubuntu-24.04.ext4
sudo mount -o loop ubuntu-24.04.ext4 /tmp/ext4-mnt
sudo rsync -a /tmp/sqfs-mnt/ /tmp/ext4-mnt/
sudo umount /tmp/ext4-mnt /tmp/sqfs-mntHost Bridge + Tap Networking
sudo tee /etc/systemd/network/10-fc-br0.netdev > /dev/null <<'EOF'
[NetDev]
Name=fc-br0
Kind=bridge
EOF
sudo tee /etc/systemd/network/20-fc-br0.network > /dev/null <<'EOF'
[Match]
Name=fc-br0
[Network]
Address=172.30.0.1/24
IPMasquerade=ipv4
ConfigureWithoutCarrier=true
EOF
sudo systemctl enable --now systemd-networkd
sudo systemctl restart systemd-networkdecho "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-firecracker.conf
sudo sysctl --system
PRIMARY_IFACE=$(ip route show default | awk '/default/ {print $5}' | head -1)
sudo iptables -t nat -A POSTROUTING -s 172.30.0.0/24 ! -o fc-br0 -j MASQUERADE
sudo iptables -A FORWARD -i fc-br0 -j ACCEPT
sudo iptables -A FORWARD -o fc-br0 -j ACCEPT
sudo apt install -y iptables-persistent
sudo netfilter-persistent savesudo ip tuntap add tap0 mode tap
sudo ip link set tap0 master fc-br0
sudo ip link set tap0 upBoot Your First MicroVM
{
"boot-source": {
"kernel_image_path": "/var/lib/firecracker/images/vmlinux.bin",
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off ip=172.30.0.10::172.30.0.1:255.255.255.0::eth0:off"
},
"drives": [
{
"drive_id": "rootfs",
"path_on_host": "/var/lib/firecracker/images/ubuntu-24.04.ext4",
"is_root_device": true,
"is_read_only": false
}
],
"network-interfaces": [
{
"iface_id": "eth0",
"guest_mac": "AA:FC:00:00:00:01",
"host_dev_name": "tap0"
}
],
"machine-config": {
"vcpu_count": 1,
"mem_size_mib": 256,
"smt": false
}
}mkdir -p /var/lib/firecracker/vms/test1
cp vm-config.json /var/lib/firecracker/vms/test1/
firecracker --config-file /var/lib/firecracker/vms/test1/vm-config.json
# Default creds for the sample image: root / root
# Inside guest: ip a; ping 8.8.8.8Run Under the Jailer (Production)
For production, never run firecracker directly. The jailer sets up chroot, drops capabilities, applies cgroups, switches UID/GID, and applies seccomp filters before exec.
sudo useradd --system --create-home --home-dir /var/lib/firecracker --shell /usr/sbin/nologin firecracker
VM_ID="vm-prod-1"
JAIL_BASE="/srv/jailer"
JAIL_ROOT="${JAIL_BASE}/firecracker/${VM_ID}/root"
sudo mkdir -p "${JAIL_ROOT}"
sudo cp /var/lib/firecracker/images/vmlinux.bin "${JAIL_ROOT}/vmlinux.bin"
sudo cp /var/lib/firecracker/images/ubuntu-24.04.ext4 "${JAIL_ROOT}/rootfs.ext4"
FC_UID=$(id -u firecracker); FC_GID=$(id -g firecracker)
sudo chown -R ${FC_UID}:${FC_GID} "${JAIL_BASE}/firecracker/${VM_ID}"
sudo ip tuntap add tap-prod-1 mode tap user firecracker
sudo ip link set tap-prod-1 master fc-br0
sudo ip link set tap-prod-1 upsudo jailer \
--id "${VM_ID}" \
--exec-file /usr/local/bin/firecracker \
--uid ${FC_UID} --gid ${FC_GID} \
--chroot-base-dir "${JAIL_BASE}" \
--new-pid-ns \
--resource-limit no-file=2048 \
--resource-limit fsize=10737418240 \
-- --api-sock /run/firecracker.socketProduction Host Hardening
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
echo 0 | sudo tee /sys/kernel/mm/ksm/run
echo "@reboot root echo 0 > /sys/kernel/mm/ksm/run" | sudo tee /etc/cron.d/disable-ksmApply CPU microcode updates, run a recent kernel (5.10/6.1 are tested), and confirm speculative-execution mitigations with lscpu | grep -i vulnerab. For production guests, drop the serial console and pass 8250.nr_uarts=0.
Snapshots, Caddy Ingress, Updates
sudo curl --unix-socket "${API_SOCKET}" -X PUT 'http://localhost/snapshot/create' \
-H 'Content-Type: application/json' \
-d '{
"snapshot_type": "Full",
"snapshot_path": "/var/backups/snapshot.bin",
"mem_file_path": "/var/backups/memory.bin"
}'app.example.com {
reverse_proxy 172.30.0.10:8080 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
encode zstd gzip
header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}Updates are a binary swap of firecracker and jailer. Running microVMs are unaffected; new launches pick up the new binary. Read the changelog before upgrading — API field names occasionally shift.
