{
    "archive_path": "archive/1790805942.390267",
    "base_url": "www.netlify.com/blog/edge-functions-firecracker-microvms",
    "basename": "",
    "bookmarked_date": "2026-09-30 22:05",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/www.netlify.com/blog/edge-functions-firecracker-microvms",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=www.netlify.com",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": "www.netlify.com/blog/edge-functions-firecracker-microvms/index.html"
    },
    "domain": "www.netlify.com",
    "extension": "",
    "hash": "1RVQ8AWFQ0KN04P9GCJK",
    "history": {
        "archive_org": [],
        "dom": [],
        "favicon": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "120",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://www.google.com/s2/favicons?domain=www.netlify.com"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-09-30T22:05:55.761923+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1790805942.390267",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-30T22:05:55.075879+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "120",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://www.netlify.com/blog/edge-functions-firecracker-microvms/"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-09-30T22:05:56.167528+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1790805942.390267",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-30T22:05:55.770002+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [],
        "media": [],
        "mercury": [
            {
                "cmd": [
                    "/app/node_modules/@postlight/parser/cli.js",
                    "https://www.netlify.com/blog/edge-functions-firecracker-microvms/"
                ],
                "cmd_version": "1.0.0",
                "end_ts": "2026-09-30T22:06:08.191744+00:00",
                "index_texts": null,
                "output": "mercury",
                "pwd": "/data/archive/1790805942.390267",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-30T22:06:04.670829+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [],
        "readability": [
            {
                "cmd": [
                    "/app/node_modules/readability-extractor/readability-extractor",
                    "/tmp/tmp78dmenha",
                    "https://www.netlify.com/blog/edge-functions-firecracker-microvms/"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2026-09-30T22:06:04.662099+00:00",
                "index_texts": [
                    "About a billion Edge Functions run on Netlify every day \u2014 Sunweb personalizing pages, Loto-Qu\u00e9bec routing traffic on a cookie check, and hundreds of thousands of other sites doing everything from personalization to routing to auth. All of it runs on a full JavaScript runtime that scales with our customers\u2019 traffic.\nThis poses a significant technical challenge, as we strive to make the latency as low as possible. To run tens, sometimes hundreds of thousands, of edge functions per second, we need to process each request, route it correctly, allocate compute capacity, and boot both our platform code and the customer\u2019s code. All of that has to happen within milliseconds.\nOver the past several months, our team has rebuilt the infrastructure behind Edge Functions, working closely with the team at Unikraft, who wrote about the experience from their side. In the past, requests went out to a hosted execution service. Today, they run on MicroVMs inside our own edge network \u2014 roughly 5x faster at the median. That shift also improves security and reliability, and opens up more possibilities for running complex compute at the edge.\nThis doesn\u2019t change how Edge Functions are written or used \u2014 URL imports, npm packages, Node built-ins, netlify.toml declarations, local development \u2014 all of it works exactly as it did before. It\u2019s now faster and more resilient. In this article we\u2019d like to share more about the new architecture and our learnings on building a new compute platform that\u2019s able to serve high volume at a low performance overhead.\nThe numbers, first\nAn edge function runs in front of a site, on every request that matches it. The time it takes is time a customer spends waiting, so milliseconds here count for more than they do almost anywhere else.\nA warm invocation \u2014 routing to a compute node, entering a MicroVM, running the function, producing response headers \u2014 now costs:\n\n~5\u20136ms at median (p50), down from 25\u201340ms on our previous infrastructure\n47.4% faster p99 invocations\n99.998% availability\n5x faster edge function log delivery\n\n\nA cold invocation is worth stating too. When a request arrives in a region that no compute node has seen before, it needs to fetch the relevant images before it\u2019s able to run anything. This happens on about 1.2% of invocations and takes about 9ms on average.\nWhat happens during request time\nWhat follows is the path a single request takes, in order: it arrives at the edge node, it\u2019s turned into a specification, is routed to a compute node, and then handed off to a MicroVM that may or may not already exist based on whether it\u2019s a cold or warm invocation.\n\nRequest arrives at the edge node\nEvery request lands on the Netlify edge node closest to the client. The node terminates the TLS connection and checks the request path against the Edge Functions\u2019 routes for that deploy.\nIf nothing matches, the request carries on to the cache and onto the origin as usual. If a route does match, this is the point where the request used to leave our network. With our old infrastructure it went out over the internet, ran the edge function, and came back to us to pass on. With the new compute platform, the request is forwarded to a compute node within our network.\n\nCreating an Edge Function service\nWhen the compute node receives the request with the machine specification and the service ID, it first checks to see if a service with that ID already exists. If it does, it forwards the request into the service for it to send into the MicroVM. A service lets us have multiple MicroVMs associated with the same site\u2019s Edge Functions, and allows us to configure parameters for when to scale MicroVMs in and out. For example, we configure each service to only allow a fixed number of requests to be handled by a MicroVM before we shut it down, to avoid MicroVMs running indefinitely. We use the same parameters to know when to eagerly boot up another MicroVM in anticipation of one shutting down.\nIf a service for the site\u2019s edge functions doesn\u2019t already exist on the compute node, one is created, and we check to see if we have all the images in the machine specification on disk. If any are missing, they\u2019re fetched from the edge node and written to disk. This approach means we only fetch the edge function images that are receiving traffic in that region.\nThe edge node writes a spec\nBefore the request goes anywhere, the edge node writes a specification for the machine that will run a function. The spec names three images: the runtime, our platform image, and the edge function image. It also sets the CPU, memory, and connection limits.\nThe spec travels with the request, on every request. Its hash and site-specific information is computed to become a service ID. This allows for isolation, since two deploys with different code or different environment variables are different services, and they never share a MicroVM.\nThis isolation matters most for failures we don\u2019t want to be possible. A potentially compromised deploy runs in a separate MicroVM, and even if it escapes the runtime, it cannot poison other customers or the compute layer itself. V8 isolates, no matter their name, do not provide this level of isolation.\nChoosing a compute node to run an edge function\nEach region has a group of compute nodes. The edge node picks one for the service using rendezvous hashing: the same service lands on the same node every time, which is what keeps a MicroVM warm and the code already on disk and in cache once it\u2019s been read. This stickiness gives us a caching strategy. If we spread requests evenly across the swarm, we\u2019d end up with a higher level of cold starts.\nIt\u2019s important to remember that while sending every request for a function to the same compute node is the fast path, it\u2019s also how a hot spot forms \u2014 where one busy function competes for resources with everything else on that box. A service taking a large share of a region\u2019s traffic pinned to a single node will saturate the node at the expense of other services.\nWe balance this by relaxing the stickiness. Over a certain threshold, we spread the service across a slice of nodes. This lets us absorb sudden spikes in traffic from a single customer without affecting other services that hashed to the same node.\nFinally, once a node is chosen, it pulls in the function\u2019s code. A compute node that has served the function before already has it. A node seeing it for the first time fetches it once and caches it, so only the first request pays that cost.\n\nStarting the MicroVM\nEach function runs in its own Firecracker MicroVM. These are created in under a millisecond and start in about 2ms at p99, because the VM starts a stripped-down Linux environment rather than a full operating system. The edge function\u2019s files are mounted as an uncompressed EROFS image and then memory-mapped, so the VM reads only the parts of the bundle it actually uses instead of loading all of it.\nWhen the MicroVM boots up and the JavaScript server begins to listen on a port, we take a snapshot of the MicroVM. When the edge function isn\u2019t being invoked, the MicroVMs running it scale to zero instead of sitting idle. The next time it\u2019s invoked, we start a new MicroVM from that snapshot. The snapshot is memory-mapped, so the VM can start executing without waiting for the entire snapshot to be read back into memory.\nThe lifecycle of the VM \u2014 boot, snapshot, restore, and scale to zero \u2014 is the work of Unikraft\u2019s product. We worked closely with them throughout the migration to make sure it holds up under our request volume and traffic patterns.\nRunning and response handling\nAfter operating this project for several years, we already had learnings we incorporated to maximize performance and the ability to debug. At scale, we\u2019ve run into all kinds of issues, from running out of ports on virtual switches to DNS (we were surprised, but it wasn\u2019t always DNS).\nIn this iteration, we made sure compute nodes run local DNS resolvers. We\u2019ve also expanded the metrics we collect, recording things like boot time, time to first port open, and time to start user code. There are also several circuit breakers in place to ensure prompt rerouting and decommissioning of compute nodes.\n\nThat\u2019s the whole path, and on a warm instance it adds about 6ms. None of it leaves our network, and we\u2019re in control of the whole request cycle. Everything above happens between the request arriving and the response going back out.\nDesigning resilient compute infrastructure\nWhen you build a system like the one described above, you\u2019re optimizing for two things at once: the end-user experience and rollout resiliency. We need to be able to roll out changes quickly but balance that with the ability to roll back just as quickly.\nThe compute nodes are built from a base image published by Unikraft and install a set of packages. These nodes are built separately from our edge nodes for a couple of reasons: it keeps our edge nodes lightweight and fast, it lets us use different instance types for our compute nodes, and it lets us scale these nodes independently.\nA control plane keeps track of which compute nodes exist and which are healthy, and the edge nodes poll it for that list. It\u2019s also what drives a deploy \u2014 a new fleet comes up alongside the running one, scales to match it, and takes over traffic only once it\u2019s healthy.\nBuilding the compute infrastructure has required close collaboration with the Unikraft team. Throughout the migration, we\u2019ve worked with them on testing correctness, handling large volumes of requests, and building capabilities specific to our platform.\nIt\u2019s live\nThe work to rebuild our edge compute architecture is more than just a speed boost. It\u2019s a faster foundation we can keep building on and have more control over. The best part is that it\u2019s already serving your production traffic today, at the same pricing, with no migration step and nothing to change in any project.\nRunning the compute ourselves means the ceiling on Edge Functions is ours to raise. Three things this makes tractable that weren\u2019t before:\n\nnpm package support, out of beta. npm packages work in edge functions today, in beta, with caveats around native binaries and importing files at runtime. A real VM with a real filesystem removes most of the reasons those caveats exist.\nRoom to revisit the operation limits. The documented limits of 50ms of CPU per request, 512MB of memory, and 20MB of compressed code came from the isolate-based execution model.\nCompute inside our own network. Anything that depends on controlling the network path, instead of reaching a third party across the internet, is now something we can build.\n\nWe\u2019re not done here. The limits and rough edges we couldn\u2019t touch before are the ones we\u2019re working on now, so stay tuned."
                ],
                "output": "readability",
                "pwd": "/data/archive/1790805942.390267",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-30T22:06:02.706228+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [],
        "singlefile": [],
        "title": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "120",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://www.netlify.com/blog/edge-functions-firecracker-microvms/"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-09-30T22:06:02.700106+00:00",
                "index_texts": null,
                "output": "5x faster Edge Functions: How we replaced v8 isolates with Firecracker MicroVMs",
                "pwd": "/data/archive/1790805942.390267",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-30T22:06:02.657460+00:00",
                "status": "succeeded"
            }
        ],
        "wget": [
            {
                "cmd": [
                    "wget",
                    "--no-verbose",
                    "--adjust-extension",
                    "--convert-links",
                    "--force-directories",
                    "--span-hosts",
                    "--no-parent",
                    "-e",
                    "robots=off",
                    "--reject=js,mjs,woff,woff2,ttf,otf,eot,mp4,m4v,mov,webm,ogv,mkv,avi,flv",
                    "--timeout=120",
                    "--restrict-file-names=windows",
                    "--page-requisites",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) wget/GNU Wget 1.21.3",
                    "--compression=auto",
                    "--timestamping",
                    "https://www.netlify.com/blog/edge-functions-firecracker-microvms/"
                ],
                "cmd_version": "GNU Wget 1.21.3",
                "end_ts": "2026-09-30T22:06:02.648865+00:00",
                "index_texts": null,
                "output": "www.netlify.com/blog/edge-functions-firecracker-microvms/index.html",
                "pwd": "/data/archive/1790805942.390267",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-30T22:05:56.176791+00:00",
                "status": "succeeded"
            }
        ]
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": null,
        "dom": null,
        "favicon": "favicon.ico",
        "git": null,
        "media": null,
        "pdf": null,
        "screenshot": null,
        "singlefile": null,
        "title": "5x faster Edge Functions: How we replaced v8 isolates with Firecracker MicroVMs",
        "warc": null,
        "wget": "www.netlify.com/blog/edge-functions-firecracker-microvms/index.html"
    },
    "link_dir": "/data/archive/1790805942.390267",
    "newest_archive_date": "2026-09-30T22:06:04.670829+00:00",
    "num_failures": 0,
    "num_outputs": 6,
    "oldest_archive_date": "2026-09-30T22:05:55.075879+00:00",
    "path": "/blog/edge-functions-firecracker-microvms/",
    "schema": "Link",
    "scheme": "https",
    "snapshot_id": "c3148e8f-4e0f-44c5-93fd-648cf49c9f19",
    "sources": [
        "/data/sources/1790805942-import.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1790805942.390267",
    "title": "5x faster Edge Functions: How we replaced v8 isolates with Firecracker MicroVMs",
    "updated": "2026-09-30T22:05:55.075243+00:00",
    "updated_date": "2026-09-30 22:05",
    "url": "https://www.netlify.com/blog/edge-functions-firecracker-microvms/"
}