Repository navigation
Expand file tree
/
Copy pathfleet.env.example
More file actions
458 lines (399 loc) · 22.8 KB
/
Copy pathfleet.env.example
File metadata and controls
458 lines (399 loc) · 22.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# Copy to fleet.env to use. That filename is ignored, because everything in here
# is specific to one host or one GitHub account.
#
# Every variable is optional, and on a normal runner host you need none of
# them: repos are read from the runner directories, groups are inferred from
# the repo names, and the owner comes from `gh`. What is left here is for
# overriding those when they get it wrong, plus the one value that genuinely
# cannot be derived (the launchd label prefix).
#
# PRECEDENCE: this file is sourced by the scripts, so it WINS over whatever is
# already in the environment. `FLEET_REPOS='a b' ./runs.sh` will not override a
# FLEET_REPOS set below — comment it out here instead.
# Prefix for the LaunchAgent labels of the two daemons (dashboard, watch),
# which become <prefix>.fleet-dashboard and so on.
#
# Set this BEFORE installing, and do not change it afterwards without
# reinstalling. launchctl identifies a loaded job by the label it was installed
# under, so editing this while agents are loaded leaves them running under the
# old label while the ctl scripts look for the new one — status and restart then
# report "not loaded" for a daemon that is working fine.
#FLEET_LABEL_PREFIX=com.runner-fleet
# GitHub owner — a user or an org — for repos referred to by bare name.
# Defaults to whoever `gh` is authenticated as.
#FLEET_OWNER=your-github-username
# Explicit repo list for runs.sh, whitespace- or newline-separated. Bare names
# get FLEET_OWNER prepended; owner/repo is taken as given.
#
# You almost certainly do not need this. On a runner host the list comes from
# the runner directories; on a machine with no runners, runs.sh asks `gh` which
# of the account's repos have Actions and caches the answer. Set this only to
# watch a specific subset, or repos belonging to someone else.
#FLEET_REPOS="my-ios-app my-web-app other-owner/shared-lib"
# Where runs.sh caches the discovered repo list, and for how long (seconds).
# The default is 6 hours, on the grounds that the answer changes when someone
# creates a repo while `--watch` asks the question every 15 seconds.
# `runs.sh --refresh` rediscovers now, whatever the age.
#FLEET_REPO_CACHE=.fleet-repos
#FLEET_REPO_CACHE_TTL=21600
# How many of the account's repos discovery will consider. Raise it for an
# account with more than 200.
#FLEET_REPO_LIMIT=200
# --- grouping -----------------------------------------------------------------
#
# The dashboard groups runners and repos so that app-ios/app-web/app-backend
# appear together under "app". This is INFERRED by default and needs no
# configuration: any first token shared by two or more repo names becomes a
# group, and repos that share nothing land under "other". The four variables
# below only exist for when the inference gets it wrong.
# Force these groups into existence and show them first, in this order.
# Everything inferred follows. Matched as a prefix, not a token, so "app" also
# claims "app2-web"; the longest matching pin wins.
#
# Use this to pin the two or three groups you look at most to the top, or to
# create a group whose repos do not share a prefix.
#FLEET_PROJECTS="app otherapp"
# Never infer these groups. Their repos fall through to "other".
#
# Worth setting for a token that is shared by accident rather than by
# convention — a `template-*` set of scaffolds, for instance, which is a real
# shared prefix but not a thing anyone wants a heading for.
#FLEET_GROUP_IGNORE="template scratch"
# How many repos must share a token before it becomes a group. Default 2.
# Raise it if a couple of unrelated repos keep forming a heading you do not
# want; ignoring that one token is usually the better fix.
#FLEET_GROUP_MIN=2
# Set to "off" to disable grouping entirely and show one flat list, which is
# the right answer for a handful of unrelated repos.
#FLEET_GROUPS=off
# ---------------------------------------------------------------------------
# Capacity and autoscaling live in the dashboard, not here
# ---------------------------------------------------------------------------
#
# The concurrency ceiling, the per-repo runner limit, the idle TTL and the
# autoscaler's on/off and dry-run switches are all editable on the dashboard's
# Capacity tab, and take effect on the next collector tick with no restart.
#
# PRECEDENCE, because it is the opposite of what you might expect: a value set
# here SEEDS the setting, it does not override it. Once a setting has been
# edited in the UI, the stored value wins and changing this file has no further
# effect on it until the row is reset from that tab. Anything else would mean a
# value edited in the UI silently reverting on the next restart.
#
# The tab shows which of the three layers — stored setting, this file, or the
# built-in default — each value came from.
# The one capacity knob it is worth seeding from here, because it decides how
# much of the machine CI may take and you may want it set before first boot.
# Default 3: on a 12-core host, two concurrent jobs already put the load average
# at five times the core count.
#FLEET_CEILING=3
# ---------------------------------------------------------------------------
# Job admission control — the one limit that applies to RUNNING jobs
# ---------------------------------------------------------------------------
#
# FLEET_CEILING above refuses to ADD a runner. It cannot stop a burst across
# runners that already exist: every runner listens independently, so if 29 of
# them are offered work in the same second, 29 jobs start. That is how this host
# recorded a load average of 760.
#
# These settings gate job EXECUTION, from hooks/job-started.sh. They must live
# in this file rather than on the Capacity tab: the hooks run inside a CI job's
# environment and cannot read the dashboard's database or reach its API, and
# making a job's ability to start depend on the daemon being alive would be a
# worse failure mode than the thrashing it prevents.
#
# Requires the hooks to be installed in each runner's .env:
# scripts/install-hooks.sh --apply --restart
# off — the default. The hooks exit immediately and nothing changes.
# observe — never delays a job, but records what enforcing WOULD have done.
# Run here first: the Capacity tab shows every decision, so the limit
# can be judged before it is able to cost a build.
# enforce — holds a job until there is room.
#FLEET_ADMIT_MODE=observe
# How many jobs may run at once. Default 3, matching FLEET_CEILING and for the
# same measured reason — median load1 on this host is 60 at two concurrent jobs
# and 91 at three.
#FLEET_ADMIT_MAX_CONCURRENT=3
# Optional stricter limit for Apple Simulator runners. Matching uses the same
# space-separated shell patterns as FLEET_SIMULATOR_RUNNERS below. This lets a
# host run (for example) one Xcode job plus one backend job while preventing two
# Xcode jobs from starving CoreSimulator's launch watchdogs. This limit remains
# strict after the general fail-open timeout. Zero or no matching
# FLEET_SIMULATOR_RUNNERS patterns disables it.
#FLEET_ADMIT_SIMULATOR_MAX_CONCURRENT=0
# LANES. With one host-wide limit a 5-second job waits behind 12-minute Xcode
# builds. Lanes put each job in `light`, `heavy` or `simulator` (a heavy job on
# a FLEET_SIMULATOR_RUNNERS runner) and give each lane its own limit and its
# own FIFO line, under a total cap. See docs/admission-and-scaling.md#lanes.
# off — the default: one lane, exactly as before.
# observe — the single lane still decides; every admission log line also
# records what the lanes would have decided (lane_would_hold,
# lane_reason, lane_wait_s). Run this first.
# enforce — the lanes decide.
#FLEET_ADMIT_LANES=observe
# The total cap the lanes apply (default FLEET_ADMIT_MAX_CONCURRENT). Its own
# setting so observe can judge the lanes at the total they will enforce while
# the single lane keeps its own limit; enforce is then FLEET_ADMIT_LANES alone.
#FLEET_ADMIT_TOTAL_MAX_CONCURRENT=16
# Heavy jobs at once (Simulator jobs count here too).
#FLEET_ADMIT_HEAVY_MAX_CONCURRENT=4
# Light jobs at once. FLEET_ADMIT_SHORT_MAX_CONCURRENT is accepted as an alias.
#FLEET_ADMIT_LIGHT_MAX_CONCURRENT=12
# Classification, first match wins: the Simulator runner rule, these patterns
# (space-separated `repo:job` shell globs; repo with or without its owner; job
# is GITHUB_JOB, the job's YAML id, `test` rather than `ios / test`), then the
# dashboard's history (/api/job-class: light when the job's recent median run
# is under FLEET_ADMIT_SHORT_JOB_S), else heavy. Quote the lists.
#FLEET_ADMIT_HEAVY_PATTERNS="*:e2e acme/app:test site:test"
#FLEET_ADMIT_LIGHT_PATTERNS="*:gate *:changes"
#FLEET_ADMIT_SHORT_JOB_S=90
# How long the hook reuses the dashboard's answer for one repo:job.
#FLEET_ADMIT_CLASS_CACHE_S=300
# Where it asks; defaults to /api/job-class beside FLEET_ADMIT_STATUS_URL, and
# an empty FLEET_ADMIT_STATUS_URL skips it too (every job is then heavy unless
# a pattern says otherwise).
#FLEET_ADMIT_CLASS_URL=http://127.0.0.1:7878/api/job-class
# How long a job may be held before the timeout action below. A held job is IN
# PROGRESS as far as GitHub is concerned, so this counts against its own
# `timeout-minutes`.
#FLEET_ADMIT_MAX_WAIT_S=600
# `admit` preserves fail-open behaviour after max-wait for host and disk limits.
# The optional Simulator limit always stays strict. `hold` keeps every limit
# strict and lets the job's own timeout/cancellation end the wait.
#FLEET_ADMIT_TIMEOUT_ACTION=admit
# Poll GitHub this often while held. This is what releases a runner when a
# cancelled run does not interrupt the sleeping job-started hook. Minimum 5s.
#FLEET_ADMIT_CANCEL_POLL_S=30
# Where that poll asks first. Runner LaunchAgents set SessionCreate, so a job
# has no keychain and `gh` there has no token; the dashboard daemon answers
# with its own. On an agent host, point this at the coordinator, or set it
# empty to go straight to gh and curl.
#FLEET_ADMIT_STATUS_URL=http://127.0.0.1:7878/api/run-status
# Refuse to start a job when the disk is this low, so a build fails on a clear
# reason rather than halfway through a link step. Matches minFreeDiskGb on the
# Capacity tab. Unlike the concurrency limit, waiting does not fix this unless
# cleanup.sh runs, so it relies on the bound above. Playwright browser caches
# can consume several GB per runner — keep FLEET_ADMIT_MIN_FREE_DISK_GB high
# enough to absorb an install mid-job.
#FLEET_ADMIT_MIN_FREE_DISK_GB=40
# The floor above counts what macOS can purge; this one is plain df free.
#FLEET_ADMIT_MIN_PLAIN_FREE_GB=15
# df: read both floors from plain df, ignoring purgeable space.
#FLEET_DISK_PROBE=df
# How often a held job re-checks. Rarely worth changing.
#FLEET_ADMIT_POLL_S=5
# Mutex spin attempts per wait-loop tick in enforce mode. The outer loop retries
# every FLEET_ADMIT_POLL_S; keep this modest so timeout and cancellation checks
# stay on wall-clock time. Enforce mode no longer bypasses the limit when the
# mutex is briefly contended.
#FLEET_ADMIT_MUTEX_TRIES=50
# Eligible held jobs are admitted FIFO. A Simulator waiter blocked by the
# Simulator-specific limit does not prevent an unrelated waiter from using free
# host capacity. Abandoned entries are reaped by hook-PID liveness.
# A slot whose owning Runner.Worker is gone is reclaimed immediately by PID
# check; this is the backstop for a PID that a reboot recycled. Six hours was
# GitHub's own default job ceiling.
#FLEET_ADMIT_SLOT_TTL_S=21600
# Shared path used by both job hooks and fleetd for admission decisions.
#FLEET_ADMISSION_LOG=/Users/you/actions-runners/dashboard/logs/admission.ndjson
# ---------------------------------------------------------------------------
# Standby tiers (coordinator only; docs/design/tiers.md)
# ---------------------------------------------------------------------------
# Run CI on this host first and bring a standby host's runners online only when
# this host is at its admission cap, has work queued with no idle runner, or is
# unfit. observe logs decisions without acting; enforce acts; off releases the
# controller's drains and stops. Rerun ./dashboard/fleetctl.sh install after
# changing any of these.
#FLEET_TIERS_MODE=off
#FLEET_TIERS_STANDBY_HOSTS=studio-1
# Standby runners for these repos are always online (the "floor").
#FLEET_TIERS_FLOOR_REPOS=app-ios,app-tvos
# This host's runners for these repos are never drained in favour of the standby.
#FLEET_TIERS_PRIMARY_ONLY_REPOS=app-backend
#FLEET_TIERS_PRIMARY_HOST=
#FLEET_TIERS_RESUME_AFTER_S=60
#FLEET_TIERS_QUEUE_AGE_S=120
#FLEET_TIERS_DRAIN_AFTER_S=600
#FLEET_TIERS_PRIMARY_STALE_S=180
#FLEET_TIERS_SELF_DRAIN=1
# Park twinless primary runners at cap, so held time is queue time, not job time.
#FLEET_TIERS_PARK=1
# Self-drain hysteresis: shed after the lane is at cap this long, come back
# after it has had room this long, hold at least / stay open at least this long.
#FLEET_TIERS_SELF_DRAIN_AFTER_S=60
#FLEET_TIERS_SELF_RESUME_AFTER_S=300
#FLEET_TIERS_SELF_MIN_HOLD_S=600
#FLEET_TIERS_SELF_MIN_OPEN_S=300
#FLEET_TIERS_BUILD_CAP=
#FLEET_TIERS_SIMULATOR_CAP=
# ---------------------------------------------------------------------------
# Periodic health repair (silent runner death)
# ---------------------------------------------------------------------------
#
# Runner LaunchAgents do not set KeepAlive. When RunnerService.js dies, launchd
# leaves the job loaded but dead and one repo's jobs queue forever. health.sh
# --repair fixes that; healthctl.sh installs a LaunchAgent to run it on a timer.
#
# Interval in seconds between repair sweeps. Default 60. Install or reinstall
# after changing: ./healthctl.sh install
#FLEET_HEALTH_INTERVAL=60
#
# Optional override for the repair log path. Default: logs/fleet-health-repair.log
# under FLEET_ROOT.
#FLEET_HEALTH_LOG=/path/to/fleet-health-repair.log
# The independent fleet watchdog always records outage/recovery transitions.
# Set this before `dashboard/watch/watchctl.sh install` to also post a local
# macOS notification when fleetd is unreachable and when it recovers.
#WATCH_NOTIFY=1
# ---------------------------------------------------------------------------
# Database backups and metrics
# ---------------------------------------------------------------------------
#
# Back up fleet.db before upgrades. Online backup is safe while fleetd runs:
# cd dashboard && ./fleetctl.sh backup
# Restore requires stopping the daemon first:
# ./fleetctl.sh stop && ./fleetctl.sh restore backups/fleet-YYYYMMDD-HHMMSS.db
#
# Where timestamped backups are written (mode 0600). Default: dashboard/backups/
#FLEET_BACKUP_DIR=/path/to/backups
#
# Snapshot age after which /api/health reports stale and Prometheus metrics
# mark the collector stale. Default 240000 (4 minutes).
#FLEET_COLLECTOR_STALE_MS=240000
# Shared control-plane state and automatic leader election. Leave unset for the
# normal single-coordinator SQLite mode. Put the PostgreSQL URL in this mode-0600
# file on each Mac rather than embedding a password in a LaunchAgent plist.
#FLEET_DATABASE_URL_FILE=/Users/you/actions-runners/dashboard/.fleet-database-url
#FLEET_DATABASE_SSL=1
#FLEET_DATABASE_SSL_INSECURE=0
# Stable and unique on every Mac.
#FLEET_REPLICA_ID=mac-mini-1
# ---------------------------------------------------------------------------
# Federation — multi-host coordinator and agent settings
# ---------------------------------------------------------------------------
#
# Single-host deployments need none of these. Set them to add agents and let
# the coordinator place new runners across multiple Macs.
# ---------------------------------------------------------------------------
# Remote access and mobile
# ---------------------------------------------------------------------------
# Bind address. Default 127.0.0.1 (loopback only — use SSH tunnel to reach it
# from another machine). Set to 0.0.0.0 to accept connections on the LAN.
# Use ./fleetctl.sh remote lan on|off to toggle without editing this file.
# Never expose this port to the public internet — use Tailscale Serve or a VPN.
#FLEET_HOST=0.0.0.0
# Extra host names or IPs that the Host-header allowlist should accept.
# Comma-separated. Add custom DNS names, reverse-proxy hostnames, etc.
#FLEET_ALLOWED_HOSTS=my-mac.example.com,192.168.1.50
# Tailscale detection is automatic. Set to off to disable it, or provide the
# CLI path when using a non-standard installation.
#FLEET_TAILSCALE=auto
#FLEET_TAILSCALE_BIN=/Applications/Tailscale.app/Contents/MacOS/Tailscale
# Path to the per-device token store (JSON, mode 0600). Each paired device gets
# its own revokable token; the master control token never leaves this machine.
# Use ./fleetctl.sh pair to generate a pairing code for a new device.
#FLEET_DEVICE_TOKENS_FILE=/path/to/.fleet-device-tokens.json
# ---------------------------------------------------------------------------
# ---- Coordinator settings --------------------------------------------------
# The coordinator's bind address is FLEET_HOST, under "Remote access and
# mobile" above. Agents on other Macs need it reachable (0.0.0.0 or a LAN IP).
# Capability labels that THIS coordinator host can satisfy. Used by the
# placement engine to route jobs that require specific software versions.
# Comma-separated, no spaces around commas.
# FLEET_HOST_LABELS=xcode-16,macos-15
#FLEET_HOST_LABELS=
# Separate token for agent heartbeats. Falls back to the control token if
# unset. Use this to rotate agent credentials without invalidating the browser
# token, or to give agents a less-privileged token.
# Generate: ./dashboard/fleetctl.sh agent-token
#FLEET_AGENT_TOKEN_FILE=/path/to/.fleet-agent-token
#
# Recommended for multiple hosts: one scoped token per host. The coordinator
# stores only SHA-256 hashes in this mode-0600 JSON file.
# Generate: ./dashboard/fleetctl.sh agent-token --host mac-studio-1
#FLEET_AGENT_TOKENS_FILE=/path/to/.fleet-agent-tokens.json
# ---- Agent settings (on agent Macs, not the coordinator) ------------------
# URL of the coordinator (set on agent hosts, not the coordinator itself).
#FLEET_COORDINATOR=http://coordinator-mac:7878
# HA: ordered comma-separated endpoints. The agent fails over automatically.
#FLEET_COORDINATORS=http://mac-mini:7878,http://mac-studio:7878
# Token the agent uses to authenticate to the coordinator.
# Generate on the coordinator:
# ./dashboard/fleetctl.sh agent-token --host mac-studio-1
#FLEET_AGENT_TOKEN=<paste token here>
# Stable token-scope and command-routing ID. Keep this unchanged across renames.
#FLEET_HOST_ID=mac-studio-1
# Display name for this host in the Hosts tab.
#FLEET_HOST_NAME=mac-studio-1
# Capability labels this agent host can satisfy. Same format as coordinator.
#FLEET_HOST_LABELS=xcode-16,macos-15
# Allow the coordinator to send drain/resume/health-check commands.
# Safe default is 0 (report-only).
#FLEET_AGENT_ALLOW_COMMANDS=0
# Allow the coordinator to register new runners on this host (implies
# FLEET_AGENT_ALLOW_COMMANDS=1). Set to 1 to enable remote provisioning.
# The agent re-checks local headroom and the per-repo cap before invoking
# register.sh, and treats an already-present runner as success (idempotent).
#FLEET_AGENT_ALLOW_REGISTER=0
# Destructive and separately opt-in: permit remote scale-down/deregistration.
#FLEET_AGENT_ALLOW_DEREGISTER=0
# Must match the coordinator's maxInstancesPerRepo setting.
#FLEET_MAX_INSTANCES_PER_REPO=4
# Repos scripts/mirror-runners.sh never copies to this host. For jobs whose
# needs no label states: an iOS lane on a plain `ci` runner, a backend suite
# that expects a local database. Bare names or owner/repo.
#FLEET_MIRROR_SKIP_REPOS="my-app my-backend"
# The Xcode every runner host builds with (xcodebuild -version, e.g. 27.0).
# join-host.sh fails preflight on a host whose Xcode differs.
#FLEET_XCODE_VERSION=27.0
# The PATH every job gets, on every host. register.sh writes it into each
# runner's .path (config.sh would otherwise copy the registering shell's PATH);
# scripts/runner-path.sh --apply brings older runners in line.
#FLEET_RUNNER_PATH=/opt/homebrew/bin:/opt/homebrew/sbin:/usr/bin:/bin:/usr/sbin:/sbin
# How often the agent sends a heartbeat (milliseconds). Default 30 000 (30s).
#FLEET_HEARTBEAT_MS=30000
#
# Multi-host rollout (recommended order):
# 1. Coordinator: install, set FLEET_HOST_LABELS, ./fleetctl.sh agent-token
# 2. One agent: FLEET_COORDINATOR + FLEET_AGENT_TOKEN + matching labels
# 3. Confirm Hosts tab heartbeats before FLEET_AGENT_ALLOW_COMMANDS=1
# 4. Enable autoscale dry-run, then live; remote register last
#
# Warm capacity / pre-warming: adding runners raises concurrency, not CPU.
# Headroom gates (FLEET_CEILING, FLEET_MAX_TOTAL_RUNNERS) still apply on each
# host. Forecast pre-warming stays locked until evaluation gates pass and never
# bypasses headroom or per-repo caps — see docs/design/capacity.md.
# ---------------------------------------------------------------------------
# Job audio — mute host output for simulator jobs
# ---------------------------------------------------------------------------
#
# Exact runner names, separated by spaces or commas. Apple simulators have no
# supported per-device mute and route app audio through the Mac. The job hooks
# save the host's mute state, mute before the first step, and restore after the
# last selected job. A guardian also restores it if a cancelled job skips its
# completed hook. Empty by default.
#FLEET_MUTE_RUNNERS="my-app-tvos"
# Shut down simulators created by selected CI runners after their final
# overlapping job finishes. Devices that were already booted before CI began
# are preserved. A Runner.Worker guardian performs the same cleanup when a
# cancellation or crash skips the completed hook.
#FLEET_SIMULATOR_CLEANUP=0
#FLEET_SIMULATOR_RUNNERS="*-ios *-ios-* *-tvos *-tvos-*"
# Remove System V shared memory segments that Postgres clusters leave when a
# job is cancelled or times out: owned by the runner user, attached by nobody,
# creator PID gone. macOS allows 32 segments host-wide, after which every new
# cluster fails in shmget. On by default; 0 turns it off.
#FLEET_SHM_REAP=1
#FLEET_SHM_LOG= # default: dashboard/logs/shm.ndjson
#FLEET_SHM_LOW_FREE=4 # log a "low" event below this many free;
# the dashboard's shm-low verdict uses it too
# Disk cleanup (cleanup.sh --auto). Below the pressure line it also erases CI
# simulators over the smaller size and idle runners' Playwright browsers.
# FLEET_CLEANUP_PRESSURE_GB=60 # default: FLEET_ADMIT_MIN_FREE_DISK_GB + 20
# FLEET_CLEANUP_RUNNER_CACHE_DAYS=7 # erase a runner's _work/<repo>/DerivedData-*, spm-*, .build-cache-* unused this long
# FLEET_CLEANUP_SIM_MAX_GB=3 # erase a runner's ci- simulator past this
# FLEET_CLEANUP_SIM_PRESSURE_GB=1 # ...and past this under pressure
# FLEET_CLEANUP_FULL_EVERY_S=86400 # full pass at least this often
# The timer that runs it: ./cleanupctl.sh install (also step 6 of join-host.sh).
# FLEET_CLEANUP_INTERVAL=900 # seconds between --auto ticks
# FLEET_CLEANUP_LOG= # default ~/Library/Logs/runner-cleanup.log