# Testing Mender with QEMU ARM64

**URL:** <https://hub.mender.io/t/testing-mender-with-qemu-arm64/8095>\
**Category:** Yocto Project\
**Created:** [November 14, 2025, 1:55pm UTC](https://hub.mender.io/t/testing-mender-with-qemu-arm64/8095 "2025-11-14T13:55:44Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![TheYoctoJester](https://yyz2.discourse-cdn.com/flex036/user_avatar/hub.mender.io/theyoctojester/32/1444_2.png) [@TheYoctoJester](https://hub.mender.io/u/TheYoctoJester)\
**Post date:** [November 14, 2025, 1:55pm UTC](https://hub.mender.io/t/testing-mender-with-qemu-arm64/8095/1 "2025-11-14T13:55:44Z")

</div>

Iterating on Mender integration with physical ARM hardware means dealing with SD cards, serial cables, and recovery mechanisms when something goes wrong. QEMU provides a faster development cycle by running your Mender-enabled images in a virtual environment, letting you test update mechanisms, rollback behavior, and partition layouts without touching hardware.

This tutorial walks you through building and running a Mender-enabled ARM64 QEMU image using `kas` and the pre-configured setup from meta-mender-community. You’ll use the `scarthgap` branch and boot with modern UEFI firmware, matching the approach used in contemporary embedded systems.

 ![LF_17_02_Yocto-Badge-Update_Participant_Final](https://canada1.discourse-cdn.com/flex036/uploads/mender/original/1X/758c60e0e8309c4dc2519392b78668e7396c15bd.png)

## Version notes

The tutorial has been verified on Ubuntu 22.04 as of 2025-11-14.

| Yocto Project | Tutorial applies | Maintenance |
| --- | --- | --- |
| scarthgap (5.0) | :test_works: | :test_works: LTS |

This tutorial specifically targets the `scarthgap` branch of meta-mender-community. While the `kas`-based approach may work with other Yocto releases if corresponding configurations exist in meta-mender-community, only `scarthgap` has been verified for this tutorial.

## Prerequisites

You’ll need:

- **`kas`** installed: `pip install kas`
- **`git`** for cloning repositories
- **A properly configured Yocto build host** - If you haven’t set up a Yocto build environment before, follow either:
  - [Preparing the Yocto Project environment from scratch](https://hub.mender.io/t/preparing-the-yocto-project-environment-from-scratch/801) on Mender Hub, or
  - [Yocto Project Quick Build](https://docs.yoctoproject.org/brief-yoctoprojectqs/index.html) from the official Yocto Project documentation

If you’re new to `kas`, take a look at the [kas tutorial](https://hub.mender.io/t/building-a-mender-yocto-project-image-using-kas/5383) first. This tutorial assumes you understand `kas` basics and focuses on the QEMU-specific configuration.

## Build and Run

The meta-mender-community repository provides a ready-to-use `kas` configuration for ARM64 QEMU. Let’s get you to a running system:

First, clone the repository and enter the `kas shell`:

```bash
git clone -b scarthgap https://github.com/mendersoftware/meta-mender-community.git
cd meta-mender-community
kas shell kas/qemuarm64.yml

```

The `kas shell` command initializes the Yocto/OpenEmbedded build environment, which is necessary for both building and running QEMU. You’re now in a shell where the build configuration and environment variables are properly set up.

From within this `kas shell`, build the image:

```bash
bitbake core-image-minimal

```

After `kas shell` has carried out the setup in a reproducible manner, handling layer fetching, patch application, configuration, this actually runs the build. The build process takes time on the first run as it downloads sources and compiles from scratch. Grab a coffee.

Once the build completes, launch QEMU:

```bash
runqemu

```

You can also run QEMU without a graphical window using the `nographic` flag:

```bash
runqemu nographic

```

The `runqemu` script reads the `qemuboot.conf` generated during build and launches QEMU with the right parameters. You’ll see the EDK2 firmware initialize, `grub` load, and the kernel boot.

**Note on login:** By default, this image does not root login without a password, which is a “secure by default” behaviour.

To exit QEMU, use `Ctrl-A` then `X`.

For development purposes, and **not suitable for production** , the Yocto Project provides `IMAGE_FEATURES`/`EXTRA_IMAGE_FEATURES` to control this behavior. In the context of this build, add the following to the `conf/local.conf` file in your build directory:

```ini
EXTRA_IMAGE_FEATURES += "empty-root-password"

```

After a rebuild with `bitbake core-image-minimal", you can log in as `root` (no password by default).

For production images, you should either set a root password using the `extrausers` class or - preferably - disable root login entirely. See the “Security Considerations” section below for more details.

**Performance note:** You’re running an ARM64 guest on an x86-64 host without KVM acceleration. Boot and runtime will be noticeably slower than native execution or x86-on-x86 QEMU with KVM. This is the tradeoff for testing on an architecture that’s more representative of embedded ARM targets. The slower execution is rarely a blocker for testing Mender update mechanisms.

To exit the `kas shell` afterwards, type `exit` or press `Ctrl-D`.

## On just building

If you only want to build the image without immediately running it, you can use `kas build` as a shortcut. This command combines the environment setup and build process in a single step:

```bash
kas build kas/qemuarm64.yml

```

This is equivalent to entering the `kas shell` and running `bitbake core-image-minimal`, but handles everything automatically. Use this approach when you’re building images for deployment or when you want to run QEMU separately later. You’ll still need to enter the `kas shell` afterwards to use `runqemu`.

## Understanding the Configuration

Now that you have a working system, let’s look at what the `kas/qemuarm64.yml` file configured:

### UEFI Boot Setup

The configuration enables UEFI boot through several settings:

```auto
EFI_PROVIDER = "grub-efi"
MACHINE_FEATURES += "efi"
MENDER_EFI_LOADER = "edk2-firmware"

```

This means your image boots through EDK2 UEFI firmware (the same open-source UEFI implementation used by many ARM boards), which then loads `grub` from the EFI partition, which finally boots the kernel. This matches modern embedded systems far better than legacy boot methods.

### QEMU Runtime Configuration

Key QEMU settings include:

```auto
QB_MACHINE = "-machine virt,secure=on"
QB_MEM = "-m 2048"
QB_DEFAULT_FSTYPE = "uefiimg"
QB_DEFAULT_BIOS = "QEMU_EFI.fd"
QB_DEFAULT_KERNEL = "none"

```

The critical setting is `QB_DEFAULT_KERNEL = "none"`. Traditionally, `runqemu` loads the kernel separately and passes it to QEMU with the `-kernel` option. With UEFI boot, the kernel lives inside the disk image and gets loaded by `grub`. Setting this to “none” tells `runqemu` to skip separate kernel loading.

This required a small patch to openembedded-core’s `qemuboot.bbclass`, which `kas` applies automatically. The patch simply teaches `qemuboot` to handle `QB_DEFAULT_KERNEL = "none"` without trying to resolve it as a file path. More details in Appendix B if you’re curious about the technical implementation.

## Security Considerations

The default configuration disables passwordless root access for development convenience, in accordance with the “secure defaults” mindset. This can be changed through Yocto’s `IMAGE_FEATURES`/`EXTRA_IMAGE_FEATURES` variable.

### Understanding IMAGE\_FEATURES for Root Access

Yocto provides several image features that control root login behavior:

- **`empty-root-password`** : Allows root login with an empty password. This doesn’t set the password itself; rather, it disables the mechanism that forces a non-empty root password.
- **`allow-empty-password`** : Allows SSH servers (Dropbear and OpenSSH) to accept logins from accounts with empty passwords.
- **`allow-root-login`** : Allows SSH servers to accept root logins.

**Important:** Prior to Yocto 5.2 (`walnascar`), these features were bundled in a `debug-tweaks` feature. Starting with Yocto 5.2, `debug-tweaks` was removed, and you must specify individual features explicitly. Since this tutorial targets `scarthgap` (5.0), `debug-tweaks` may still be available in some configurations, but using the explicit features is recommended for forward compatibility.

### For Production Images

When building images for production deployment, you should configure security settings appropriately. The following examples show `kas` configuration overrides - YAML snippets that extend the base `kas/qemuarm64.yml` configuration. For more details on how `kas` configuration layering works, see the [kas tutorial](https://hub.mender.io/t/building-a-mender-yocto-project-image-using-kas/5383) mentioned in the prerequisites.

You can apply these overrides by:

- Creating a custom `kas` YAML file that includes the base configuration and adds your overrides, or
- Adding the settings directly to your `kas/qemuarm64.yml` file (though this modifies the upstream configuration)

1. **Set a root password** using the `extrausers` class:

```yaml
local_conf_header:
  extrausers: |
    INHERIT += "extrausers"
    EXTRA_USERS_PARAMS = "usermod -P 'your-hashed-password' root;"

```

Generate a hashed password with: `openssl passwd -6 "your-password"`

1. **Remove development features** from `IMAGE_FEATURES` to ensure no passwordless access is enabled.

For this tutorial’s development and testing purposes, the default passwordless root access simplifies the workflow.

## Next Steps

Now that you have a working QEMU environment:

- Test Mender artifact installation with `mender install`
- Experiment with the dual partition layout (`/dev/vda2` and `/dev/vda3`)
- Trigger updates and verify rollback behavior
- Modify the `kas` configuration to include your own layers

If you want to connect your QEMU instance to a Mender server for full OTA testing, see Appendix A.

That’s it! You now have a reproducible ARM64 QEMU environment for Mender development.

* * *

## Appendix A: Mender Server Integration (Optional)

To test OTA updates from a Mender server, you’ll need to configure server settings before building.

Create a `kas` overlay file `kas/qemuarm64-server.yml`:

```yaml
header:
  version: 14
  includes:
  - qemuarm64.yml

local_conf_header:
  mender-server: |
    MENDER_SERVER_URL = "https://hosted.mender.io"
    MENDER_TENANT_TOKEN = "your-tenant-token-here"

```

Replace `your-tenant-token-here` with your actual tenant token from [hosted.mender.io](http://hosted.mender.io) or your self-hosted server.

Build with the overlay:

```bash
kas build kas/qemuarm64-server.yml

```

After booting, the Mender client will connect to your server. Authorize the device in the Mender UI, then you can deploy updates through the standard Mender workflow.

**Security note:** Don’t commit files containing real tenant tokens to public repositories. For production workflows, consider using environment variable substitution or `kas`’s secret management features.

## Appendix B: The qemuboot Patch Explained

The `kas` configuration applies a patch to openembedded-core that enables `QB_DEFAULT_KERNEL = "none"`. Here’s why it’s needed:

The `qemuboot.bbclass` generates a `qemuboot.conf` file that `runqemu` reads to determine how to launch QEMU. For the kernel setting, it traditionally does:

```python
kernel_link = os.path.join(d.getVar('DEPLOY_DIR_IMAGE'), d.getVar('QB_DEFAULT_KERNEL'))
kernel = os.path.realpath(kernel_link)

```

This works fine when `QB_DEFAULT_KERNEL` points to a kernel file like “bzImage” or “zImage”. But when set to “none” (signaling UEFI boot where the kernel is in the disk image), this code tries to resolve a path to a file literally named “none”, which fails.

The patch adds a simple check:

```python
qb_default_kernel = d.getVar('QB_DEFAULT_KERNEL')
if qb_default_kernel == "none":
  kernel = "none"
else:
  kernel_link = os.path.join(d.getVar('DEPLOY_DIR_IMAGE'), qb_default_kernel)
  kernel = os.path.realpath(kernel_link)

```

Now “none” passes through cleanly, `runqemu` sees it, and knows to skip the `-kernel` option to QEMU, allowing the UEFI firmware and `grub` to handle kernel loading instead.

The patch is located at `patches/openembedded-core/0001-qemuboot-handle-QB_DEFAULT_KERNEL-none-properly.patch` in meta-mender-community.
