How to build Photon OS images using POI - dcasota/photonos-scripts GitHub Wiki

prerequisites.

tdnf makecache
tdnf install -y git docker docker-buildx
systemctl enable docker
systemctl start docker
# credentials
docker login

Clean an old installation.

cd $HOME
CONTAINERNAME="photon-os-installer"
rm -rf /root/repo
docker image rm $CONTAINERNAME --force && docker system prune --force && docker image prune --force && docker container prune --force && docker volume prune --force

Clone photon-os-installer.
NOTE: Cloning the latest repository content ends up with an photon.iso which cannot display the installer menu.
Cloning v2.7 does not have that issue. It remained unclear why the .spec file is on v2.4 but there obviously is a source v2.7

RELEASE="4.0"
mkdir -p $HOME/repo/$RELEASE
cd $HOME/repo/$RELEASE
# tags might be necessary
git clone --branch v2.7 https://github.com/vmware/photon-os-installer
cd $HOME/repo/$RELEASE/photon-os-installer

Copy the following zip file into $HOME/repo/$RELEASE/photon-os-installer. minimal-iso-4.0_x86_64.zip

optional: modifications.zip

Unzip it.

tdnf install -y unzip wget
wget https://github.com/user-attachments/files/21509001/minimal-iso-4.0_x86_64.zip
unzip ./minimal-iso-4.0_x86_64.zip

# optional
wget https://github.com/user-attachments/files/21518980/modifications.zip
unzip ./modifications.zip
mv ./iso_config.py photon_installer/
mv ./installer.py photon_installer/

Build the container and start it interactively.

docker buildx build -t $CONTAINERNAME --build-context poi-helper=/root/repo/4.0/photon-os-installer ./minimal-iso-4.0_x86_64/.
docker run --rm -it --privileged -v /dev:/dev -v /$HOME/repo/4.0/photon-os-installer/minimal-iso-4.0_x86_64:/output $CONTAINERNAME /bin/bash

Make the iso.

# run inside the container
cd examples/iso
../../photon_installer/isoBuilder.py -y iso.yaml
# after the iso has been created, copy it to /output
cp photon-4.0.iso /output

Setup of a vm from the iso still fails. See https://github.com/vmware/photon-os-installer/issues/35 (edited 9th July 2025).

Cleanup.

# cleanup old photon-iso-builder directories
rm -rf /$HOME/repo/4.0/photon-os-installer/examples/iso/photon-*
docker image rm $CONTAINERNAME --force && docker system prune --force && docker image prune --force && docker container prune --force && docker volume prune --force

vmdk-convert Dockerfile. Dockerfile.txt

local cached repo

Optional: classic make minimal iso build with official remote sources Insert here the classic make build process or copy all official RPMS from https://packages.vmware.com/photon/4.0/photon_release_4.0_x86_64.

#!/bin/bash

# define the path
mkdir -p $HOME/4.0/stage/RPMS
cd $HOME/4.0/stage/RPMS

# Array of URLs and corresponding output directories
urls=(
    "https://packages.vmware.com/photon/4.0/photon_release_4.0_x86_64/x86_64/"
    "https://packages.vmware.com/photon/4.0/photon_release_4.0_x86_64/noarch/"
)
output_dirs=(
    "./x86_64"
    "./noarch"
)

# Check if the number of URLs matches the number of output directories
if [ ${#urls[@]} -ne ${#output_dirs[@]} ](/dcasota/photonos-scripts/wiki/-${#urls[@]}--ne-${#output_dirs[@]}-); then
    echo "Error: Number of URLs (${#urls[@]}) does not match number of output directories (${#output_dirs[@]})"
    exit 1
fi

# Loop through URLs and output directories
for ((i=0; i<${#urls[@]}; i++)); do
    url="${urls[$i]}"
    output_dir="${output_dirs[$i]}"

    # Create output directory if it doesn't exist
    mkdir -p "$output_dir"

    # Fetch directory listing and extract .rpm file URLs
    echo "Processing URL: $url"
    curl -s "$url" | grep -o 'href="[^"]*\.rpm"' | sed 's/href="//' | sed 's/"//' | while read -r file; do
        # Decode %2B%2B to ++ in the filename
        decoded_file=$(echo "$file" | sed 's/%2B%2B/++/g')
        # Download the file, saving it with the decoded filename
        curl -s -o "$output_dir/$decoded_file" "$url$file"
        if [ $? -ne 0 ](/dcasota/photonos-scripts/wiki/-$?--ne-0-); then
            echo "Error: Failed to download $decoded_file from $url"
            exit 1
        fi
        echo "Downloaded: $output_dir/$decoded_file"
    done

    # Check if any files were downloaded for this URL
    if ls "$output_dir"/*.rpm >/dev/null 2>&1; then
        echo "Successfully downloaded RPMs to $output_dir"
    else
        echo "Warning: No RPM files downloaded to $output_dir from $url"
    fi
done

echo "All downloads completed"

Copy the missing RPMS from photon_updates_4.0/RPMS/noarch.

cd $HOME/4.0
mkdir -p ./photon_updates_4.0/RPMS/noarch
cd ./photon_updates_4.0/RPMS/noarch
wget https://packages.vmware.com/photon/4.0/photon_updates_4.0_x86_64/noarch/stig-hardening-1.5-2.ph4.noarch.rpm
wget https://packages.vmware.com/photon/4.0/photon_updates_4.0_x86_64/noarch/grub2-theme-4.0-2.ph4.noarch.rpm
wget https://packages.vmware.com/photon/4.0/photon_updates_4.0_x86_64/noarch/python3-requests-2.26.0-5.ph4.noarch.rpm
mkdir -p ./photon_updates_4.0/RPMS/x86_64/
cd /root/4.0/photon_updates_4.0/RPMS/x86_64/
wget https://packages.vmware.com/photon/4.0/photon_updates_4.0_x86_64/x86_64/rpm-plugin-systemd-inhibit-4.16.1.3-21.ph4.x86_64.rpm

content of rpmdb.sqlite.

#!/bin/bash

# SQLite database file
db_file="/var/lib/rpm/rpmdb.sqlite"

# Check if file exists
if [ ! -f "$db_file" ](/dcasota/photonos-scripts/wiki/-!--f-"$db_file"-); then
    echo "Error: SQLite file $db_file not found"
    exit 1
fi

# Check if sqlite3 is installed
if ! command -v sqlite3 >/dev/null 2>&1; then
    echo "Error: sqlite3 command not found. Install with 'tdnf install sqlite'"
    exit 1
fi

# List all tables
echo "Tables in $db_file:"
sqlite3 "$db_file" ".tables"

# Loop through each table and display schema and data
tables=$(sqlite3 "$db_file" ".tables")
for table in $tables; do
    echo -e "\nSchema for table '$table':"
    sqlite3 "$db_file" ".schema $table"
    echo -e "\nData in table '$table':"
    sqlite3 -column -header "$db_file" "SELECT * FROM $table;"
done

run container interactively:

# cleanup old photon-iso-builder directories
rm -rf /workdir/photon-*

docker run --rm -it --privileged -v /dev:/dev -v /$HOME/4.0/photon_updates_4.0:/photon_updates_4.0 -v /$HOME/4.0/stage:/repo -v /$HOME/repo/4.0/photon-os-installer/examples/iso:/workdir $CONTAINERNAME /bin/bash

Run inside the interactive shell.

createrepo /repo
createrepo /photon_updates_4.0

Some remarks. The more you use the /repo repository, adopt the yaml files to use local rpms. (todo)

The following script modifies packages_installer_40_initrd.json with the /repo path and full file name of the package. update_json_packages.txt

mv update_json_packages.txt update_json_packages.sh
chmod a+x ./update_json_packages.sh
./update_json_packages.sh ./packages_installer_40_initrd.json
./update_json_packages.sh ./packages_minimal.json

Make the iso.

cd /$HOME/repo/4.0/photon-os-installer/examples/iso
# -v /$HOME/repo/4.0/photon-os-installer/examples/iso:/workdir : Here are all .json and .yaml files
# -v /$HOME/4.0/stage:/repo : Here is the local repository done by classic make build
docker run --rm --privileged -v /dev:/dev -v /$HOME/4.0/stage:/repo -v /$HOME/repo/4.0/photon-os-installer/examples/iso:/workdir $CONTAINERNAME photon-iso-builder -y iso_40_minimal.yaml