> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/ask/flat/chapter-18-linux-compatibility-layer/di-18.2-jie-rocky-linux-jian-rong-ceng.md).

# 18.2 Rocky Linux Compatibility Layer

FreeBSD provides a Rocky Linux 9.7 compatibility layer through the **emulators/linux-rl9** Port (which includes linux\_base-rl9). This section covers installation steps, version verification, and basic configuration.

## Installing the Rocky Linux Compatibility Layer via FreeBSD Ports

### Rocky Linux Version Number Overview

As of February 16, 2026, the FreeBSD Ports [emulators/linux-rl9](https://www.freshports.org/emulators/linux-rl9/) (whose corresponding base package is [emulators/linux\_base-rl9](https://www.freshports.org/emulators/linux_base-rl9/)) is built on the Rocky Linux 9.7 distribution.

View the current Linux distribution version information within the Rocky Linux compatibility layer environment:

```sh
bash-5.1$ cat /etc/redhat-release      # Inside the Rocky Linux compatibility layer
Rocky Linux release 9.7 (Blue Onyx)
```

This version number serves only as a reference, not as an absolute identifier. The **/etc/redhat-release** file is provided by the Rocky Linux `rocky-release` package and contains static text. A Linux distribution is essentially a collection of multiple independent packages. The version information of a single package cannot determine that all packages in the distribution belong to the same version; therefore, the version number of a Linux distribution is a relative concept.

Further examination of the commit record [emulators/linux\_base-rl9: update to Rocky Linux 9.7 (+)](https://github.com/freebsd/freebsd-ports/commit/3eb21a5228ef7b1c30886e17ad3f53b0066e1fa9) reveals that what truly determines the version number is the value of the `LINUX_DIST_VER` variable in the [Mk/Uses/linux.mk](https://github.com/freebsd/freebsd-ports/blob/main/Mk/Uses/linux.mk) file within the Ports framework:

```make
.  if empty(linux_ARGS)
.    if exists(${LINUXBASE}/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7)  # Check if RPM-GPG-KEY-CentOS-7 exists in the compatibility layer; if so, it is a CentOS 7 compatibility layer
linux_ARGS=		c7
.    elif exists(${LINUXBASE}/etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9) # Check if RPM-GPG-KEY-Rocky-9 exists in the compatibility layer; if so, it is a Rocky Linux 9 compatibility layer
linux_ARGS=		rl9
.    else
linux_ARGS=		${LINUX_DEFAULT}
.    endif
.  endif

.  if ${linux_ARGS} == c7  # Check if it is a CentOS 7 compatibility layer
LINUX_DIST_VER?=	7.9.2009    # Currently importing CentOS 7.9.2009
.  elif ${linux_ARGS} == rl9   # Check if it is a Rocky Linux 9 compatibility layer
LINUX_DIST_VER?=	9.7  # Currently importing Rocky Linux 9.7
.  else
ERROR+=			"Invalid Linux distribution: ${linux_ARGS}"  # If neither, print error message directly
.  endif
```

### Enabling the Linux Compatibility Layer Service

Before installing the Rocky Linux compatibility layer, you must first enable the Linux compatibility layer service.

Enable the Linux compatibility layer service and set it to start on boot:

```sh
# service linux enable
```

Start the Linux compatibility layer service immediately:

```sh
# service linux start
```

### Installing the Base System

* Install using pkg:

```sh
# pkg install linux-rl9
```

* Alternatively, install using Ports:

```sh
# cd /usr/ports/emulators/linux-rl9/
# make install clean
```

### Configuring Related Services

After installation, you also need to configure related services.

```sh
# service dbus enable  # Usually already configured by the desktop environment
# service dbus start  # Usually already configured by the desktop environment
```

Restart the system for changes to take effect.

## Installing the Rocky Linux 10 Compatibility Layer via Shell Script

The following script will build Rocky Linux 10.2 (codename Red Quartz) under **/compat/rocky**.

```sh
#!/bin/sh

ROOT_DIR=/compat
DIST=rocky
DIST_FULLNAME="Rocky Linux"
VER=10
SUB_VER=2
TIME_VER="latest"
SORT=Base
FILE=Rocky-${VER}-WSL-${SORT}.${TIME_VER}.x86_64.wsl
SUBDIR=""
URL=https://mirrors.ustc.edu.cn/${DIST}/${VER}.${SUB_VER}/images/x86_64
UPDATE_CMD="dnf makecache"
UPGRADE_CMD="dnf upgrade -y"
INSTALL_CMD="dnf install -y"
UPDATE=1
UPGRADE=1
INSTALL=1

# Create target directory in advance
TARGET_PATH="${ROOT_DIR}/${DIST}"
mkdir -p "${TARGET_PATH}"

echo "Starting ${DIST_FULLNAME} installation"
sleep 0.5

# Check Linux module
echo "Checking required modules"

if [ "$(sysrc -n linux_enable 2>/dev/null)" != "YES" ]; then
    echo "Linux service is not enabled. Enable it now? (Y|n)"
    read ANSWER
    case $ANSWER in
        [Nn][Oo]|[Nn])
            echo "Warning: You must start the Linux service with \"service linux start\" after each FreeBSD reboot."
            echo "Are you sure you want to continue without enabling the Linux service? (y|N)"
            read ANSWER
            case $ANSWER in
                [Yy][Ee][Ss]|[Yy])
                    echo "WARNING: Linux module not enabled"
                    ;;
                [Nn][Oo]|[Nn]|"")
                    echo "Enabling Linux module"
                    service linux enable
                    ;;
                *)
                    echo "Aborting."
                    exit 4
                    ;;
            esac
            ;;
        [Yy][Ee][Ss]|[Yy]|"")
            echo "Enabling Linux module"
            service linux enable
            ;;
        *)
            echo "Aborting."
            exit 4
            ;;
    esac
fi

echo "Starting Linux service"
service linux start

# Check dbus
if ! which -s dbus-daemon; then
    echo "dbus-daemon not found. Install D-Bus now? (Y|n)"
    read ANSWER
    case $ANSWER in
        [Nn][Oo]|[Nn])
            echo "Aborting. D-Bus not installed"
            exit 2
            ;;
        [Yy][Ee][Ss]|[Yy]|"")
            echo "Installing D-Bus"
            pkg install -y dbus
            ;;
        *)
            echo "Aborting."
            exit 4
            ;;
    esac
fi

if [ "$(sysrc -n dbus_enable 2>/dev/null)" != "YES" ]; then
    echo "D-Bus is not enabled. Enable it now? (Y|n)"
    read ANSWER
    case $ANSWER in
        [Nn][Oo]|[Nn])
            echo "WARNING: You must start D-Bus with \"service dbus start\" after each FreeBSD reboot."
            echo "Are you sure you want to continue without enabling D-Bus? (y|N)"
            read ANSWER
            case $ANSWER in
                [Yy][Ee][Ss]|[Yy])
                    echo "Warning: D-Bus service not enabled"
                    ;;
                [Nn][Oo]|[Nn]|"")
                    echo "Enabling D-Bus service"
                    service dbus enable
                    ;;
                *)
                    echo "Aborting."
                    exit 4
                    ;;
            esac
            ;;
        [Yy][Ee][Ss]|[Yy]|"")
            echo "Enabling D-Bus service"
            service dbus enable
            ;;
        *)
            echo "Aborting."
            exit 4
            ;;
    esac
fi


# =====================================================================
# Download and extraction main logic
# =====================================================================
echo "${DIST_FULLNAME} will be installed in ${TARGET_PATH}"

# Dynamic integrity verification
check_integrity() {
    # 1. Target file does not exist, return failure directly
    [ -f "${FILE}" ] || return 1

    # 2. Check if checksum file exists
    if [ ! -f "CHECKSUM" ]; then
        echo "Error: CHECKSUM file is missing." >&2
        return 1
    fi

    # 3. Extract the expected hash value for the corresponding file from CHECKSUM
    local expected_hash
    expected_hash=$(grep "(${FILE})" CHECKSUM | awk '{print $NF}')

    if [ -z "${expected_hash}" ]; then
        echo "Warning: No checksum found for ${FILE} in CHECKSUM file." >&2
        return 1
    fi

    # 4. Get the local actual hash value
    local actual_hash
    if command -v sha256 >/dev/null 2>&1; then
        actual_hash=$(sha256 -q "${FILE}")
    else
        echo "Error: sha256 command not found on this system." >&2
        return 1
    fi

    # Print SHA256 comparison list
    echo "========================================================"
    echo " Verifying checksum for: ${FILE}"
    echo "--------------------------------------------------------"
    echo " Expected SHA256: ${expected_hash}"
    echo " Actual   SHA256: ${actual_hash}"
    echo "========================================================"

    # 5. String comparison
    if [ "${expected_hash}" = "${actual_hash}" ]; then
        echo " -> Status: [ OK ] Checksum matched perfectly."
        return 0
    else
        echo " -> Status: [ FAILED ] Checksum mismatch!"
        return 1
    fi
}

# Core prerequisite: download the latest CHECKSUM verification manifest
echo "Updating verification manifest (CHECKSUM)..."
fetch "${URL}/CHECKSUM"

if [ ! -f "CHECKSUM" ]; then
    echo "Critical Error: Failed to download CHECKSUM manifest from remote server." >&2
    exit 1
fi

# Step 1: Check if a valid file already exists locally
if check_integrity; then
    echo "Skipping download as valid archive is already present."
else
    # If the file exists but verification fails, the previous download was incomplete; attempt to resume
    if [ -f "${FILE}" ]; then
        echo "Local file exists but is incomplete or invalid. Attempting to resume download..."
    else
        echo "Downloading basic system..."
    fi

    # First download: use -r parameter to enable resume
    fetch -r "${URL}/${FILE}"

    # Step 2: Verify after download; if failed, retry once
    if ! check_integrity; then
        echo "Cleaning up corrupted cache and retrying download one last time..."

        # Clean up potentially corrupted resume fragments, retry full download
        rm -f "${FILE}"
        fetch "${URL}/${FILE}"

        # Final verification: if still failed, terminate script
        if ! check_integrity; then
            echo "Error: Installation aborted due to persistent checksum failures." >&2
            exit 1
        fi
    fi
fi

# Step 3: Extract the base system
echo "Extracting basic system"
sleep 0.5
tar xvpf "${FILE}" ${SUBDIR:-} -C "${TARGET_PATH}" --numeric-owner

# Modify the compatibility layer default path
sysctl compat.linux.emul_path="${TARGET_PATH}"
if ! grep -q "compat.linux.emul_path" /etc/sysctl.conf; then
    echo "compat.linux.emul_path=${TARGET_PATH}" >> /etc/sysctl.conf
else
    # If it already exists, update it
    sed -i '' "s|compat.linux.emul_path=.*|compat.linux.emul_path=${TARGET_PATH}|" /etc/sysctl.conf
fi

linux_path=$(sysctl -n compat.linux.emul_path)
echo "Now compat.linux.emul_path is $linux_path"

# Set Linux kernel version, otherwise chroot will prompt FATAL: kernel too old
sysctl compat.linux.osrelease=7.0.11
if ! grep -q "compat.linux.osrelease" /etc/sysctl.conf; then
    echo "compat.linux.osrelease=7.0.11" >> /etc/sysctl.conf
else
    sed -i '' "s|compat.linux.osrelease=.*|compat.linux.osrelease=7.0.11|" /etc/sysctl.conf
fi

osrelease=$(sysctl -n compat.linux.osrelease)
echo "Now compat.linux.osrelease is $osrelease"
service linux restart

# Configure DNS
echo "Should ${DIST_FULLNAME} use Alibaba DNS or local resolv.conf? ((A)li | (L)ocal | (C)ancel)"
read ANSWER
case $ANSWER in
    [Aa][Ll][Ii]|[Aa]|"")
        echo "Setting Alibaba DNS"
		grep -q "nameserver 223.5.5.5" "${TARGET_PATH}/etc/resolv.conf" 2>/dev/null || \
		    echo "nameserver 223.5.5.5" >> "${TARGET_PATH}/etc/resolv.conf"
		grep -q "nameserver 223.6.6.6" "${TARGET_PATH}/etc/resolv.conf" 2>/dev/null || \
		    echo "nameserver 223.6.6.6" >> "${TARGET_PATH}/etc/resolv.conf"
        ;;
    [Ll][Oo][Cc][Aa][Ll]|[Ll])
        echo "Using local resolv.conf"
        cp /etc/resolv.conf "${TARGET_PATH}/etc/resolv.conf"
        ;;
    *)
        echo "Canceled."
        echo "You must manually edit ${TARGET_PATH}/etc/resolv.conf!"
        ;;
esac

# Set USTC mirror source
echo "Do you want to use the USTC mirror for ${DIST_FULLNAME}? (Y|n)"
read ANSWER
case $ANSWER in
    [Yy][Ee][Ss]|[Yy]|"")
        echo "Setting USTC mirror"
        chroot "${TARGET_PATH}" /bin/bash -c 'sed \
            -e "s|^mirrorlist=|#mirrorlist=|g" \
            -e "s|^#baseurl=http://dl.rockylinux.org/\$contentdir|baseurl=https://mirrors.ustc.edu.cn/rocky|g" \
            -i.bak \
            /etc/yum.repos.d/rocky-extras.repo \
            /etc/yum.repos.d/rocky.repo'
        ;;
    [Nn][Oo]|[Nn])
        echo "Will not set USTC mirror. Skipping update, upgrade, and installation."
        UPDATE=0
        UPGRADE=0
        INSTALL=0
        ;;
    *)
        echo "Aborting."
        exit 4
        ;;
esac

# Update, upgrade, and install software
# In FreeBSD, the unit of ping -W parameter is milliseconds
if ping -c 1 -W 3000 223.5.5.5 > /dev/null 2>&1; then
    echo "Network reachable, starting package operations..."

    # 1. Update package cache
    if [ "$UPDATE" = "1" ]; then
        echo "Updating package cache..."
        chroot "${TARGET_PATH}" /bin/bash -c "${UPDATE_CMD}" || exit 1
    fi

    # 2. Upgrade system packages
    if [ "$UPGRADE" = "1" ]; then
        echo "Upgrading system packages..."
        chroot "${TARGET_PATH}" /bin/bash -c "${UPGRADE_CMD}" || exit 1
    fi

    # 3. Install tools and language packs
    if [ "$INSTALL" = "1" ]; then
        echo "Installing nano and language packs..."
        chroot "${TARGET_PATH}" /bin/bash -c "${INSTALL_CMD} nano langpacks-en glibc-all-langpacks" || exit 1
    fi

else
    echo "Network unreachable, skipping updates and installation."
fi

# Cleanup
echo "Cleaning up"
rm -f "${FILE}"

echo "All done."
echo "You can switch to ${DIST_FULLNAME} with \"chroot ${TARGET_PATH} /bin/bash\""
```

![Rocky Linux 10 Compatibility Layer](/files/aaShBfFx0ExAzd2gh3mg)

## References

* Rocky Linux Documentation. Rocky Linux 9 Release Notes\[EB/OL]. \[2026-04-17]. <https://docs.rockylinux.org/9/release_notes/>. Release notes for Rocky Linux 9 series versions, 9.7 codenamed Blue Onyx.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.bsdcn.org/ask/flat/chapter-18-linux-compatibility-layer/di-18.2-jie-rocky-linux-jian-rong-ceng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
