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