Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • See all Red Hat products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Red Hat OpenShift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • See all technologies
    • Programming languages & frameworks

      • Java
      • Python
      • JavaScript
    • System design & architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer experience

      • Productivity
      • Tools
      • GitOps
    • Automated data processing

      • AI/ML
      • Data science
      • Apache Kafka on Kubernetes
    • Platform engineering

      • DevOps
      • DevSecOps
      • Red Hat Ansible Automation Platform for applications and services
    • Secure development & architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & cloud native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • See all learning resources

    E-books

    • GitOps cookbook
    • Podman in action
    • Kubernetes operators
    • The path to GitOps
    • See all e-books

    Cheat sheets

    • Linux commands
    • Bash commands
    • Git
    • systemd commands
    • See all cheat sheets

    Documentation

    • Product documentation
    • API catalog
    • Legacy documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore the Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Effortless Red Hat Enterprise Linux virtual machines with Libvirt and Kickstart

March 10, 2026
Fernando Lozano
Related topics:
Virtualization
Related products:
Red Hat Enterprise Linux

    From time to time, as a developer you need to do something on a full-fledged virtual machine (VM). Maybe you need to reproduce an issue or perform integration tests in an environment configured as close as possible to the production environment, including kernel, system services, firewall settings, authentication policies, and anything not easily reproduced in a container. In this article, I demonstrate a quick and easy way to create such a VM using the standard libvirt found in any Linux distribution, and Kickstart scripts supported by Red Hat Enterprise Linux (RHEL).

    Most developers target production servers, which are very different from their work systems. Whether you run macOS, Windows, or a desktop-oriented Linux distribution (such as Fedora Linux), your target production system runs a server-oriented distribution, such as RHEL. Possibly, you must target multiple distributions, or at least multiple releases of the same distribution, across different production systems.

    Automating the provisioning of fully-configured test VMs

    Developers need a way to quickly provision and configure different VMs with settings that emulate many different production environments. You could use a number of automation technologies, such as Vagrant and Ansible, to set up a VM, but you don't need them to get started. The standard RHEL installer, named Anaconda, already provides a powerful automation feature called Kickstart.

    By using Kickstart together with features of the standard Linux virtualization stack (libvirt + Qemu + KVM) you can build a simple set of scripts, which you can store in a Git repository, to provision highly customized test VMs.

    These scripts are useful for even developers not running Linux desktops. Windows users can take advantage of those scripts on any distribution that supports Windows Subsystem for Linux (WSL2) by using nested virtualization, creating a RHEL VM inside the WSL VM. You can also use these scripts on cloud instances that support nested virtualization.

    MacOS users can take advantage of Kickstart scripts (without the libvirt scripts). Thanks to Podman Desktop, which relies on Podman Machine and macadam, you have an easy way to create test VMs preconfigured with RHEL or any other Linux distribution, and then use nested virtualization on those VMs.

    This article first describes a common network-based workflow for automatic provisioning of RHEL machines using Kickstart. Then I demonstrate how libvirt can simplify the workflow so that no network services are required, meaning you can use rootless VMs without administrator access to local machines or networks.

    From network to local automatic provisioning

    To people who haven't had the pleasure of using it, Kickstart technology from the RHEL installer can be surprisingly powerful.

    Many years ago, before the availability of cloud-based Red Hat Training and Certification virtual classrooms, I was a Red Hat Certified Instructor. All certified instructors carried a USB drive that enabled them to provision classrooms for 12 students, already customized to a specific course, in about 30 minutes. When I arrived in the classroom, I would boot the instructor machine using this USB drive, and it automatically configured the machine with DHCP, DNS, BOOTP, and HTTP services for the classroom. Then I would turn each student machine on, select network boot in BIOS, and those machines would be configured as hypervisors, running the number of VMs required by the specific course. I didn't have to wait for one student machine to finish before moving on the next one, so multiple machines were automatically configured in parallel.

    A process for automated, network-based installation

    You can add the ks argument to the kernel command line to prompt a computer to fetch a Kickstart file from a web server. The Kickstart file directs the installer to fetch RPM packages from a specified server.

    The kernel command line is entered manually, by interactive editing the GRUB menu from the standard RHEL installation media, or it can be preconfigured on the network boot server. You can use the reposync command to populate a web server with RPM packages, or just copy the files from the installation media.

    The full process, as illustrated in Figure 1, on a computer booting to a RHEL installer is:

    1. BIOS sends DHCP request for network configuration. This step requires a valid DHCP server.
    2. BIOS loads kernel and initial RAM disk (initrd) using PXE, BOOTP, and UEFI. This step requires a network boot server.
    3. BIOS invokes the kernel passing a ks argument.
    4. Kernel starts Anaconda, which gets the Kickstart script location from the kernel.
    5. Anaconda downloads the Kickstart script using HTTP. This step requires a valid web server with the Kickstart in an accessible location.
    6. Anaconda downloads RPM packages specified in the Kickstart script using HTTP. This step requires an RPM package server.
    Process for network-based automatic installation with network boot.
    Figure 1: Process for network-based automatic installation with network boot.

    It's easy to find instructions on the internet to perform similar network-based processes because they make sense for IT operations teams, who would configure all required network infrastructure only once, and then use it to provision a large number of machines. The same infrastructure could support multiple RHEL releases, and also provide RHEL updates.

    Red Hat Satellite makes this kind of infrastructure especially easy. Large IT operations teams are used to this infrastructure, but it can be intimidating for a developer.

    A process for network-based installation, without networking infrastructure

    For developers, preparing the required network infrastructure may be perceived as too much work. And besides, it wastes memory and CPU cycles on their work machines. A developer might reasonably want a non-networked process.

    It's not difficult to configure a local web server to provide the required files (the Kickstart script and RPM packages) and then use libvirt's direct kernel loading feature to skip both the virtual BIOS and the network boot server to install a VM directly from the local web server. This saves you the trouble of configuring network infrastructure, and avoids potential conflicts with DHCP services already running on your local network.

    The local-only version of the workflow, illustrated in Figure 2, looks like this:

    1. Libvirt loads the kernel and initrd from a drive on localhost.
    2. Libvirt invokes the kernel passing a ks argument, which exists on a drive on localhost.
    3. Within the RHEL virtual machine that's booted, the kernel starts Anaconda, which receives the Kickstart script location from the kernel.
    4. Anaconda downloads the Kickstart script using HTTP from a local web server running on localhost.
    5. Anaconda downloads RPM packages using HTTP from the local web server.
    Process for network-based automatic installation with direct kernel loading
    Figure 2: Process for network-based automatic installation with direct kernel loading

    If you have a good internet connection, you could stop here, and just use the Red Hat Content Delivery Network (CDN) instead of providing your own web server with RHEL packages.

    A process for local installation, without any network requirement

    The good news for developers who don't want to rely on an external network connection is that you don't really even need a web server. You can use libvirt's features to also load the Kickstart script and RPM packages from a local directory, instead. You would end up with a longer virt-install command, but you don't need to deal with a web server setup, SELinux file contexts, port redirections, firewalls, and container volumes that a web server setup could require.

    The only modification to the workflow, as illustrated in Figure 3, involves a change to the initrd and the inclusion of virtiofs to access a virtual filesystem.

    1. Libvirt loads the kernel and a modified initrd.
    2. Libvirt invokes the kernel passing a ks argument.
    3. Within the RHEL virtual machine that's booted, the kernel starts Anaconda, which receives the Kickstart script location from the kernel.
    4. Anaconda reads the Kickstart script from initrd.
    5. Anaconda reads RPM packages from a virtiofs device.
    Process for local automatic installation with direct kernel loading and virtiofs.
    Figure 3: Process for local automatic installation with direct kernel loading and virtiofs.

    Now that you understand why there are a simpler process than what you may have found elsewhere on the internet, and how they work, here are two examples of these processes you can try on your own Linux machine.

    Disconnected installation from the RHEL DVD media

    The first example performs a disconnected installation, which requires only a copy of the RHEL installation media and no internet access.

    The resulting VM can't install additional packages or updates on Day 2, unless you either register it to Red Hat with an internet connection, or configure it with access to a copy of the installation media, but this is the fastest way of provisioning a short-lived test VM.

    You can get RHEL and other Red Hat products at no cost by registering to the Red Hat Developer program and agreeing to the terms of the Red Hat Developer Subscription for Individuals. With that, you gain access to the Red Hat Developer product downloads for RHEL. You can download ISO images for the installation media for the latest stable releases of RHEL.

    Important: Do NOT click the first download links you see! Scroll down to the All Downloads heading, and download the DVD ISO image.

    All scripts from this article are available in a public Git repository, which you can clone to follow these instructions and later adapt to your needs.

    $ git clone https://github.com/flozanorht/kickstart.git

    Create a working directory and copy the contents of the dvd-iso directory.

    $ mkdir temp-rhel-vm
    $ cd temp-rhel-vm
    $ cp -r ../kickstart/dvd-iso/* .

    Now review the kickstart script dvd.ks. It starts with a very basic set of Kickstart commands, which prepare a system for an unattended installation by setting the locale, keyboard layout, automatic partitioning, and dynamic network configuration. It then sets a minimal set of packages, which includes the Apache web server.

    lang en_US.UTF-8
    keyboard us
    timezone Etc/UTC --utc
    
    zerombr
    clearpart --all --initlabel
    reqpart --add-boot
    part / --grow --fstype xfs
    
    network --bootproto=dhcp
    
    rootpw --lock
    
    cdrom
    text
    reboot
    
    %packages
    @^Minimal Install
    httpd
    %end

    The Kickstart script finishes with a %post section, which does the really interesting stuff. Some of these settings, such as creating an initial user and setting the hostname, could be performed by Kickstart commands, but I found that doing it in a %post section is more portable with image mode for RHEL.

    %post --log=/var/log/anaconda/post-install.log --erroronfail
    
    useradd -g wheel core
    echo "core:redhat123" | chpasswd
    mkdir /home/core/.ssh
    cat > /home/core/.ssh/authorized_keys << EOFSSH
    REPLACE_WITH_SSH_PUB_KEY
    EOFSSH
    
    echo "rhel-dvd" > /etc/hostname
    chmod 644 /etc/hostname
    
    systemctl enable httpd.socket
    firewall-offline-cmd --zone=public --add-service=http
    
    mkdir -p /mnt/host-files
    mount -t virtiofs -o ro host-files /mnt/host-files
    cp /mnt/host-files/index.html /var/www/html
    %end

    The commands to enable the httpd socket with systemd, and to enable the http service with firewalld, make the system ready to function as a web server immediately after installation. The final command copies HTML files from the host-files virtiofs device to the default Apache document root.

    There is a REPLACE_WITH_SSH_PUB_KEY placeholder in the example file, which you must replace with the contents of a valid public SSH key that has access to the VM, especially if you don't like the idea of having a default user with a well-known password!

    To generate an SSH key pair, and then insert the public part of it in the Kickstart file:

    $ ssh-keygen -N '' -f vm-key -C 'initial key for test VMs'
    $ SSH_PUB_KEY=$( cat vm-key.pub )
    $ sed -i "s|REPLACE_WITH_SSH_PUB_KEY|$SSH_PUB_KEY|" dvd.ks

    Now review the virt-install.sh script, which creates the test VM. It's just a long virt-install command, which starts by setting the VM size and user mode networking, with a couple of forwarded ports.

    #!/bin/bash
    
    virt-install --name rhel-dvd --os-variant rhel9.5 \
    --vcpus 2 --ram 4096 --disk size=20 \
    --network passt,portForward0=8022:22,portForward1=8080:80 \
    --location ~/Downloads/rhel-9.6-x86_64-dvd.iso \
    --initrd-inject ./dvd.ks \
    --memorybacking source.type=memfd,access.mode=shared \
    --filesystem $PWD/html,host-files,driver.type=virtiofs \
    --graphics none \
    --extra-arg console=ttyS0 \
    --extra-args inst.ks=file:/dvd.ks

    The --location option specifies direct kernel loading, which bypasses the virtual BIOS and bootloader and enables the virt-install command to control the kernel command line. It also enables the script to use the --initrd-inject option to dynamically modify the initrd to insert the Kickstart script so it's available to Anaconda before it configures networking or additional storage devices.

    The --memorybacking and --filesystem directives create a virtual device (using the virtiofs driver) to make a host directory available to the VM. Notice the name of the virtual device is set to host-files, which is referred to in the Kickstart script.

    Finally, the --graphics and --extra-arg options configure a real text mode console (instead of a virtual VGA graphics card) using a virtual serial port, which enables you to capture all kernel boot messages, Anaconda messages, and systemd messages by redirecting standard output to a file. This is especially useful for troubleshooting, should something go wrong while installing your VM. It also enables you to create your VMs over SSH on a remote system.

    Run the script and wait a few moments while your terminal is flooded with kernel and installation messages:

    $ bash virt-install.sh

    The end result is a RHEL login prompt. Log in with the user name core and the password redhat123. Or press Ctrl+] to return to your host shell.

    You can use the first forwarded port to open a SSH session to the VM:

    $ ssh -i vm-key -p 8022 core@127.0.0.1

    You can verify that the VM is not registered with Red Hat, which prevents it from downloading additional packages or updates:

    $ sudo subscription-manager status
    +-------------------------------------------+
       System Status Details
    +-------------------------------------------+
    Overall Status: Unknown
    
    System Purpose Status: Unknown
    
    $ sudo dnf search podman
    Updating Subscription Management repositories.
    Unable to read consumer identity
    
    This system is not registered with an entitlement server. You can use "rhc" or "subscription-manager" to register.
    
    No matches found.

    You can also use the second forwarded port to access the web server running inside the VM:

    $ curl 127.0.0.1:8080
    RHEL VM installed offline

    You can use your virtiofs volume to provide any kind of files to the VM, including additional RPM packages, container image layers, or SQL scripts.

    Connected installation from the RHEL boot media

    This second example uses a network-based installation, which can use the smaller Boot ISO instead of the full DVD ISO. This process takes longer to install because of the time spent downloading RPM packages from Red Hat over the internet, but it creates a system using the latest packages, instead of whatever versions are in the latest DVD ISO.

    This process also creates a VM that's subscribed to Red Hat, so it can easily download additional packages and updates on Day 2. It's a better method for provisioning long-lived VMs.

    To use this method, you need an activation key, which is used to authenticate to the Red Hat Customer Portal and register your VM. It works with your free Developer subscription.

    On the Downloads page from Red Hat Developer (or any other page on the site, once you're authenticated), click the user icon in the top right corner of your web browser window. This displays your user name and email, and a series of links to configure your user, community, and certification profiles. Click Subscriptions to leave the Red Hat Developer site and enter the Red Hat Subscription Management page of the Red Hat Customer Portal.

    Look for the Systems panel and click Activation keys. This opens the Activation Keys page of the Red Hat Hybrid Cloud Console, and probably shows no activation keys for your account. Take note of the Organization ID displayed just below the Activation Keys heading, because you'll need that value later.

    Click the Create activation key button to start the assistant. On the first page, you can leave the autogenerated name as is, and optionally provide a description. Click Next three times, until you reach the Review tab, and then click Create. Then dismiss the information pop-up, which states the activation key was created.

    Use the values from the Red Hat Hybrid Cloud Console to set two shell variables on your developer machine (the values shown here are examples only):

    $ RHSM_ORG=1234567
    $ RHSM_KEY=12345678-90ab-cdef-1234-567890abcdef

    You can keep using the same work directory you created for the previous example, and just copy the new scripts over it. You can reuse the same SSH key pair that you already generated.

    $ cp -r ../kickstart/boot-iso/* .

    Review the boot.ks Kickstart script. It contains the same commands as the previous script, except that the cdrom command is replaced by an rhsm command, which uses your activation key to authenticate to Red Hat and download RPM packages:

    rhsm --organization REPLACE_WITH_ORG_ID --activation-key REPLACE_WITH_ACTIVATION_KEY

    Alter the working copy of the Kickstart script to include the values of the shell variables you set after creating your activation key:

    $ sed -i "s/REPLACE_WITH_ORG_ID/$RHSM_ORG/" boot.ks
    $ sed -i "s/REPLACE_WITH_ACTIVATION_KEY/$RHSM_KEY/" boot.ks

    The new virt-install.sh script is also very similar to the script in the previous example. It changes the name of the VM and references the rhel*boot.iso instead of the rhel*dvd.iso installation image. It also forwards different local ports to the SSH and HTTP ports of the VM, so you can run both VMs in parallel.

    Run the installation script:

    $ bash virt-install.sh

    Log in using the user name core and the password redhat123, as you did with the previous example VM. Alternately, detach from the VM console by pressing Ctrl+].

    You can verify that the VM got a different host name, making it easy to differentiate between shells on each one, and that its web server contains a different HTML file. If you're running this from the host and not the VM, add port 8180 to the curl command:

    $ curl 127.0.0.1
    RHEL VM installed and registered

    You can also verify that this VM is able to connect to Red Hat to retrieve additional packages and updates:

    $ sudo subscription-manager status
    +-------------------------------------------+
       System Status Details
    +-------------------------------------------+
    Overall Status: Disabled
    Content Access Mode is set to Simple Content Access. This host has access to content, regardless of subscription status.
    
    System Purpose Status: Disabled
    
    $ sudo dnf search podman
    Updating Subscription Management repositories.
    Red Hat Enterprise Linux 9 for x86_64 - BaseOS   14 MB/s |  95 MB     00:06    
    Red Hat Enterprise Linux 9 for x86_64 - AppStre  22 MB/s |  79 MB     00:03    
    Last metadata expiration check: 0:00:07 ago on Mon 29 Dec 2025 08:49:17 PM UTC.
    ========================= Name Exactly Matched: podman =========================
    podman.x86_64 : Manage Pods, Containers and Container Images
    [...]

    Now that you have your local VMs ready, you can manage them using the Cockpit web interface, the virsh command, the virt-manager or Boxes GNOME applications, and other tools from the libvirt suite. Or just destroy them, knowing that you can quickly recreate them, when you need them.

    Moving forward

    The two examples I've provided in this article install RHEL using package mode, but they can be adapted to install RHEL using image mode. You can see an example of using virtiofs in a %pre section of a Kickstart script to make a bootc container image available to Anaconda, without requiring that the image is pushed to a container registry. Or you could use direct kernel loading to give Anaconda a Kickstart file that refers to a bootc container image in a container registry.

    The examples here are intentionally simple, but you don't need to embed all your customization scripts on your Kickstart files. They can just invoke external scripts, which are made available to Anaconda using virtiofs. Keep the scripts in a Git repository to make them easy to test and troubleshoot in isolation. Of course, you can also use Red Hat Ansible Automation Platform, with or without RHEL system roles, to interact with or to manage your VMs.

    The examples here install a VM running RHEL 9.6, but they also work with older and newer releases of RHEL. You don't need to declare the exact RHEL release with the --os-variant option of the virt-install command.

    You can find reference documentation about Kickstart scripts on the RHEL product docs and on the upstream community docs. Other Linux distributions, such as Fedora Linux and CentOS Stream, also use the Anaconda installation program and support Kickstart scripts.

    I know some developers use scripts similar to the examples here in their CI/CD workflows, because creating a nested VM in a cloud instance (not necessarily a bare metal instance) is faster and easier than dealing with cloud provider proprietary APIs (and sometimes also cheaper). This gives them the advantage of using the same scripts for local development.

    Special thanks to Clemens Lang, Marc-André Lureau, Peter Larsen, and Sergio Correia for reviewing drafts of this article.

    Related Posts

    • Automating the testing process for SystemTap, Part 1: Test automation with libvirt and Buildbot

    • How to use RHEL 10 as a WSL Podman machine

    • Getting started with RHEL on WSL

    • How to use RHEL as a WSL Podman machine

    Recent Posts

    • Effortless Red Hat Enterprise Linux virtual machines with Libvirt and Kickstart

    • 5 steps to triage vLLM performance

    • Automate AI agents with the Responses API in Llama Stack

    • Smarter multi-cluster scheduling with dynamic scoring framework

    • What's new in network observability 1.11

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue