Use APFS VM Volume From Another Disk

mojavevirtual-memory

Due to the sad demise of my main machine, I'm booting a much less capable system from an external drive to keep myself going while I await a replacement. The machine itself has its own internal drive. Both the machine's own internal drive, and my recovered (now external) drive, are formatted APFS, and both have the full set of system volumes (preboot, recovery and VM).

The problem is, when booting from the external drive, macOS wants to store swapfiles on it, even though the performance is poor when doing this, especially since this emergency setup has a lot less RAM making swapping a lot more likely.

What I'd like to know is; can I tell macOS Mojave to mount the internal drive's VM volume and use that for swap-files, rather than using the external disk?

One oddity is that although the external drive has its own VM volume (not that I want to use it), macOS Mojave isn't utilising that either, instead it's storing swap-files under /vm, rather than mounting the VM volume at /private/var/vm as normal.

In case it's easier to visualise, my drives look like this, where disk0 is internal, and disk2 is external, and was pulled from my dead machine:

diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *320.1 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                 Apple_APFS Container disk1         319.9 GB   disk0s2

/dev/disk1 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +319.9 GB   disk1
                                 Physical Store disk0s2
   1:                APFS Volume Macintosh HD            34.4 GB    disk1s1
   2:                APFS Volume Preboot                 41.8 MB    disk1s2
   3:                APFS Volume Recovery                507.4 MB   disk1s3
   4:                APFS Volume VM                      20.5 KB    disk1s4

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk2
   1:                        EFI EFI                     209.7 MB   disk2s1
   2:                 Apple_APFS Container disk3         1000.0 GB  disk2s2

/dev/disk3 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +1000.0 GB  disk3
                                 Physical Store disk2s2
   1:                APFS Volume macOS                   102.5 GB   disk3s1
   2:                APFS Volume Preboot                 64.3 MB    disk3s2
   3:                APFS Volume Recovery                1.0 GB     disk3s3
   4:                APFS Volume VM                      20.5 KB    disk3s4
   5:                APFS Volume Users                   321.0 GB   disk3s5

To clarify; I'm not looking to disable swapping entirely, but to use the internal drive as the swap-file location. Indeed, swapping is necessary on the older machine I'm using, as it was never used for much more than a media-centre, and only has 4gb of RAM, which fills fast!

Best Answer

I believe I've found a solution to this thanks to an answer to a similar question, the process that is responsible for the swap-files is /sbin/dynamic_pager, which is triggered by a launchd task found at /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist. Since this file is located in /System it means that this solution requires System Integrity Protection to be disabled, and for Catalina you will need to perform the changes from your recovery volume.

First step is to take a backup of the original plist:

cp /System/Library/com.apple.dynamic_pager.plist /System/Library/com.apple.dynamic_pager.plist.orig

Now we want to modify the command that is executed. In the original it will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>EnableTransactions</key>
        <true/>
        <key>Label</key>
        <string>com.apple.dynamic_pager</string>
        <key>KeepAlive</key>
        <dict>
                <key>SuccessfulExit</key>
                <false/>
        </dict>
        <key>POSIXSpawnType</key>
        <string>Interactive</string>
        <key>ProgramArguments</key>
        <array>
                <string>/sbin/dynamic_pager</string>
        </array>
</dict>
</plist>

I modified my ProgramArguments section to the following:

        <key>ProgramArguments</key>
        <array>
                <string>/bin/bash</string>
                <string>-c</string>
                <string>diskutil mount -mountPoint /private/var/vm disk1s4 &amp;&amp; /sbin/dynamic_pager -F /private/var/vm/swapfile</string>
        </array>

This now runs two commands, the first to mount the internal VM volume at /private/var/vm and the second is a modified call to dynamic_pager telling it to create swapfiles at the new location.

NOTE: In my case the VM volume was disk1s4, and while this should be the case for most systems, you should double check with diskutil list first and modify the new command accordingly.

To activate the changes, either restart or, if you're sure you aren't using a swap-file at the moment, you can unload and reload the launchd task immediately:

launchctl unload /System/Library/com.apple.dynamic_pager.plist
launchctl load /System/Library/com.apple.dynamic_pager.plist

The new swapfile location should now be set, and if necessary you can remove any leftover swapfile(s) (in my case at /vm).

UPDATE: Simpler method

It occurred to me that there may be a simpler solution to this problem; since the dynamic pager launchd task doesn't supply a specific location for swapfiles, it seems it will choose in order of preference, with /private/var/vm being preferred if available. To this end it may be possible to force the use of the internal drive's VM volume like so:

  1. Get the VM volume's UUID (diskutil info disk1s4)
  2. Run sudo vifs
  3. Add a line like so: UUID=9e2cd41c-1566-11ea-9237-ef9cfb4e0fac /private/var/vm apfs rw,nobrowse,union swapping in your volume's UUID

This should cause your system to automatically mount your VM volume at /private/var/vm, and since this will occur before the dynamic pager runs, it should see and select this location, with no need to disable SIP or edit its launch daemon.

Related Question