Your GitLab CI Mac Runner appears online, but you still do not know whether it can safely run your Apple-platform jobs or recover after logout.
You can install GitLab Runner on macOS, but treat the Shell executor as a limited-isolation, maintenance-mode option. Start on a dedicated Mac with trusted jobs, then accept it only after a real Xcode build and a restart test pass.
Who this is for: iOS and macOS developers moving builds or tests into GitLab CI.
DevOps engineers responsible for registering the runner and validating its execution and recovery.
Platform and security owners deciding whether the node’s trust and isolation boundaries are acceptable.
SECTION 01Before deployment: admit the Mac node
Separate Apple-platform work from general CI orchestration before you provision a runner. Linux can continue to handle jobs that do not need macOS. Reserve the Mac for work that depends on Apple’s toolchain, such as Xcode builds or tests that must run on macOS.
GitLab documents installing Runner on macOS and using the Shell executor for iOS and macOS builds. But Shell does not provide the isolation of a container boundary. GitLab marks it as being in maintenance mode, and its security guidance warns that jobs run with the permissions of the runner user. Check the GitLab Shell executor documentation and executor status information before deciding to use it.
Can GitLab Runner be installed on macOS? Yes. GitLab provides a macOS installation and configuration path. That confirms support for installing the Runner; it does not prove that your project’s Xcode build, signing setup, or recovery behavior works on your node. Use the official macOS Runner installation guide as the deployment reference, then validate your own workload.
Do not treat an existing developer’s Mac as a ready-made shared runner. A personal account may hold unrelated credentials, cached repositories, signing assets, or interactive applications. A job that can run arbitrary scripts may be able to affect more than its checkout directory.
A practical case: your cross-platform pipeline already builds and packages code on a Linux host, but its iOS target needs Xcode. Keep general jobs on the existing host and route only the Apple-specific stage to a dedicated Mac. That keeps the change bounded and makes the new node easier to remove if validation fails.
Use this admission test before you install anything:
- [ ] The Mac is dedicated to CI or otherwise isolated from personal work.
- [ ] You know which projects and branches may send jobs to it.
- [ ] The runner account does not have access to credentials beyond those required for its assigned jobs.
- [ ] You can pause scheduling and redirect or disable the jobs if the node fails.
- [ ] Your security owner accepts Shell executor’s limited isolation for this workload.
- [ ] You have a plan for cleanup of workspaces, logs, and credentials between jobs.
If a box is unchecked, keep the node out of shared production scheduling. A runner that is technically installable is not automatically an acceptable execution boundary.
SECTION 02Before registration: prepare the toolchain and account
Choose a dedicated macOS account for the Runner. Record its purpose, its permitted projects, and whether it needs access to a graphical login session. Keep general developer activity out of that account. This makes job ownership and workspace cleanup easier to review.
Next, install the Xcode application or command-line tools your project actually requires. Apple documents the command-line tool installation process and the available Xcode command-line tools. Use Apple’s installation instructions and Xcode command-line tool reference to confirm the supported commands and setup for your selected toolchain.
Do not infer that Xcode is ready because the application is present. Inspect the active developer directory and the selected tool version from the same account that will run CI:
xcode-select -p
xcodebuild -version
Save the command output in your deployment record. If your job expects a full Xcode installation, check that the active developer directory points to that installation rather than assuming that command-line tools alone will provide every required component. Then run a project-level command, such as listing the project’s schemes, under the runner account.
| Node option | Strengths | Risks and limits | Suitable when |
|---|---|---|---|
| Existing shared developer Mac | No separate host to prepare | Personal files, credentials, active sessions, and CI jobs share an environment | Only for a controlled, short-lived test that uses trusted code |
| Dedicated Mac with Shell executor | Clearer ownership and a bounded CI purpose | Shell jobs still lack strong executor isolation; the user session affects availability | Trusted Apple-platform jobs on a node approved for this boundary |
| Different supported execution topology | May better fit stronger separation requirements | Needs its own compatibility and operations review | Shell’s isolation limits do not meet the project’s security needs |
This is a security and operations choice, not a performance comparison. Do not assign concurrent jobs or capacity expectations without measuring your actual project on the chosen node. If rental is one of your options, use the MACNOX pricing page to compare the available commercial terms with the cost of preparing and maintaining a dedicated machine; verify the current details on that page rather than estimating from assumptions.
SECTION 03During registration: install the GitLab Runner macOS service
Follow GitLab’s macOS installation instructions for the installation method you use, then register the Runner with the intended GitLab instance and project or group scope. GitLab’s Runner registration guide describes the registration flow. Use the current token provided through your GitLab configuration flow. Do not place a real token in a public script, build log, or example pipeline.
For a Shell executor, set a specific tag that describes the Mac’s intended use, such as a project-approved Apple build tag. Restrict jobs to that tag. A broad tag that unrelated projects can request defeats the purpose of limiting the node’s scope. Review the GitLab self-hosted Runner security guidance before granting projects access.
macOS service behavior needs special attention. GitLab documents its macOS Runner as a user-level LaunchAgent that depends on a logged-in user session; it is not a LaunchDaemon. This changes the recovery plan. Do not assume that a service starting at boot can run CI work while the user is logged out.
Will the Runner keep working after logout or a Mac restart? Do not assume so. A user-level LaunchAgent depends on that user’s session, and a reboot also interrupts active work. Test the exact logout, login, and restart sequence on your node. Automatic login can change the availability pattern, but it also changes the physical-access and account-security risk. It is not a default security recommendation.
| Service state or event | What to verify | Deployment decision |
|---|---|---|
| Runner installed | The expected binary and service configuration are present | Continue only after confirming the intended account owns the service |
| User logged in | The Runner process is active and can accept a tagged job | Proceed to a controlled pipeline test |
| User logged out | Whether the Runner stops or can no longer accept work | Treat the node as unavailable until the team accepts the dependency |
| Mac restarted | Login requirement, service recovery, and job scheduling after recovery | Record the observed behavior; do not infer it from the online indicator |
After registration, verify the runner’s scope and tags in GitLab. Confirm that only intended projects can target it. Keep a written rollback path: stop scheduling to the runner, disable or remove its registration as appropriate, and revoke any credentials that should no longer be available to it.
SECTION 04During the first job: prove the Xcode execution path
Start with a small pipeline that tests the path from GitLab scheduling to commands running under the intended macOS account. Use placeholders for your project details and runner tag:
macos-check:
tags:
- <MAC_RUNNER_TAG>
script:
- whoami
- pwd
- xcode-select -p
- xcodebuild -version
- xcodebuild -list -project "<PROJECT_PATH>"
Keep the first job limited to inspection. Confirm that the job lands on the intended runner, checks out the expected repository, reports the expected account and working directory, and can invoke the required Xcode tools. Review the job log and compare its toolchain output with the record you captured from the runner account.
How do you confirm that CI uses the expected Xcode? Check the output of xcode-select -p and xcodebuild -version inside the CI job, not only in an SSH session or the macOS interface. Then run a real build using the project’s intended scheme and destination. A runner marked online proves only that GitLab can see a runner; it does not prove that the job was scheduled, that Xcode was selected correctly, or that the build passed.
Once the inspection job passes, try a representative build with your real project settings. Replace the placeholders with your repository’s actual values:
xcodebuild \
-project "<PROJECT_PATH>" \
-scheme "<SCHEME>" \
-destination "<DESTINATION>" \
build
For a workspace-based project, use the appropriate workspace argument instead of the project argument. Preserve the build log and record the scheme, destination, active developer directory, and Xcode version reported by the job. Do not mark the node accepted based on a successful shell command alone if your production pipeline also depends on tests, simulator use, or signing.
Use this evidence ladder to diagnose failure without conflating different states:
- Runner visible in GitLab: registration and connectivity are present.
- Job assigned to the intended tag: scheduling and scope are working.
- Repository checkout succeeds: the job can access its source.
- Xcode checks match the approved toolchain: the CI account resolves the intended tools.
- Real build or test completes: the project’s selected workflow works on the node.
- Logs and outputs are reviewed: the result is attributable and repeatable enough for your acceptance decision.
A failure at any stage has a different remedy. A job that never schedules points toward tags or scope. A job that starts but cannot find Xcode points toward the account’s tool selection or installation. A build failure after tool detection requires project-level diagnosis; it is not evidence that the Runner installation itself is broken.
SECTION 05Before expanding access: review credentials and job isolation
Shell executor jobs run on the host rather than inside a strong per-job container boundary. Treat each job script as code executing with the runner account’s host permissions. GitLab’s Shell executor documentation and security guidance make this boundary central to the decision.
Review repository access first. The runner should fetch only what its assigned projects need. Check which branches and contributors can trigger jobs, who can edit pipeline scripts, and whether untrusted changes can reach the Mac. If you cannot establish that code is trusted, do not schedule it on a Shell executor with access to signing material or other sensitive host resources.
Then inspect the host state that survives a job:
- Workspace residue: confirm how the project directory is reused or cleaned, and test whether a later job can read files left by an earlier job.
- Keychain access: determine which keychains are available to the runner account and whether the job can unlock them.
- Signing assets: limit certificate and provisioning-profile access to jobs that require signing; do not expose them to general build scripts.
- Logs and artifacts: check whether tokens, environment variables, or signing details can appear in output retained by GitLab or copied into artifacts.
- Account scope: verify that the runner account cannot reach unrelated personal data or administrative secrets.
Stop expansion if the node is shared with personal work, accepts untrusted pipeline code, or exposes high-privilege signing assets to jobs that do not need them. Also stop if your team expects a Shell executor to act like a disposable isolation container. It does not provide that boundary. If the risk is not acceptable, assess another supported execution topology before assigning more projects or credentials.
Which executor should an Apple-platform pipeline use? Use Shell only when your jobs are trusted, the node is dedicated or otherwise appropriately scoped, and the team accepts the host-level isolation trade-off. If those conditions do not hold, do not select Shell merely because it is the documented macOS route for Xcode work; assess another supported topology against your security and operations requirements.
For mixed pipelines, keep general compile, lint, and orchestration work on the existing non-Mac runners where possible. Send only the steps that require Apple tools to the Mac. This reduces the amount of code and credentials exposed to the Mac node, but it does not replace access controls or job review.
SECTION 06At acceptance: retest recovery and decide whether to go live
Run recovery checks before treating the node as an available CI service. Observe the Runner while the macOS user is logged in, then test the logout behavior your team expects. Restart the Mac and repeat the check after the required login step. Record whether GitLab sees the runner, whether a tagged job is actually scheduled, and whether that job reports the expected account and toolchain.
Do not hide a failed recovery check by changing automatic login settings without an explicit security review. If unattended availability is necessary, document the trade-off between the operational requirement and the risk of keeping a user session available on the host. If your policy does not permit the needed session behavior, keep the node out of production scheduling and choose a different operating approach.
Use this acceptance checklist before enabling routine jobs:
- [ ] The runner is scoped to the intended project or group and uses a restricted tag.
- [ ] A job checks out the repository and runs as the expected macOS account.
- [ ] The CI log shows the expected developer directory and Xcode version.
- [ ] A representative project build or test completes with the intended settings.
- [ ] Workspace residue, Keychain access, and signing assets have been reviewed.
- [ ] Untrusted code cannot reach the runner or its sensitive credentials.
- [ ] Logout and restart behavior have been observed and documented.
- [ ] The team can stop scheduling work and follow the rollback plan.
| Observed result | Decision |
|---|---|
| Trusted workload, approved Shell boundary, real build passes, recovery behavior accepted | Begin a limited trial with the approved projects and tags |
| Build passes, but logout or restart interrupts required service | Keep the runner out of unattended production use until the session plan is accepted |
| Signing assets are exposed more broadly than intended, or untrusted code can run | Stop; reduce access or choose a different execution topology |
| Runner is online, but scheduling, Xcode selection, or project build is unverified | Do not call deployment complete; fix the failing acceptance stage |
The lowest-risk rollout is a limited trial with a real project and a clear owner. Expand access only after the team has reviewed logs, credentials, recovery behavior, and the fallback path. If the Mac’s login-session dependency or Shell isolation limit conflicts with your service requirements, pause the rollout rather than treating a green runner status as production acceptance.
A Linux CI host can remain the right place for general jobs, but it cannot replace macOS where the workflow needs Xcode. A shared personal Mac avoids a separate host but mixes CI state with user data and credentials. Buying and maintaining a dedicated Mac gives you direct hardware control, but creates a fixed provisioning and maintenance responsibility. For a temporary trial or a dedicated remote Mac environment, review MACNOX’s available Mac options after your security boundary and first real-project test pass; choose a rental only if its access model, session behavior, and operating terms fit your acceptance criteria.