So here's the deal—I've got this Z97-based Hackintosh with an i7-4770 and an RX 580, and it's basically a time machine for macOS versions. Both CPU and GPU are natively supported from Sierra (10.12) through Monterey(12), and with the right SMBIOS configuration and some essential kexts, these versions run almost like they would on actual Apple hardware. Newer releases like Ventura and Sonoma? Also doable, but you're looking at heavier kext dependencies and OCLP patches—essentially entering "it works but the maintenance overhead is real" territory.
The how-to-get-each-version-running part isn't what this post is about though. This is about what happens after you've figured out how to boot each version of macOS on it's own (from an USB stick for example) and realized that having them coexist peacefully on one drive is its own separate nightmare.
OpenCore is genuinely impressive at what it does. It's flexible, well-documented (by Hackintosh standards, which is a low bar, but still), and handles the heavy lifting of making non-Apple hardware pretend to be a Mac. The thing is—and this is where my setup started falling apart—OpenCore operates on a "one config to rule them all" philosophy.
You get one config.plist. One set of SMBIOS settings, kexts, ACPI patches, and boot arguments.
This works fine if you're running a single macOS version. But I've got a 1TB SSD that can easily hold multiple 60-100 GB macOS partitions (I'm sticking to APFS-compatible versions, so High Sierra and newer), and each version has different requirements:
- Different SMBIOS: Mojave might want
iMac15,1, while Monterey prefersiMac17,1for optimal compatibility. Use the wrong one and you're looking at anything from subtle bugs to straight-up boot failures. - Different kext configurations: Some kexts are version-specific, some need different settings per OS version.
- Different boot arguments: What works for one version might cause kernel panics on another.
The result? You can install multiple macOS versions and OpenCore will happily show all of them in the boot picker—including the ones that absolutely will not boot with your current config. It's like a restaurant menu where half the items will give you food poisoning, but there's no indication which ones. Classic.
I briefly considered the "just swap config.plist files manually" approach, but that's basically asking for human error to ruin my day. There had to be a better way.
After going down various rabbit holes (some productive, most not), I landed on a setup that actually works. It's not exactly elegant in the "single unified solution" sense, but it's robust and—most importantly—it doesn't require me to remember which config goes with which OS.
The architecture breaks down into two main layers with what I'd call a "half-layer" for volume filtering:
Layer 1: rEFInd — The initial boot manager that the BIOS hands off to. It presents a clean menu where I pick which OpenCore instance to load.
Layer 2: Multiple OpenCore Instances — Each macOS version gets its own complete OpenCore setup with version-appropriate settings. They live in separate subdirectories under \EFI\OC\.
The Half-Layer: .contentVisibility Files — These sit in each macOS installation's Preboot volume and tell specific OpenCore instances to ignore that volume. It's not really a "layer" in the boot chain sense—more like a filter that runs during OpenCore's volume discovery. But as a special sauce that makes everything work together it deserves its place in the spotlights.
I also bumped my EFI partition to 512MB to fit multiple complete OpenCore installations. The default 200MB works fine for a single config, but once you're duplicating the entire OC folder structure multiple times, you'll hit space constraints fast.
Here's what happens when I power on:
-
BIOS → rEFInd: The firmware loads
\EFI\BOOT\BOOTx64.efi, which is rEFInd. I see a clean menu with entries like "macOS Mojave (OC)", "macOS Monterey (OC)", "Windows", etc. -
rEFInd → OpenCore: I select an entry, and rEFInd chainloads the corresponding
OpenCore.efifrom that version's subdirectory. -
OpenCore → macOS: OpenCore does its thing—applies patches, injects kexts, and presents its own boot picker. But here's the key part: thanks to the
.contentVisibilityfiltering, it only shows the appropriate macOS volume. No more "which one of these five options won't kernel panic" guessing games. -
The Auxiliary Mode Bonus: Pressing Space in OpenCore's picker reveals auxiliary entries—Recovery, Reset NVRAM, etc. Currently, this also shows Recovery partitions from other macOS installations (haven't filtered those out yet, but it should work the same way). Honestly, repairing borked installation is a moment when the brain shouldn't be on autopilot anyway so having to pick the right recovery environment is a brain warm up - not a bug but a feature!
The whole sequence is about as seamless as running two bootloaders in series can be. There's a brief rEFInd screen, then a brief OpenCore screen, then macOS. Total added boot time is maybe 3-4 seconds of menu navigation, which is an acceptable tax for the flexibility.
rEFInd goes into \EFI\BOOT\ so the firmware picks it up as the default bootloader. The critical configuration bit in refind.conf:
scanfor external,optical,manual
This disables auto-scanning for internal bootloaders—otherwise rEFInd would discover all the OpenCore instances and the macOS installations directly, creating a cluttered mess of duplicate entries. We want manual control here.
menuentry "macOS Mojave (OC)" {
icon \EFI\BOOT\icons\os_mac.png
loader \EFI\OC\Mojave\OpenCore.efi
}
menuentry "macOS Monterey (OC)" {
icon \EFI\BOOT\icons\os_mac.png
loader \EFI\OC\Monterey\OpenCore.efi
}
menuentry "Windows 10" {
icon \EFI\BOOT\icons\os_win8.png
volume WINEFI
loader \EFI\Microsoft\Boot\bootmgfw.efi
}
menuentry "Parted Magic" {
icon \EFI\BOOT\icons\tool_rescue.png
volume PARTEDMAGIC
loader \EFI\boot\bootx64.efi
}
Straightforward stuff. Each OpenCore instance gets its own entry pointing to its specific OpenCore.efi.
Each instance lives in its own directory with a complete, independent setup. All examples are with two macOS versions but can be scaled up:
EFI/
├── BOOT/
│ ├── BOOTx64.efi ← rEFInd (as a default bootloader)
│ ├── refind.conf
│ └── ...
└── OC/
├── Mojave/
│ ├── OpenCore.efi
│ ├── config.plist ← Mojave-specific SMBIOS & settings
│ ├── ACPI/
│ ├── Drivers/
│ └── Kexts/
└── Monterey/
├── OpenCore.efi
├── config.plist ← Monterey-specific SMBIOS & settings
├── ACPI/
├── Drivers/
└── Kexts/
Most of your config.plist will be version-specific (SMBIOS, kexts, patches, etc.), but these settings are specifically required for the multi-instance isolation to work:
Misc → Boot:
<key>HideAuxiliary</key>
<true/>
<key>PickerMode</key>
<string>External</string>
<key>ShowPicker</key>
<true/>
<key>InstanceIdentifier</key>
<string>OC-MOJAVE</string>The InstanceIdentifier is the key piece—it's what the .contentVisibility files use to target specific OpenCore instances. Make it unique and descriptive.
Misc → Security:
<key>ScanPolicy</key>
<integer>257</integer>ScanPolicy = 257 limits scanning to internal APFS drives. Keeps the picker clean and speeds up boot slightly.
| Instance | InstanceIdentifier |
|---|---|
| Mojave | OC-MOJAVE |
| Monterey | OC-MONTEREY |
This is where the magic happens—and honestly, it took me way too long to discover this feature existed. The .contentVisibility file is an OpenCore mechanism that lets you hide specific volumes from specific OpenCore instances.
Every macOS installation has a Preboot volume with a UUID-named directory:
/System/Volumes/Preboot/<UUID>/System/Library/CoreServices/
OpenCore scans these Preboot directories to discover bootable macOS installations. Drop a .contentVisibility file in there, and you can tell specific OpenCore instances to skip that volume entirely.
Dead simple—one line, no whitespace, no trailing newline:
<InstanceIdentifier>:Disabled
In case more instances should be filtered it's a comma separated list (again no whitespaces):
<InstanceIdentifier1>,<InstanceIdentifier2>:Disabled
Say you've got:
- Mojave — Preboot UUID:
5174715E-A61F-444E-B527-E27CC7BBCE9C - Monterey — Preboot UUID:
CB2D58B1-D1BD-41B3-B0C3-FBE3AC031AE7
To hide Monterey from the Mojave OpenCore instance:
cd /Volumes/Preboot/CB2D58B1-D1BD-41B3-B0C3-FBE3AC031AE7/System/Library/CoreServices
echo "OC-MOJAVE:Disabled" > .contentVisibilityTo hide Mojave from the Monterey OpenCore instance:
cd /Volumes/Preboot/5174715E-A61F-444E-B527-E27CC7BBCE9C/System/Library/CoreServices
echo "OC-MONTEREY:Disabled" > .contentVisibilityNow when I boot via Mojave's OpenCore, only Mojave shows up. Boot via Monterey's OpenCore, only Monterey. No cross-contamination, no "oops wrong volume" moments.
The Preboot volume isn't mounted by default in a running macOS, so you'll need to do this from Recovery:
- Boot into macOS Recovery from OpenCore
- Open Terminal from Utilities menu
- Mount the Preboot volume:
diskutil apfs list diskutil mount diskXsY # Your Preboot identifier ls /Volumes/Preboot - Navigate to the
CoreServicesfolder for given volume and create the files as shown above - Unmount the volume
diskutil umount diskXsYand repeat the same process for every macOS installation - Reboot and verify each OpenCore instance only shows its intended volume
The setup scales pretty well. To add another version:
- Create
\EFI\OC\<NewVersion>with a complete OpenCore configuration tuned for that OS version - Set a unique
InstanceIdentifier(e.g.,OC-SONOMA) - Install macOS to a new partition
- Update
.contentVisibilityfiles:- Add the new identifier to existing installations' Preboot directories
- Create
.contentVisibilityin the new installation's Preboot, listing all other instance identifiers
- Add a rEFInd menu entry
The .contentVisibility management does get a bit tedious as you add more versions—each new installation means updating N existing files and creating one new file with N-1 entries. (Foreshadowing for a future "I should script this" post, probably.)
| Component | Purpose |
|---|---|
rEFInd at \EFI\BOOT\ |
First-stage boot manager, presents OC instance selection |
| Multiple OC directories | Version-specific configs, SMBIOS, kexts |
InstanceIdentifier |
Unique ID for .contentVisibility targeting |
ScanPolicy = 257 |
Limit to internal APFS drives |
.contentVisibility |
Hide volumes from non-matching OC instances |
So after all this—BIOS hands off to rEFInd, I pick an OpenCore instance, that instance shows only its designated macOS volume (with Recovery and other auxiliary stuff available via Space key), and I boot into a properly configured system. It's about as seamless as chaining two bootloaders can realistically be.
The setup preserves software update compatibility, doesn't require manual config swapping, and scales to additional macOS versions without architectural changes. Is it more complex than a single-boot Hackintosh? Obviously. But for a testing environment where I need multiple macOS versions with different configurations, it's the most maintainable solution I've found.
Still need to filter out the cross-instance Recovery entries from auxiliary mode, but that's a problem for future me. Same goes for the .contentVisibility file management. The core functionality works, and that's what matters.