Containers

Install and Run Fedora CoreOS on KVM / OpenStack

Fedora CoreOS (FCOS) is a minimal, auto-updating, container-focused operating system built on the foundations of Fedora Linux and the original CoreOS Container Linux. It is designed to run containerized workloads at scale with a focus on security, reliability, and hands-off management. Unlike traditional Linux distributions, FCOS uses an immutable root filesystem and is configured declaratively through Ignition configs at first boot rather than through manual package installation.

Original content from computingforgeeks.com - post 44789

What Makes Fedora CoreOS Different

  • Immutable filesystem – The root filesystem is read-only. System updates are applied as atomic transactions via rpm-ostree, which means you can roll back to a previous version if something breaks.
  • Automatic updates – FCOS uses Zincati to check for and apply updates from the Fedora CoreOS update stream. Updates reboot the system automatically during a configured maintenance window.
  • Ignition provisioning – All system configuration (users, SSH keys, systemd units, files, network settings) is defined in an Ignition config that runs exactly once at first boot.
  • Butane for human-friendly configs – Ignition configs are JSON, which is hard to write by hand. Butane is a tool that converts a readable YAML format into Ignition JSON.
  • Container-native – Podman and systemd are the primary tools for running containers. Docker is not included, but Podman provides a compatible CLI.

Prerequisites

  • A Linux workstation with KVM/QEMU or access to an OpenStack cloud
  • The butane tool installed on your workstation
  • The virt-install command (from the libvirt/virt-manager package) for KVM deployments
  • At least 2 GB RAM and 10 GB disk for the FCOS virtual machine

Step 1 – Install Butane

Butane converts human-readable YAML configs into Ignition JSON. Install it on your workstation.

On Fedora:

sudo dnf install butane -y

On Ubuntu/Debian, download the binary from GitHub:

BUTANE_VER=$(curl -s https://api.github.com/repos/coreos/butane/releases/latest | grep tag_name | cut -d '"' -f4)
curl -Lo /usr/local/bin/butane https://github.com/coreos/butane/releases/download/${BUTANE_VER}/butane-x86_64-unknown-linux-gnu
chmod +x /usr/local/bin/butane

Verify the installation:

butane --version

Step 2 – Write a Butane Configuration

Create a Butane YAML file that defines your FCOS instance. This example sets up an SSH key for the core user (the default user on FCOS), configures the hostname, and sets the timezone:

cat <<'EOF' > fcos-config.bu
variant: fcos
version: 1.5.0
passwd:
  users:
    - name: core
      ssh_authorized_keys:
        - ssh-ed25519 AAAA...your-public-key-here...
storage:
  files:
    - path: /etc/hostname
      mode: 0644
      contents:
        inline: fcos-node01
    - path: /etc/zoneinfo
      mode: 0644
      contents:
        inline: UTC
systemd:
  units:
    - name: [email protected]
      enabled: true
EOF

Replace the SSH key with your actual public key. You can add more configuration – additional users, static network configuration, systemd units for container workloads, and filesystem mounts.

Example – Run a Container at Boot

Add a systemd unit that starts an Nginx container automatically:

cat <<'EOF' > fcos-config.bu
variant: fcos
version: 1.5.0
passwd:
  users:
    - name: core
      ssh_authorized_keys:
        - ssh-ed25519 AAAA...your-public-key-here...
storage:
  files:
    - path: /etc/hostname
      mode: 0644
      contents:
        inline: fcos-node01
systemd:
  units:
    - name: nginx.service
      enabled: true
      contents: |
        [Unit]
        Description=Nginx Container
        After=network-online.target
        Wants=network-online.target

        [Service]
        ExecStartPre=-/usr/bin/podman rm -f nginx
        ExecStart=/usr/bin/podman run --name nginx -p 80:80 docker.io/library/nginx:stable
        ExecStop=/usr/bin/podman stop nginx
        Restart=always
        RestartSec=10

        [Install]
        WantedBy=multi-user.target
EOF

Step 3 – Convert Butane to Ignition

Convert the Butane YAML to Ignition JSON:

butane --pretty --strict fcos-config.bu > fcos-config.ign

The --strict flag causes butane to error on any warnings, catching config mistakes early. Verify the output is valid JSON:

cat fcos-config.ign | python3 -m json.tool > /dev/null && echo "Valid JSON"

Step 4 – Download the Fedora CoreOS Image

Download the QCOW2 image for KVM or the OpenStack image from the official Fedora CoreOS download page:

https://fedoraproject.org/coreos/download/

For KVM, download the QEMU QCOW2 image. You can use the coreos-installer tool or download directly:

STREAM=stable
ARCH=x86_64
coreos-installer download -s ${STREAM} -p qemu -a ${ARCH} -f qcow2.xz --decompress -C /var/lib/libvirt/images/

If you do not have coreos-installer, install it:

sudo dnf install coreos-installer -y

On Ubuntu, use cargo or download the binary:

sudo apt install cargo -y
cargo install coreos-installer

Alternatively, download the image manually from the download page and place it in /var/lib/libvirt/images/.

Verify the image is present:

ls -lh /var/lib/libvirt/images/fedora-coreos-*.qcow2

Step 5 – Launch FCOS on KVM with virt-install

Use virt-install to create a virtual machine that boots from the FCOS image and consumes the Ignition config. Set the FCOS image filename to match what you downloaded:

FCOS_IMAGE=/var/lib/libvirt/images/fedora-coreos-41.20250216.3.0-qemu.x86_64.qcow2
IGNITION_CONFIG=/path/to/fcos-config.ign

sudo virt-install \
  --name fcos-node01 \
  --vcpus 2 \
  --memory 2048 \
  --disk path=${FCOS_IMAGE},format=qcow2,size=20 \
  --os-variant fedora-coreos-stable \
  --network network=default \
  --import \
  --graphics none \
  --noautoconsole \
  --qemu-commandline="-fw_cfg name=opt/com.coreos/config,file=${IGNITION_CONFIG}"

The key parameter is --qemu-commandline, which passes the Ignition config to FCOS through QEMU’s firmware configuration mechanism. FCOS reads this config on first boot and applies all settings.

Check that the VM is running:

sudo virsh list --all

Get the VM’s IP address:

sudo virsh domifaddr fcos-node01

SSH into the instance:

ssh core@<vm-ip-address>

Verify the system is running FCOS:

cat /etc/os-release
rpm-ostree status

Launch FCOS on OpenStack

OpenStack supports Ignition configs through its user-data mechanism. Upload the FCOS image to Glance first:

openstack image create \
  --disk-format qcow2 \
  --container-format bare \
  --file fedora-coreos-41.20250216.3.0-openstack.x86_64.qcow2 \
  --property os_distro=fedora-coreos \
  fedora-coreos-41

Verify the image uploaded:

openstack image list | grep fedora-coreos

Launch an instance with the Ignition config passed as user-data. OpenStack’s config drive is required for Ignition to read the config:

openstack server create \
  --image fedora-coreos-41 \
  --flavor m1.medium \
  --network private-net \
  --security-group default \
  --key-name my-keypair \
  --user-data fcos-config.ign \
  --config-drive true \
  --property os_distro=fedora-coreos \
  fcos-node01

Check the instance status:

openstack server show fcos-node01 -f value -c status

Once active, get the IP and SSH in:

openstack server list
ssh core@<floating-ip>

Managing FCOS After Deployment

Check Update Status

FCOS updates itself automatically. Check the current deployment and pending updates:

rpm-ostree status
sudo systemctl status zincati

Install Additional Packages

Since the filesystem is immutable, you cannot use dnf directly. Use rpm-ostree to layer packages:

sudo rpm-ostree install htop vim tmux
sudo systemctl reboot

Layered packages persist across updates. A reboot is required after installing packages.

Roll Back an Update

If an update causes problems, roll back to the previous deployment:

sudo rpm-ostree rollback
sudo systemctl reboot

Run Containers with Podman

Podman is the container runtime on FCOS. It works like Docker but does not need a daemon:

podman run -d --name webserver -p 8080:80 docker.io/library/nginx:stable
podman ps
curl http://localhost:8080

Troubleshooting

Cannot SSH into the instance – Verify your SSH public key in the Butane config matches your private key. Check that the Ignition config was passed correctly by viewing the VM console: sudo virsh console fcos-node01.

Ignition config not applied – Ignition runs only on the first boot. If you need to change the config, you must reprovision the instance (destroy and recreate it with the new config).

“os-variant not found” error with virt-install – Update your osinfo database: sudo dnf install osinfo-db -y. If fedora-coreos-stable is still not recognized, use --os-variant fedora-unknown as a fallback.

VM boots but Ignition reports errors – Check the Ignition log from the console output or via journal: journalctl -t ignition. Common issues include invalid JSON (run butane with --strict) and incorrect file paths in the config.

Wrapping Up

Fedora CoreOS is purpose-built for running containers with minimal operational overhead. The Butane/Ignition workflow replaces traditional configuration management with a declarative first-boot provisioning model. On KVM, virt-install with the QEMU firmware config flag makes it straightforward to pass Ignition configs to the VM. On OpenStack, the same config goes through user-data with a config drive. Once deployed, FCOS handles its own updates, and Podman provides a Docker-compatible container runtime without the baggage of a daemon process.

Related Articles

Containers Install Google Cloud SDK on Linux Mint 22 | Ubuntu 24.04 KVM How to create a Linux VM on KVM using Virt-Manager KVM Mount VM virtual disk on KVM hypervisor with Libguestfs Virtualization Install VMWare Workstation 17.x Pro on CentOS 8

Leave a Comment

Press ESC to close