Trimming system services

vphoned's trimmed service profile turns off about 140 launchd jobs a research VM has no use for: Siri and the intelligence daemons, Find My, Wallet/NFC/Secure Element, Watch, Home, health coaching, Maps background work, iCloud and iMessage sync, ads and recommendations, telemetry, accessory firmware updates and OTA. On an iOS 27.0 guest that cuts process memory by 29%, idle host CPU by 26% and idle host writes by 60%; on iOS 26.6.2, with the same list less what 26 does not load, by 30%, 39% and 94%. No vphone feature was lost on either. The profile is applied once, in the setup boot of a machine template, and every clone of the template inherits it.

services.profile and services.profile.apply are described in vphoned_http_api.md; the lists are in VPhoneDaemon/Daemon/GuestServiceProfile.swift.

How it works

The profile uses the override launchctl disable writes, which lives in launchd's database on the data volume (/private/var/db/com.apple.xpc.launchd/disabled.plist). launchd does not load a disabled job at the next boot. It is the same mechanism services.disable and the OTA block use.

  • It takes a reboot. services.stop and services.unload answer with launchd status 144 on the guest, so a running job keeps running until the guest restarts.
  • It is not a firmware patch. The system volume and launchd.plist are untouched, nothing goes into a patch set, and the Services panel still lists every job, disabled ones marked as such.
  • On iOS 27 it leans on one. launchd there ignores the override for the jobs the board's jetsam properties mark DisableAllowed = false, and system-jetsamproperties-cfw-disable_allowed removes that mark (iOS 27 and DisableAllowed).
  • It reverts cleanly. vphoned records the labels it disabled in /var/db/vphoned/service-profile.plist. profile: "none" turns back on those labels and no others. A job that was already disabled when the profile ran, such as the OTA trio, is never recorded and stays disabled.
  • The host has to ask. The host cannot write the data volume of a guest: it is encrypted with the guest's keys. So the profile is applied by vphoned from inside the guest.

What trimmed disables

GroupDefaultLabelsWhy they can go
baseon133Appendix A of the measurement below, less siriactionsd (list version 2), passd and financed (list version 3), generativeexperiencesd (list version 4). Nothing in vphone, IcliKit or the guest hooks talks to them
app_storeoncom.apple.appstored, com.apple.itunesstoredOff with the App Store and iTunes Store apps a template removes. appstorecomponentsd is already in base
signin_followuponcom.apple.followupd, com.apple.appleidsetupdThe sign-in follow-up and Apple Account setup prompts. They complement libsigninfix's pane suppression. A guest that went through setup.skip has no follow-up items, so no orphaned badge is left
accountsoff, groups: ["accounts"]com.apple.akd, com.apple.amsaccountsd, com.apple.appleaccountdOnly for guests that never sign in: with these off, the guest cannot sign in to an Apple Account

The base group, by area:

AreaExamplesKind
Siri and intelligenceassistantd, siriinferenced, intelligenceflowd, modelmanagerd, corespeechdNot used. corespeechd and modelmanagerd serve dictation. siriactionsd and generativeexperiencesd stay: see below
Photos and media analysismediaanalysisd, photoanalysisd, photosfacemediaanalysisd alone wrote 79 MiB in a 15-minute idle window
Ads, content and recommendationsamsengagementd, promotedcontentd, tipsd, weatherd, gamedamsengagementd was the top idle CPU and write consumer
Find Myfindmydeviced, searchpartyd, findmylocated, fmflocatordNo Apple Account
Wallet, NFC, Secure Elementnfcd, seld, seserviced, coreidvdNo NFC or SE hardware. passd and financed stay: see below
Watchnanotimekitcompaniond, nanoprefsyncd, nanobackupdNo paired watch. nanoregistryd stays (see below)
Home and healthhomed, homeeventsd, fitnesscoachingd, ThreadCommissionerServiceHomeKit apps under test need homed
Maps backgroundroutined, navd, destinationd, mapspushd, mapssyncdNot the location simulation path
iCloud, iMessage, mail syncidentityservicesd, imagent, maild, remindd, replicatord, syncdefaultsdNo account. Mail, Reminders and iMessage under test need theirs
Telemetryanalyticsd, triald, cloudtelemetryd, hangtelemetryd and other reporting daemonsUpload only. ReportCrash and osanalyticshelper stay, for logs.crashes
Accessory firmwareaccessoryupdaterd, uarpd and helpersNo accessories. accessoryupdaterd used 18 s of CPU at boot
OTAsoftwareupdateservicesd, mobile.softwareupdated, OTATaskingAgent, OTACrashCopierSame as the OTA block
Cellular extras and predictionWirelessRadioManager, carkitd, vmd, PowerUIAgent, duetexpertd, ospredictiondCommCenter itself stays

siriactionsd stays (list version 2, 2026-10-24). It was in the measured base list, but it also serves com.apple.siri.VoiceShortcuts.xpc, through which every Control Center control and App Intents widget serializes its intent (-[INAppIntent initWithAppBundleIdentifier:linkAction:linkActionMetadata:]). With it disabled, each such extension traps in WidgetKit (ControlAction.swift: Fatal error: Can't create CHSIntentReference from BarcodeScannerIntent: nilError), and Control Center draws Timer, Calculator, Camera, Code Scanner, Alarm, Magnifier and Voice Memo as blank circles. Seen on an iPhone Duo 27.1 guest; allowing the label and restarting restored every control. It is now in neverDisable, so trimmed no longer selects it and a guest that applies the profile again turns it back on.

passd and financed stay (list version 3, 2026-10-24). Wallet's first screen asks passd synchronously for its pass groups (-[PKPassLibrary _getGroupsControllerSnapshotWithOptions:synchronously:retries:handler:]), and passd waits on financed's Core Data store (com.apple.financed.service.coredatastore). With passd disabled, Wallet's NSXPCStore retried until FrontBoard's 20-second scene-create watchdog killed it; with passd running but financed disabled, the synchronous call never returned and the same watchdog fired. With both running Wallet opens; passd keeps retrying nfcd, which stays disabled, at about 0.02 CPU-seconds a minute.

generativeexperiencesd stays (list version 4, 2026-11-02). It serves com.apple.generativeexperiences.ExternalProviderService, and with it disabled BackgroundShortcutRunner reconnects to that service with no back-off (EPS failed to resubscribe to provider changes, NSCocoaErrorDomain 4099, after each failed bootstrap look-up), while launchd answers every look-up. On three idle iPhone17,3 27.0.1 guests built trimmed+accounts, BackgroundShortcutRunner used about 9 CPU-seconds and launchd about 4.5 in a 12-second window, and each VM held the host at about 130% CPU. With the label enabled and the guest restarted, the whole guest used 0.3 to 0.5 CPU-seconds per 10 seconds, the VM 17% or less, and generativeexperiencesd stayed quiet (#791). It is now in neverDisable, on 26 as well as 27.

Some of these are only optional: Shortcuts, dictation, HomeKit, Maps, Mail, Reminders, iMessage and Safari bookmark sync. A research target that uses one of those frameworks needs its daemon back. Pass that label in allow and apply again; the profile turns back on a recorded label it no longer selects.

iOS 26 and 27 have a list, both measured on iPhone guests (see iOS 26 for how the 26 list differs). vphoned picks the list by the major version alone, so an iPadOS guest of those versions gets the iPhone list. An iPad16,1 template builds with it on 26.6.2 and on 27.0.1 (iOS 27 and DisableAllowed); its idle cost was not measured there. Any other version gets "No trimmed service list".

Never disabled

GuestServiceProfile.neverDisable lists what the profile refuses to disable, whatever list or group names it. Five kinds of job were measured to make the guest worse when off, and are kept out of the lists for that reason:

JobWhat happens without itEvidence
com.apple.sleepdSpringBoard's and mobiletimerd's HealthKit sleep clients (HKSPXPCConnectionProvider) reconnect to com.apple.sleepd.sleepserver with no back-off5000 log lines in 3 s, host at about 400% CPU, launchd used 115 s of CPU and wrote 385 MiB in the first two minutes
com.apple.generativeexperiencesdBackgroundShortcutRunner reconnects to com.apple.generativeexperiences.ExternalProviderService with no back-offAbout 9 CPU-seconds per 12 s in BackgroundShortcutRunner and 4.5 in launchd; about 130% host CPU per idle 27.0.1 VM (#791)
com.apple.CommCenter, CommCenterMobileHelper, CommCenterRootHelperSpringBoard's SBTelephonyManager reconnects to com.apple.commcenter.xpc every second and resets the idle timer each time (updateIdleTimerForReason:"TelephonyManager:UpdateSB"), so the screen never turns offA 1 Hz loop in the log; backboardd grew from 99 to 160 MB; SpringBoard took 4418 wakeups in two minutes
com.apple.clouddtransparencyd (Retrying CK account state fetch) and ScreenTimeSettingsAgent keep retrying CloudKitEach wrote 50–60 MiB per 15 idle minutes, against nothing on the untrimmed guest. Idle writes rose above the untrimmed guest's, to 337 MiB per 15 minutes
com.apple.nanoregistryd, nanoregistrylaunchdlocationd waits synchronously on NanoRegistry's management queueA spindump of a hung locationd showed com.apple.nanoregistry.registry.managementqueue in dispatch_sync. 27.0's locationd also hangs and crashes on its own, so this is kept out of caution

The rest of the list is what vphone's own features and the tools it drives rely on. Each one was traced to vphoned, IcliKit, the guest hooks or a research note: SpringBoard, backboardd, runningboardd, lsd, containermanagerd, installd, misagent, lockdownd, remoted, cryptexd, remotepairingdeviced, avconferenced, diskimagesiod, locationd, bluetoothd (locationd calls it synchronously), pasted, powerd, amfid (com.apple.MobileFileIntegrity), profiled, keybagd, cfprefsd, notifyd, mDNSResponder, configd, timed, tzlinkd, securityd, logd, diagnosticd, ReportCrash, osanalyticshelper, audiomxd, mediaplaybackd, mediaremoted, cameracaptured, mobileactivationd, mobileassetd and storekitd (which the OTA and App Store notes require), and vphoned itself. dtremotedisplayd and dtdeviceinfod are not launchd.plist jobs on 27.0 and are out of reach anyway.

Two jobs looked like candidates and cannot be disabled this way. com.apple.siri.context.service and com.apple.siri.acousticsignature are XPC services, not LaunchDaemons, so the override is written and they run anyway.

Measurements

These numbers come from svctest-a, a --new-identity clone of an iPhone 27.0 (24A435) guest with cloudOS 26.4, 8 GB and 8 cores, started --headless after setup.skip (2026-10-07). Both runs used the same machine and disk. The baseline is its third untrimmed boot. Host figures are proc_pid_rusage of the VM process every 10 s. Guest figures come from processes.list, memory.jetsam and /usr/bin/taskinfo, run through a one-shot launchd job.

After 17 minutes idleUntrimmedTrimmed (137 + store)Change
Guest process footprint, summed1658 MB1177 MB−29%
Processes394263−33%
memorystatus_level (free memory)62%73%
Host VM process CPU, idle window6.5% of a core4.8%−26%
Host writes, 15 idle minutes220 MiB89 MiB−60%
Host writes, first 100 s of boot572–604 MiB363–423 MiBabout −35%
Host VM process footprint8.67 GB7.26 GB

Boot CPU varied more between runs (72–208 s in the first 100 s untrimmed, 44–138 s trimmed) than trimming changed it, so only the drop in boot writes is dependable. vphoned answered 5–14 s after start either way, because launchd starts it early. The host keeps most of the memory the guest frees, since the VM has no balloon device. The gain is headroom inside the guest.

A template set up with the profile (P0, 2026-10-07) showed the same effect on its clones. A clone's first 100 s wrote 372–375 MiB, against 1040 MiB for a clone of an unprepared template.

Function checks

These checks gave the same result trimmed and untrimmed: unlock, screenshot, apps.list (258), installing a small app with apps.install and launching it, Settings, clipboard, audio.state, starting the Camera app, uninstalling. SpringBoard kept the same pid through every run, and no new crash type appeared.

Not checked:

  • installd's own install path (ideviceinstaller).
  • Xcode and DeviceHub screen viewing. The jobs they need are kept.
  • Frida.
  • A real microphone path.
  • Location. 27.0's locationd hangs or crashes with EXC_ARM_PAC_FAIL on untrimmed guests too, and Location Services were off after setup.skip. On iOS 26.6.2, where locationd works, location simulation and Maps' blue dot work with the Maps background group and nearbyd off (iOS 26). On 27 it waits for locationd to be fixed.

Known side effect: with weatherd off, the default Home Screen's Weather widget reads "Weather Unavailable". Pass allow: ["com.apple.weatherd"] to keep it.

iOS 26

The iOS 26 list is the iOS 27 one mapped onto iPhone17,3 26.6.2 (23G90), measured on t26-a (cloudOS 26.4, 8 GB, 8 cores, --headless, 2026-10-09), a clone of a template built --service-profile none.

How it was derived:

  1. The LaunchDaemons and LaunchAngels of both IPSWs' launchd.plist, by Label: 685 on 26.6.2, 724 on 27.0.
  2. Of the 137 labels of the 27 base list, 132 exist on 26 under the same name. cloudtelemetryd, hybridsearchd, libsqlite3.dbtelemetryd, speechmaintenanced and visualintelligenced do not exist on 26, under that name or another.
  3. Of those 132, 131 load on the guest. safetyalertsd has a LimitLoadToHardware list of real devices on 26, so it never loads there and is left out, as the 27 list holds only jobs loaded at boot.
  4. The store, sign-in follow-up and account groups have the same seven labels on 26.
  5. Four jobs exist only on 26. com.apple.factory.NFQRCoded lives in /Developer and does not load; deviceinterfaced and systemactions load but did not run. com.apple.timesync.audioclocksyncd runs and was the guest's largest idle CPU user once trimmed (1.5 s and 14,000 context switches per 15 minutes, printing PTP clock statistics). With it disabled as well the host's idle CPU did not move (2.69% of a core either way), so it stays on.

So trimmed on 26 disables 131 + 2 + 2 = 135 labels (GuestServiceProfile.base26, notLoadedOn26). The never-disabled set needed nothing new: no job of the list made the guest worse when off, and the traps of 27 are kept on 26 as on 27 without being measured again.

After 17 minutes idleUntrimmedTrimmed (131 + store + follow-up)Change
Guest process footprint, summed1516 MB1061 MB−30%
Processes373230−38%
Running launchd jobs300179
memorystatus_level (free memory)65%74%
Host VM process CPU, 15 idle minutes4.43% of a core2.69%−39%
Host writes, 15 idle minutes301 MiB18 MiB−94%
Guest CPU, 15 idle minutes (taskinfo)19.1 s8.9 s−53%
Host writes, first 100 s of boot480 MiB305–315 MiBabout −35%
Host VM process footprint8.20 GB6.92 GB

One untrimmed and one trimmed run of the same machine and disk, the windows from 120 to 1020 s after start. Boot CPU was 72 s untrimmed and 48–75 s trimmed in the first 100 s, within the spread 27 showed. Most of the untrimmed idle writes came in one burst five minutes after boot. With the profile on, the screen still turned off, the logs stayed at 50–80 lines per 10 s after two minutes, and locationd started once on every boot.

Function checks, trimmed and untrimmed alike: unlock, screenshot, apps.list (236), installing a small app with apps.install, launching it, Settings, clipboard, audio.state, playing a WAV in Safari (21 s, 247 writes, none starved, host 47999.7/s), starting the Camera app (26.6.2 publishes the synthetic camera: libvcamcaptured appends its source to _sSourceList), location.set and location.current with Location Services turned on, and Maps showing the blue dot at the simulated coordinate, uninstalling. SpringBoard kept its pid throughout.

One difference showed in crash reports. On 2 of 16 trimmed boots, and none of 7 untrimmed ones, three control and widget extensions (CalculatorWidget, BarcodeScannerWidgetExtension, LauncherControlExtension) stopped in their first seconds in WidgetKit's ControlAction.init(_:) with "Can't create CHSIntentReference from" an App Intent. They are launched again on demand, and nothing visible was affected; which disabled job the intent reference needs was not found. SiriSearchFeedback crashed in both configurations.

iOS 27 and DisableAllowed

On a guest that presents its board, iOS 27 kept five of the profile's jobs running after the reboot, and a template's strict setup boot failed its last check (2026-10-13, iPad16,1 27.0.1 (24A446), bundle 2.9.3):

[setup] failed at verify: 5 profile service(s) still running:
com.apple.OTATaskingAgent, com.apple.analyticsd, com.apple.diagnosticextensionsd,
com.apple.mobile.softwareupdated, com.apple.symptomsd-diag

All 144 overrides were in place (services.profile → disabled). 139 of those jobs were not loaded at all; services.print showed the other five with force-enabled among their properties.

The cause is a key iOS 27 added to the jetsam properties, /System/Library/LaunchDaemons/com.apple.jetsamproperties.<target-type>.plist. Under Version4 → Daemon (and XPCService) → Override → <label>, 19 entries carry DisableAllowed = false: the five above and com.apple.DumpPanic, ReportCrash, ReportCrash.Root, ReportCrashService, logd, logd_helper, logd_reporter, mobile.obliteration, mobileactivationd, osanalytics.osanalyticshelper and its .agent, pluginkit.pkd, sysdiagnose and sysdiagnose_helper. launchd reads the key with the other JetsamProperties and, when it bootstraps a disabled job that has it, logs Ignoring disabled state because DisableAllowed=false and loads the job anyway. launchctl print then lists force-enabled.

Which file launchd reads follows the device tree's target-type:

  • An iPhone17,3 guest keeps VPHONE600. The profile and the OTA block were measured there, and a slimmed 27.0.1 template passes the same check without the patch, so nothing is force-enabled; the image presumably has no jetsam properties under that name, which was not looked at.
  • Every other guest presents its board (VPhoneGuestDevice.presentsBoard: J410 for the iPad16,1), gets that board's file, and with it the 19 marks.
  • iOS 26 has the files without the key: the J410 one on 26.6.2 (23G90) holds no DisableAllowed, and services.dump on that guest shows no force-enabled job. A trimmed iPad16,1 template builds there unchanged.

system-jetsamproperties-cfw-disable_allowed removes every DisableAllowed = false from each com.apple.jetsamproperties.*.plist during cfw install and cfw update-environment, for iOS 27 only. It keeps a .bak beside each file and puts it back when the patch is turned off. Nothing else in the files changes, and the key has no effect on a job that is not disabled.

Two things did not work from inside the guest. The system volume is read-only there, so vphoned cannot edit the file. And services.remove unloads the three of the five that live in the user domain but is refused for com.apple.analyticsd and com.apple.OTATaskingAgent, which are in the system domain ("remove failed: launchd status 3 (No such process)").

Validated on 2026-10-13 with the patch, on iPad16,1 27.0.1 (24A446):

  • The failing guest after cfw update-environment ("19 DisableAllowed = false removed" for each of J410 and J411): no force-enabled job, none of the 144 labels loaded, services.profile → running empty.
  • A new machine from nothing (vphone-launchpad-cli vm create --device iPad16,1 --slim on): every step passed, the strict setup boot in 87 s (settled after 64 s, 144 services owned, verify passed), and the clone of template 0c8682d7c241 reaches the Home Screen with the same three answers.

The same creation on iPadOS 26.6.2 (23G90) passed with bundle 2.9.3, which does not have the patch: 138 services owned, setup boot in 90 s, template 65cb509bf161.

An iOS 27 guest restored by an older bundle has the marks until an environment update or a reinstall runs with the patch. Until then the five jobs keep running, and so does softwareupdated on a guest whose OTA block was applied by hand.

Applying it in a template

A template is changed once, in its setup boot, and frozen; every clone inherits the overrides and the record. The order matters:

  1. setup.skip.
  2. setup.settle, until first-boot app expansion is done.
  3. Remove the system apps.
  4. services.profile.apply {profile: "trimmed", force: true}. The signin_followup group belongs after the first-boot work, which step 2 already waited for. Every override waits for the reboot anyway, so one call covers both the services and the sign-in follow-up.
  5. Reboot, and check that no job the profile owns is running (services.profile → running empty).
  6. Shut down and freeze.

Reverting

On a running guest:

vphone-launchpad-cli guest rpc <vm> services.profile.apply '{"profile":"none","force":true}'
vphone-launchpad-cli vm stop <vm> && vphone-launchpad-cli vm start <vm> --wait

That turns back on only what the profile disabled. To keep the profile but bring back one job, apply trimmed again with that label in allow. To bring back the Apple Account daemons on a guest that had accounts, apply trimmed without that group.

Idle cost of vphone's own components

With the profile applied, the largest idle cost left in a guest was vphone's own plumbing: timers in the guest hooks that ran whether or not anything used the feature behind them. Measured 2026-10-09 on two clones of one trimmed iPhone 27.0 (24A435) template, --headless, 15 idle minutes (902 s) starting three minutes after boot, screen off and locked, no camera, audio or API client. Guest figures are /usr/bin/taskinfo differences (CPU time and interrupt wakeups per process), run through a one-shot launchd job; host figures are the CPU time of the VM's Virtualization service and of vphone-vm.

Per 15 idle minutesBefore (2 runs)After (2 runs)Source
cameracaptured wakeups / CPU27,212–27,960 / 2.1–3.1 s953–956 / 0.1 slibvcamcaptured's 30 Hz frame drive ran for the daemon's whole life; it now runs only while a viewfinder stream or video sink exists (virtual_camera_transport.md)
audiomxd wakeups / CPU21,670–22,548 / 1.8–2.6 s944–952 / 0.1 sThe virtio sound speaker's 1/24 s flush timer kept firing after the boot sound stopped; it is now disarmed when the stream stops (virtio_sound.md)
AccessibilityUIServer, InputUI wakeupsabout 905 each0–7libcamfix's 1 Hz preview-layer scan on the main queue of every app-path process with AVFoundation; it now starts with the first running vcam session
SpringBoard wakeups / CPU1,603–1,606 / 0.8–1.0 s714–717 / 0.3 sThe same scan, in SpringBoard
backboardd wakeups788–817419–433libvphonegyro republished its status through cfprefsd every two seconds; now only while samples are dispatched (virtual_gyroscope.md)
vphoned wakeups315–32838–40The 3 s device.state poll ran with no subscriber; it now runs only while an event socket is open
All guest processes, wakeups62,873–64,39612,324–12,374−81%
All guest processes, CPU11.2–20.4 s7.7–7.8 s
Host VM service CPU26.4–33.5 s (2.9–3.7% of a core)20.2 s (2.2%)
Host vphone-vm CPU1.9–2.1 s0.3 sThe 3 s health probe read and hashed the 9 MB vphoned on the main thread each time; the hash is now kept until the file changes (VPhoneFileDigestCache)

What is left at the top is Apple's own: mDNSResponder (about 1,900), locationd (1,100), cameracaptured and audiomxd (about 950 each with nothing of ours running in them), clipserviced, fseventsd and backboardd.

remotepairingdeviced is quiet on a guest that was never paired: 30 wakeups and under 0.01 s of CPU per 15 minutes, 0.12 s in its first 18 minutes, no log lines in a minute. No vphone code talks to it. The 3.5–8 s measured earlier came from a clone of a DeviceHub test machine that had been paired with this Mac's CoreDevice, so that cost follows pairing between Apple's daemons on both sides. An unpaired guest that is unlocked gets a "Trust This Computer?" alert from the Mac shortly after; answering it is what pairs it.

Checked after the change: playback through Safari (91 s, 1,073 writes, none starved, host rate 48000.1/s) on a boot whose speaker had already started and stopped once, so the timer was re-armed; audiomxd fell back to 1.6 wakeups a second when it stopped. The gyroscope configuration still reaches the provider and provider_running stays true while idle. device.state arrives about a second after an event socket opens and again after an unlock; vphoned took 45 wakeups a minute with a socket open and 7 without. A guest with an older vphoned was still updated by vphone-vm over HTTP. Not checked live: camera frames, because the iPhone 27.0 guest publishes no synthetic camera (libvcamcaptured logs _sSourceList not located on every boot, before and after), so no client can reach the drive; and the microphone, whose code did not change.

Open items

  • iPadOS. vphoned picks the list by major version only, so iPad guests on 26 and 27 get the iPhone lists. Templates build with them (iOS 27 and DisableAllowed), but no iPad guest's idle cost or function checks have been measured.
  • Other board-presenting iPhones on iOS 27. They read their board's jetsam properties as an iPad does, so the same patch should apply; only the iPad16,1 was run.
  • WidgetKit control extensions on iOS 26. The ControlAction.init(_:) crashes on 2 of 16 trimmed 26.6.2 boots (iOS 26) point at a disabled job an App Intent reference needs; which one was not found.
  • Location on iOS 27. Location simulation under the profile is verified on 26.6.2 only; on 27.0 it waits for locationd itself to work (locationd startup deadlock).
  • Host memory. The VM has no balloon device, so the guest's freed memory stays with the VM process (8.67 → 7.26 GB on 27.0). Returning it would take VZVirtioTraditionalMemoryBalloonDevice and a guest that drives it, which was not evaluated; giving trimmed machines less memory is the simple way to use the headroom.
  • Sample size. Each configuration's idle figures come from one or two 15-minute windows, and bursts of maintenance work that dasd schedules (mediaanalysisd, cloudd, searchd) dominated some of them. They show the direction, not a precise rate.