Skip to content

First Boot on KVM

Garden Linux is a minimal, security-hardened Linux distribution designed for cloud and container environments. This tutorial guides you through deploying your first Garden Linux instance using QEMU with KVM acceleration, from downloading a disk image to connecting via SSH.

Difficulty: Beginner | Time: ~10 minutes

Learning Objective: By the end of this tutorial, you'll have a running Garden Linux virtual machine and understand the basic deployment process using plain QEMU commands.

Prerequisites

Before starting, you'll need:

  • A Linux host with KVM support (/dev/kvm must exist and be accessible)
  • QEMU installed (qemu-system-x86_64 for amd64, qemu-system-aarch64 for arm64)
  • OVMF firmware installed (UEFI boot support)
  • An SSH client on your local machine
  • sudo or appropriate permissions to access /dev/kvm

TIP

Install QEMU and OVMF on Debian/Ubuntu-based systems:

bash
# For amd64 hosts
sudo apt install qemu-system-x86 ovmf

# For arm64 hosts
sudo apt install qemu-system-arm qemu-efi-aarch64

What You'll Build

You'll launch a Garden Linux virtual machine using plain QEMU commands with environment variables for configuration. The tutorial covers downloading a disk image for your architecture (amd64 or arm64), creating a configuration script for first-boot setup (including SSH access), and verifying the installation.

Steps

Step 1: Choose an Image

Garden Linux provides pre-built disk images for KVM. Start by selecting an appropriate image for your deployment.

Official Images

Choose a release from the GitHub Releases page. For this tutorial, we'll use release 2150.0.0.

In the Assets section at the bottom of the release page, find the kvm-gardener_prod archive matching your workstation architecture. Garden Linux provides images for both amd64 and arm64.

Detect your workstation architecture and download the correct image:

bash
GL_VERSION="2150.0.0"
GL_BUILD_ID="eb8696b9"

# Auto-detect host architecture
GL_ARCH="$(uname -m)"
case "$GL_ARCH" in
  x86_64)  GL_ARCH="amd64" ;;
  aarch64) GL_ARCH="arm64" ;;
  *)       echo "Unsupported architecture: $GL_ARCH"; exit 1 ;;
esac

GL_IMAGE="gardenlinux-${GL_VERSION}.raw"
GL_ASSET="kvm-gardener_prod-${GL_ARCH}-${GL_VERSION}-${GL_BUILD_ID}"

curl -L -o "${GL_ASSET}.tar.xz" \
  "https://github.com/gardenlinux/gardenlinux/releases/download/${GL_VERSION}/${GL_ASSET}.tar.xz"

tar -xf "${GL_ASSET}.tar.xz"
mv "${GL_ASSET}/${GL_ASSET}.raw" "$GL_IMAGE"
rm -rf "${GL_ASSET}" "${GL_ASSET}.tar.xz"

TIP

To use a specific architecture instead of auto-detecting, set GL_ARCH before running the download commands:

bash
export GL_ARCH="arm64"

TIP

For a complete list of maintained releases and their support lifecycle, see the releases reference.

Build Your Own Images

To create custom Garden Linux images with additional features or configurations, see the Building Flavors guide.

Step 2: Verify KVM Support

Ensure your system supports KVM hardware acceleration for optimal performance:

bash
# Check if /dev/kvm exists and is accessible
if [ -w /dev/kvm ]; then
    echo "KVM acceleration is available"
else
    echo "KVM acceleration is not available or /dev/kvm is not accessible"
    echo "Falling back to TCG emulation (significantly slower)"
fi

WARNING

Without KVM acceleration, QEMU will use TCG software emulation, which is substantially slower. Ensure your BIOS/UEFI has virtualization enabled (Intel VT-x or AMD-V) and that your user has access to /dev/kvm.

Step 3: Configure SSH Access

Garden Linux disables SSH by default for security. You must explicitly enable it and create a user on first boot using a firmware configuration script.

bash
# Generate an SSH key pair
SSH_KEY_DIR="${HOME}/.ssh/gardenlinux-kvm"
mkdir -p "$SSH_KEY_DIR"
ssh-keygen -t ed25519 -f "$SSH_KEY_DIR/id_ed25519" -N ""

# Read the public key
SSH_PUBLIC_KEY=$(cat "$SSH_KEY_DIR/id_ed25519.pub")

# Define the SSH user
SSH_USER="gardenlinux"

# Create the fw_cfg configuration script
FW_CFG_SCRIPT="fw_cfg-script.sh"

# Determine console device based on architecture
GL_ARCH_TMP="${GL_ARCH:-$(uname -m)}"
case "$GL_ARCH_TMP" in
  x86_64|amd64)  CONSOLE="/dev/ttyS0" ;;
  aarch64|arm64) CONSOLE="/dev/ttyAMA0" ;;
  *)             CONSOLE="/dev/ttyS0" ;;
esac

cat >"$FW_CFG_SCRIPT" <<EOF
#!/usr/bin/env bash
set -eufo pipefail

# Send all output to the serial console
exec >${CONSOLE}
exec 2>&1

# Enable SSH service
systemctl enable --now ssh

# Create the user and configure SSH access
useradd -U -m -G wheel -s /bin/bash ${SSH_USER}
mkdir -p /home/${SSH_USER}/.ssh
chmod 700 /home/${SSH_USER}/.ssh
echo "${SSH_PUBLIC_KEY}" >> /home/${SSH_USER}/.ssh/authorized_keys
chown -R ${SSH_USER}:${SSH_USER} /home/${SSH_USER}/.ssh
chmod 600 /home/${SSH_USER}/.ssh/authorized_keys
EOF

TIP

The fw_cfg script mechanism passes a shell script to the VM via QEMU's firmware configuration interface. Garden Linux executes scripts found at opt/gardenlinux/config_script during early boot. The script output is redirected to the serial console (/dev/ttyS0 on amd64, /dev/ttyAMA0 on arm64) for debugging.

Step 4: Launch the VM

Set environment variables and launch the virtual machine. The command adapts automatically based on your host architecture:

bash
# Configuration variables
GL_ARCH="${GL_ARCH:-$(uname -m)}"
case "$GL_ARCH" in
  x86_64)  GL_ARCH="amd64" ;;
  aarch64) GL_ARCH="arm64" ;;
esac
GL_CPU="${GL_CPU:-2}"
GL_MEM="${GL_MEM:-2048}"
GL_SSH_PORT="${GL_SSH_PORT:-2222}"
GL_IMAGE="${GL_IMAGE:-gardenlinux-2150.0.0.raw}"

if [ "$GL_ARCH" = "amd64" ]; then
  QEMU_BIN="${QEMU_BIN:-qemu-system-x86_64}"
  QEMU_MACHINE="q35"
  QEMU_CPU="${QEMU_CPU:-host}"
  UEFI_CODE="${UEFI_CODE:-/usr/share/OVMF/OVMF_CODE.fd}"
  UEFI_VARS="${UEFI_VARS:-/usr/share/OVMF/OVMF_VARS.fd}"
elif [ "$GL_ARCH" = "arm64" ]; then
  QEMU_BIN="${QEMU_BIN:-qemu-system-aarch64}"
  QEMU_MACHINE="virt"
  QEMU_CPU="${QEMU_CPU:-max}"
  UEFI_CODE="${UEFI_CODE:-/usr/share/AAVMF/AAVMF_CODE.fd}"
  UEFI_VARS="${UEFI_VARS:-/usr/share/AAVMF/AAVMF_VARS.fd}"
fi

# Create a writable copy of UEFI vars
UEFI_VARS_TMP="ovmf-vars.fd"
cp "$UEFI_VARS" "$UEFI_VARS_TMP"

# Launch QEMU
$QEMU_BIN \
    -machine "$QEMU_MACHINE" \
    -cpu "$QEMU_CPU" \
    -enable-kvm \
    -smp "$GL_CPU" \
    -m "$GL_MEM" \
    -drive if=virtio,format=raw,file="$GL_IMAGE" \
    -drive if=pflash,format=raw,unit=0,file="$UEFI_CODE",readonly=on \
    -drive if=pflash,format=raw,unit=1,file="$UEFI_VARS_TMP" \
    -fw_cfg name=opt/gardenlinux/config_script,file="$FW_CFG_SCRIPT" \
    -netdev user,id=net0,hostfwd=tcp::${GL_SSH_PORT}-:22 \
    -device virtio-net-pci,netdev=net0 \
    -device virtio-rng-pci \
    -serial mon:stdio \
    -nographic \
    -no-reboot

Configuration Variables

VariableDefault (amd64)Default (arm64)Description
GL_IMAGEgardenlinux-2150.0.0.rawgardenlinux-2150.0.0.rawPath to the .raw disk image
GL_ARCHamd64arm64Architecture (auto-detected)
GL_CPU22Number of virtual CPUs
GL_MEM20482048Memory in MiB
GL_SSH_PORT22222222Host port forwarded to guest SSH (port 22)
QEMU_BINqemu-system-x86_64qemu-system-aarch64QEMU binary to use
QEMU_MACHINEq35virtQEMU machine type
QEMU_CPUhostmaxQEMU CPU model
UEFI_CODE/usr/share/OVMF/OVMF_CODE.fd/usr/share/AAVMF/AAVMF_CODE.fdPath to UEFI firmware code
UEFI_VARS/usr/share/OVMF/OVMF_VARS.fd/usr/share/AAVMF/AAVMF_VARS.fdPath to UEFI firmware variables

Step 5: Connect to Your Instance

Wait for the VM to boot and the configuration script to complete (typically 30-60 seconds). Then connect via SSH using the forwarded port:

bash
ssh -i "$SSH_KEY_DIR/id_ed25519" -p "$GL_SSH_PORT" "$SSH_USER"@localhost

TIP

Garden Linux uses gardenlinux as the default SSH username for local and KVM deployments. This differs from cloud platforms like AWS, which use ec2-user.

Step 6: Verify the Installation

Once connected, verify your Garden Linux installation with the following commands:

bash
# Check OS information
cat /etc/os-release

# Verify kernel version
uname -a

# Check system status
systemctl status

# View network configuration
ip addr show

Expected output from /etc/os-release should show:

NAME="Garden Linux"
VERSION="2150.0.0"
ID=gardenlinux
...

Success Criteria

You have successfully completed this tutorial when:

  • Your Garden Linux virtual machine is running
  • You can connect via SSH on the forwarded port
  • You can verify the Garden Linux version using cat /etc/os-release

Cleanup

When you're finished with the tutorial, clean up the resources:

bash
# Stop the VM by pressing Ctrl+A, then X in the QEMU console
# or send the poweroff command from within the VM

# Remove temporary files
rm -f "$FW_CFG_SCRIPT" "$UEFI_VARS_TMP" "$GL_IMAGE"
rm -rf "$SSH_KEY_DIR"

Next Steps

Now that you have a running Garden Linux instance on KVM, you can: