I Tried Out Lambda MicroVMs on the x86 Architecture

I Tried Out Lambda MicroVMs on the x86 Architecture

AWS Lambda MicroVMs run on x86 when X86_64 is specified at image creation with base image version 3 or later. In the Tokyo region, x86 pricing is now about 15% higher than Arm.
2026.11.13

This page has been translated by machine translation. View original

Introduction

On November 11, 2026, x86 architecture support for Lambda MicroVMs was announced.

https://aws.amazon.com/jp/about-aws/whats-new/2026/11/aws-lambda-microvms-x86-architecture/

At GA in June 2026, the only supported architecture was Arm (AWS Graviton). With this update, you can now choose between Arm and x86.

https://dev.classmethod.jp/articles/aws-lambda-microvms-flask-suspend-resume/

This article introduces the results of trying x86 Lambda MicroVMs in the Tokyo region.

Specifying x86

Querying supported versions

x86 is supported in managed base image version 3 and later. You can check supportedCpuConfigurations for each version with list-managed-microvm-image-versions.

aws lambda-microvms list-managed-microvm-image-versions --image-identifier arn:aws:lambda:ap-northeast-1:aws:microvm-image:al2023-1 --region ap-northeast-1
Version Created (JST) supportedCpuConfigurations
3 2026-10-24 ARM_64, X86_64
2 2026-09-30 ARM_64
1 2026-07-17 ARM_64
0 2026-06-18 ARM_64

Creating and launching an image

I used a Dockerfile based on the multi-architecture node:24-alpine.

Dockerfile
FROM node:24-alpine
WORKDIR /app
COPY app.js .
EXPOSE 8080
CMD ["node", "app.js"]

I added --cpu-configurations architecture=X86_64 to create-microvm-image to create an x86 image. The AWS CLI version is 2.40.2.

aws lambda-microvms create-microvm-image --name devio-x86-test-x86 --code-artifact uri=s3://amzn-s3-demo-bucket/app.zip --base-image-arn arn:aws:lambda:ap-northeast-1:aws:microvm-image:al2023-1 --build-role-arn arn:aws:iam::123456789012:role/MicroVMBuildRole --cpu-configurations architecture=X86_64

The cpuConfigurations in the response was X86_64. Because I did not specify the base image version, baseImageVersion became 3.1.

    "cpuConfigurations": [
        {
            "architecture": "X86_64"
        }
    ],

This is the result of calling /arch on the MicroVM launched from this image. EX and TOKEN_x86 are the MicroVM's endpoint and authentication token.

$ curl -sS -m 30 "https://$EX/arch" -H "X-aws-proxy-auth: $TOKEN_x86"
{"uname_m":"x86_64","process_arch":"x64"}

For reference, if you build from the same Dockerfile without --cpu-configurations, you get an Arm image.

aws lambda-microvms create-microvm-image --name devio-x86-test-default --code-artifact uri=s3://amzn-s3-demo-bucket/app.zip --base-image-arn arn:aws:lambda:ap-northeast-1:aws:microvm-image:al2023-1 --build-role-arn arn:aws:iam::123456789012:role/MicroVMBuildRole

The cpuConfigurations in the response was ARM_64, and baseImageVersion was 3.1, the same as x86.

    "cpuConfigurations": [
        {
            "architecture": "ARM_64"
        }
    ],

This is the result of calling /arch on the MicroVM launched from this Arm image. EA and TOKEN_default are that MicroVM's endpoint and authentication token.

$ curl -sS -m 30 "https://$EA/arch" -H "X-aws-proxy-auth: $TOKEN_default"
{"uname_m":"aarch64","process_arch":"arm64"}

Here is the full source of the app used for verification.

app.js
app.js
// 検証用: アーキテクチャと実行環境の CPU・メモリを返す HTTP サーバー(ポート 8080)。
// 読むのは固定のファイルと固定の API だけ。リクエストの内容はパスの分岐にしか使わない。
// 環境変数と /proc/self/environ は読まない。ホスト名は返さない。
const http = require('http');
const os = require('os');
const fs = require('fs');
const { execFileSync } = require('child_process');

const read = (p) => {
  try { return fs.readFileSync(p, 'utf8'); } catch (e) { return `ERROR: ${e.code}`; }
};

// /proc/cpuinfo は先頭 1 コアのブロックと、コア数だけ返す。
const CPU_KEYS_X86 = ['vendor_id', 'cpu family', 'model', 'model name', 'stepping', 'microcode', 'cpu MHz', 'cache size', 'flags'];
const CPU_KEYS_ARM = ['BogoMIPS', 'Features', 'CPU implementer', 'CPU architecture', 'CPU variant', 'CPU part', 'CPU revision'];
const cpuinfo = () => {
  const text = read('/proc/cpuinfo');
  const blocks = text.split(/\n\s*\n/).filter((b) => b.trim());
  const first = {};
  for (const line of (blocks[0] || '').split('\n')) {
    const i = line.indexOf(':');
    if (i < 0) continue;
    const k = line.slice(0, i).trim();
    const v = line.slice(i + 1).trim();
    if (CPU_KEYS_X86.includes(k) || CPU_KEYS_ARM.includes(k)) first[k] = v;
  }
  const flags = (first.flags || '').split(/\s+/).filter(Boolean);
  const feats = (first.Features || '').split(/\s+/).filter(Boolean);
  return {
    processorBlocks: blocks.length,
    first,
    x86FlagCount: flags.length,
    x86Flags: flags.filter((f) => /^(avx|avx2|avx512.*|sse4_2|amx.*|vnni|avx_vnni|bmi2|sha_ni|vaes|vpclmulqdq|gfni|rdrand|rdseed|adx|fma|erms|fsrm|serialize|tsxldtrk|ibrs|ibpb|stibp|ssbd|md_clear|hypervisor)$/.test(f)),
    armFeatureCount: feats.length,
  };
};

const meminfo = () => {
  const out = {};
  for (const line of read('/proc/meminfo').split('\n')) {
    const m = line.match(/^(MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree):\s+(\d+) kB/);
    if (m) out[m[1]] = `${m[2]} kB`;
  }
  return out;
};

const status = () => {
  const out = {};
  for (const line of read('/proc/self/status').split('\n')) {
    const m = line.match(/^(Cpus_allowed_list|Cpus_allowed|Mems_allowed_list):\s+(.*)$/);
    if (m) out[m[1]] = m[2];
  }
  return out;
};

const osRelease = () => {
  const out = {};
  for (const line of read('/etc/os-release').split('\n')) {
    const m = line.match(/^(PRETTY_NAME|NAME|VERSION|VERSION_ID|ID)=(.*)$/);
    if (m) out[m[1]] = m[2].replace(/^"|"$/g, '');
  }
  return out;
};

const report = () => {
  const cpus = os.cpus();
  return {
    uname_m: execFileSync('uname', ['-m']).toString().trim(),
    node: { version: process.version, arch: process.arch, platform: process.platform },
    os: { release: os.release(), version: os.version(), type: os.type(), machine: os.machine() },
    procVersion: read('/proc/version').trim(),
    osRelease: osRelease(),
    vcpu: {
      availableParallelism: os.availableParallelism(),
      osCpusLength: cpus.length,
      osCpusModel: cpus.length ? cpus[0].model : null,
      osCpusSpeedMHz: cpus.length ? cpus[0].speed : null,
      ...status(),
    },
    cpuinfo: cpuinfo(),
    memory: { ...meminfo(), osTotalmemBytes: os.totalmem(), osFreememBytes: os.freemem() },
  };
};

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  if (req.url === '/arch') {
    res.end(JSON.stringify({ uname_m: execFileSync('uname', ['-m']).toString().trim(), process_arch: process.arch }) + '\n');
  } else if (req.url === '/env-report') {
    res.end(JSON.stringify(report(), null, 2) + '\n');
  } else {
    res.end(JSON.stringify({ status: 'ok' }) + '\n');
  }
});

server.listen(8080, () => {
  console.log('Listening on port 8080');
});

Checking CPU and memory

I retrieved the MicroVM's CPU and memory information from the app's /env-report. The values come from /proc/cpuinfo, /proc/meminfo, /proc/self/status, and Node.js's os module.

CPU

The vCPU count was 4 on both Arm and x86. Node.js's availableParallelism and the processor count in /proc/cpuinfo were 4, and Cpus_allowed_list was 0-3.

In the build information for the image specified as x86 (get-microvm-image-build), the chipset was INTEL. There were two builds, and chipsetGeneration was icelake and sapphirerapids. When I launched four x86 MicroVMs from the same image, the CPUs were also split into two types, two each.

Generation model stepping model name cache size Generation-specific instructions Count
Sapphire Rapids 143 8 Intel(R) Xeon(R) Processor 107520 KB AMX, avx_vnni, avx512_fp16 present 2
Ice Lake 106 6 Intel(R) Xeon(R) Processor @ 2.90GHz 55296 KB AMX, avx_vnni, avx512_fp16 absent 2

The model name of the MicroVM conceals the model number. So I compared it against the /proc/cpuinfo values in DevelopersIO's measurement articles on M6i and M7i.

https://dev.classmethod.jp/articles/amazon-ec2-m6i-instances/

https://dev.classmethod.jp/articles/ec2-m7i-m7iflex-tokyo/

The MicroVM with model 106 matched the M6i article in model, stepping, and cache size (55296 KB), and likewise had no AMX-family instructions. The MicroVM with model 143 matched the M7i article in model, stepping, and cache size (107520 KB). It also had AMX, avx_vnni, and avx512_fp16, as in that article.

Memory

The default baseline is 2 GB / 1 vCPU. In the MicroVMs I launched this time with the default size, both Arm and x86 showed 4 vCPUs and about 8 GB of memory. Swap (SwapTotal) was 0 on both.

Item Arm x86
MemTotal 8209056 kB 8222748 kB
os.totalmem() 8406073344 bytes 8420093952 bytes

The pricing page states that the peak is up to 4 times the baseline. The 4 vCPUs seen equal this upper limit (1 vCPU × 4), and the roughly 8 GB of memory is close to the upper limit (2 GB × 4). You are billed for the baseline portion plus the amount actually used beyond the baseline.

Pricing

The unit prices that differ between Arm and x86 are vCPU-seconds and memory GB-seconds. The unit prices for the Tokyo region are as follows.

Item Arm (USD) x86 (USD) Difference
vCPU-second 0.0000322421 0.0000371569 +15.24%
Memory GB-second 0.0000042688 0.0000049195 +15.24%

In the US East (N. Virginia) region, x86 is 17.91% more expensive for both vCPU-seconds and memory GB-seconds.

Summary

Lambda MicroVMs now supports x86 from base image version 3 onward.

If you want to use x86 binaries or libraries, or if your development environment is x86 and cross-compiling for Arm was taking a lot of build time, give the x86 image a try.

However, the execution unit price is about 15% higher than Arm in the Tokyo region and about 18% higher in N. Virginia. I also confirmed that the CPU generations allocated can be mixed.

For workloads that do not depend on the CPU architecture, I recommend sticking with the default Arm.

Share this article

AWSのお困り事はクラスメソッドへ