Skip to content

Instantly share code, notes, and snippets.

@codello
Last active March 6, 2024 19:28
Show Gist options
  • Save codello/f70973b1106978722eb2016b8b37b801 to your computer and use it in GitHub Desktop.
Save codello/f70973b1106978722eb2016b8b37b801 to your computer and use it in GitHub Desktop.
How to install CentOS 8 on a Mac.

Installing CentOS on Apple Hardware

Installing recent CentOS versions on Macs is not as straight forward as I wish it to be. I repeatedly had issues even getting the installation media to boot. In this snippet I gather my findings and present a way to successfully install CentOS on a Mac.

Tested using CentOS Stream 8 on a Late 2014 Mac Mini.

1. Preparing the Installation Media

When using the usual methods of creating a bootable USB drive (e.g. by using balenaEtcher) I was greeted by a black screen. In order to successfully get to the CentOS installer I did the following things:

  1. Format the USB drive
  2. Copy the ISO onto the USB drive
  3. Replace the EFI boot file
  4. Edit grub.cfg
  5. Copy repair-grub.sh and mount-lvm.sh to the USB drive

1.1 Formating the USB drive

This step is mainly necessary in order to be able to modify files on the USB drive in macOS. By default the system wasn't able to read the filesystem of the CentOS ISO or the drive created by Etcher. If you are using a Linux machine you can probably skip steps 1.1 and 1.2 and instead copy the contents of the ISO to the USB drive using dd.

On a Mac however I erased the USB drive using Disk Utility:

  • Choose a name for the volume. This name will be important later. This name will be referred to as <USB>.
  • Format the volume with a FAT filesystem
  • Use the GUID partition table (apparently some macs are especially picky about this setting).

1.2 - 1.4 Mac-specific tweaks

Edit the create-ush.sh script with your <USB> name and the location of the CentOS ISO file. Then run the script. The script will perform the following actions in a docker container:

  • Mount the ISO
  • Copy its contents to the installation media
  • Create a EFI boot image that does not result in a black screen but actually gets you into grub.
  • Fix the grub.cfg file to use your <USB> name. By default it expects a certain name for the volume, depending on the ISO you used.

1.5. Copy repair-grub.sh and mount-lvm.sh

Copy the contents of repair-grub.sh and mount-lvm.sh onto the installation media. We will use them in step 3. Alternatively you type the commands in the script manually in step 3.

Remember to adjust the values in mount-lvm.sh to your environment.

2. Installing CentOS

Installing CentOS is relatively straight forward. However we have to do some custom partitioning. Here are the relevant steps, slightly adjusted:

  • Select your disk
  • At the bottom, click the "Full disk summary and boot loader" text
    • Click on the disk in the list
    • Click "Do not install boot loader"
    • Close
  • Select "Custom" (I didn't try automatic, but it probably would not create the EFI partition)
  • Done in the top left to get to the partitioning screen
  • Delete existing partitions if needed
  • Click +
    • Create /foo mountpoint, 600M, Standard partition, then edit the partition to be on /boot/efi and change the file system to 'EFI System Partition'.
  • Click + repeatedly to create the rest of the partitions as usual (/boot, /swap, /home, etc.).
  • Done

In my case the package installation from install media was not possible. Instead I used a package repository URL as the installation source, more specifically http://mirror.centos.org/centos/8/BaseOS/x86_64/os/.

When starting the installation there will be an error about a missing mactel-boot package. Ignore the error and continue the installation.

3. Making the Installation Bootable

When the installation finishes the system is not quite usable yet. We need to fix two things:

  • Reinstall grub (to avoid the booting-to-black-screen problem)
  • Create a grub.cfg.

To do this, boot from the installation media again and enter the rescue mode.

  • Choose 1 to mount the system on /mnt/sysimage. If you chose to use a LVM root partition there may be an error that no linux filesystem could be found. Run bash /mnt/install/repo/mount-lvm.sh to fix this issue. This is the mount-lvm.sh script copied in step 1.5. Alternatively you can type the commands from the script manually.
  • Run bash /mnt/install/repo/repair-grub.sh. This is the repair-grub.sh script from step 1.5. Alternatively you can type the commands from the script manually.
  • Exit the rescue mode and reboot the machine. It should reboot once after SELinux relabelling and then boot into CentOS.
#!/usr/bin/env sh
ISO_PATH="/path/to/centos.iso" # Must be an absolute path.
USB_NAME="<USB>"
USB_PATH="/Volumes/$USB_NAME" # Must be an absolute path
# Mounting the ISO requires a privileged container.
docker run --rm -it --privileged \
-v "$ISO_PATH":/centos.iso \
-v "$USB_PATH":/usb \
centos bash -c "
echo -e '\033[0;32mInstalling Dependencies...\033[0m'
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
yum install -y grub2 grub2-efi-x64-modules rsync genisoimage
# Mount the ISO at /mnt to access its contents.
mount -o ro /centos.iso /mnt
ISO_NAME=\$(isoinfo -d -i /centos.iso | grep -i 'Volume id:' | cut -d' ' -f3-)
# Store the name of the ISO for later
ISO_NAME=\$(isoinfo -d -i /centos.iso | grep -i 'Volume id:' | cut -d' ' -f3-)
# Copy the contents of the ISO to the USB drive
echo -e '\033[0;32mCopying ISO contents...\033[0m'
rsync -ra --info=progress2 /mnt/. /usb/
# Generate a EFI boot image that can be used on Macs. It may be possible to
# shorten this but it works.
# This is adapted from https://unix.stackexchange.com/a/273334
echo -e '\033[0;32mCreating EFI boot image...\033[0m'
grub2-mkimage -O x86_64-efi -o /usb/EFI/BOOT/BOOTX64.EFI -p /efi/grub \
disk part_msdos part_gpt linux loopback normal configfile test search \
search_fs_uuid search_fs_file true iso9660 test search_label efi_uga \
efi_gop gfxterm gfxmenu gfxterm_menu fat ext2 ntfs cat echo ls memdisk tar
# Replace the default boot drive name with the actual boot drive name.
echo -e '\033[0;32mFixing grub.cfg...\033[0m'
sed -i \"s/\$ISO_NAME/$USB_NAME/g\" /usb/EFI/BOOT/grub.cfg
umount /mnt
"
#!/usr/bin/env sh
LVM_GROUP=<changeme> # The name of the LVM group
LVM_NAME=root # The name of the LVM volume
EFI_DEVICE=/dev/sda1 # Change if necessary
BOOT_DEVICE=/dev/sda2 # Change if necessary
# Find and activate the LVM volume
vgscan
vgchange -ay
# Mount the volume as well as some auxiliary directories to be able to use
# grub inside a chroot.
mount /dev/$LVM_GROUP/$LVM_NAME /mnt/sysimage
mount $BOOT_DEVICE /mnt/sysimage/boot
mount $EFI_DEVICE /mnt/sysimage/boot/efi
mount --bind /dev /mnt/sysimage/dev
mount --bind /sys /mnt/sysimage/sys
mount --bind /proc /mnt/sysimage/proc
mount --bind /run /mnt/sysimage/run
#!/usr/bin/env sh
# Install the grub2-efi-x64-modules in the rescue system. This is used to be
# able to create a new boot image with grub. By installing the package into the
# rescue system we do not modify our installation with additional packages.
# The installed package will be gone after a reboot.
# Since the rescue system usually does not have internet access we create a
# local repo file that points to the BaseOS repo on the installation media.
mkdir -p /etc/yum.repos.d/
cat <<-EOF | tee /etc/yum.repos.d/local.repo >/dev/null
[local]
name=CentOS Local BaseOS
baseurl=file:///mnt/install/repo/BaseOS/
enabled=1
gpgcheck=0
EOF
# We now install the grub modules. Grub itself is already installed.
yum install -y grub2-efi-x64-modules
# This command re-installs grub. This is the grub installation that will
# usually be used for booting CentOS.
grub2-install --target=x86_64-efi \
--boot-directory=/mnt/sysimage/boot \
--efi-directory=/mnt/sysimage/boot/efi \
--bootloader-id=GRUB
# This command re-installs grub at /EFI/BOOT/BOOTX64.EFI. This is usually not
# used during boot but provides a safe fallback as the file generated by the
# CentOS installer is not usable on macOS (results in a blank screen).
grub2-install --target=x86_64-efi \
--boot-directory=/mnt/sysimage/boot \
--efi-directory=/mnt/sysimage/boot/efi \
--bootloader-id=GRUB \
--removable
# This command generates the grub boot menu. If this file does not exist grub
# will immediately drop into the emergency console.
chroot /mnt/sysimage grub2-mkconfig -o /boot/grub2/grub.cfg
@jameshskoh
Copy link

Thanks for your installation guide. I have managed to install the OS into my 2015 13’ MBP.

However, when I was trying to execute the following command:
yum install -y grub2-efi-x64-modules

I got the following error

Running transaction test
Error: Transaction test error
… [truncated glibc, bash, libsepol]
Installing package grub2-common-1:2.02-99.e10.no arch needs 54MB on the / filesystem
installing package grub2-efi-x64-modules-1:2.02-99.e18.no arch needs 60MB on the / filesystem

Error Summary
————————
Disk Requirements:
At least 60MB more space needed on the / filesystem.

Subsequent commands also failed because /usr/lib/grub/x86_64-efi/modinfo.sh doesn’t exist. I believe it is due to the missing module above.

I have little experience in dealing with bootloaders, so I really don’t know what’s gone wrong after hours of searching.
Do you have any idea what I have done wrong? What should I try instead? Thank you!

FYI:
The MBP uses a 3rd party 500GB NVMe SSD. Partitions as follow:
EFI: 600MB EFI
BOOT: 1GB ext4
/: rest of the drive ext4
SWAP: 16GB
None is set as a LVM partition.

I wrote the CentOS installer image onto a 120GB external SSD using your method. So there is definitely more than 60MB of space in my external SSD.

I have also tried df -h and found that /dev/mapper/live-rw mounted on / still has 442M available.

@codello
Copy link
Author

codello commented Sep 14, 2021

Hey @jameshskoh, glad my notes helped you so far. Unfortunately I'm not an expert on this as well and I don't currently have a Mac that I can test this with. But here are my thoughts:

IIRC the rescue system uses the filesystem on the install disk (the 120GB SSD in your case) as a read-only base image and overlays a tmpfs read-write layer on top. I think /dev/mapper/live-rw is the resulting overlay filesystem. Essentially this means that the usable disk space of the rescue system is limited by the amount of memory rather than disk space (which I guess is at least 8GB in your case). I don't think the rescue system uses swap.

I am no expert with yum/dnf but I have found other sources reporting similar errors although enough disk space should be available. I guess this could be the case if the 60MB is only the required space for one package but there are multiple packages being installed. Or maybe yum tries to keep at least some percentage of disk space available. Anyway I think that the amount of available disk space (read: memory) might actually be the source of the problem.

Although I'm wondering why this happened (as I did my original tests on a machine with 8GB of memory as well) I have found some ideas how to circumvent this problem:

  • yum clean all might clean up leftover package data giving enough available space for the grub2-efi-x64-modules package.
  • I haven't tried this but it is probably possible to remount the read-write tmpfs filesystem with more storage using the command mount -o remount,size=<new_size> /path/to/tmpfs. You could maybe find the path of the mount point by running df -h or mount.
  • You could also probably somehow modify the /etc/fstab of the rescue system to increase the size of the tmpfs overlay. But you would have to do that on the persistent part on the install disk before booting the rescue system.
  • Adding diskspacecheck=0 to /etc/yum.conf (or rather /etc/dnf/dnf.conf). This is probably a bad idea on any system that is actually being used but since we are using a temporary recovery system at this point I don't think there's any harm in trying. Only the grub2-install ... commands and the chroot ... grub2-mkconfig ... actually change any persistent data. All the other steps in the rescue system should be gone after a reboot.

I hope this helps.

@zwalex
Copy link

zwalex commented Sep 6, 2022

First of all - thanks for putting this together.
When running the create-usb.sh, I received the error
[Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist]
After some research, I found the solution here:
(https://stackoverflow.com/questions/70963985/error-failed-to-download-metadata-for-repo-appstream-cannot-prepare-internal)

I added

  cd /etc/yum.repos.d/
  sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
  sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
  yum update -y

before the first yum install and the script finished w/o errors.

I can boot the installer from the USB, but I also could not get packages installed from the installation media. My problem is that I cannot use a package repository URL, as my Network devices are not detected by the installer (Nvidia MCP79 and Broadcom 4321). It's a late 2009 Mac Mini. Not sure I will get that resolved.

@jameshskoh
Copy link

@codello sorry for not replying. I don't really understand how to do what and I ended up using a VM instead. Guess I still need to pick up more knowledge about Linux first.

Thank you anyway 😂

@codello
Copy link
Author

codello commented Sep 30, 2022

@zwalex Sorry for not replying sooner. Thanks for your comment. It's been quite some time since I last used these scripts. I'm guessing that changes since then caused the issue with downloading packages. I will update the Gist accordingly.

As for the package installation from the install media I'm afraid I don't really have a solution and unfortunately I don't really have a device for testing. A couple of thoughts though:

  • I personally had a lot more success installing Fedora instead of CentOS on older Macs (as it includes the necessary packages by default). Depending on your situation this might not be a viable alternative. For my Homelab this is one of the reasons why I switched to Fedora.
  • In my experience sometimes the graphical installation fails but the text installation succeeds. Maybe you could try installing in Text Mode.
  • I remember that I tried installing packages from the installation media and failed as well. I am not sure why, because it did work on non-Mac systems. Looking through the CentOS docs it seems to be possible to install packages from an ISO file. Maybe an option could be to put an ISO file (not sure which one though) on another USB and install packages from there). To me this seems to be the most promising option.
  • Another crazy idea might be to try and run a local http, ftp or nfs server on the installation system that serves a mirror of the CentOS package repository. I'm not sure this is possible but installing packages from localhost might just work even with without drivers for the networking hardware.

Edit: Another thought: In the repair-grub.sh script we are using a local directory as the source for yum:

cat <<-EOF | tee /etc/yum.repos.d/local.repo >/dev/null
	[local]
	name=CentOS Local BaseOS
	baseurl=file:///mnt/install/repo/BaseOS/
	enabled=1
	gpgcheck=0
EOF

Maybe you could use a similar approach in the installation system. I am not quite sure if the installer lets you do this but maybe this can be manually configured somewhere.

@zwalex
Copy link

zwalex commented Oct 1, 2022

Thanks @codello!
Yes, I managed to install Fedora and Debian 11 on that old Mac Mini, although with some tweaking because of the Broadcom wireless adapter. I might give CentOS another try with your suggestions.

@abirchall
Copy link

abirchall commented Aug 5, 2023

I had to add the following fixes for /usb/EFI/BOOT/grub.cfg in create-usb.sh:

sed -i 's/linuxefi/linux/g' /usb/EFI/BOOT/grub.cfg
sed -i 's/initrdefi/initrd/g' /usb/EFI/BOOT/grub.cfg

I'm currently stuck on fixing grub post centos install due to the following error when running grub2-install:

grub2-install: error: this utility cannot be used for EFI platforms because it does not support UEFI Secure Boot.

I've attempted to apply the steps here but to no avail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment