Your pipeline has pending work, but GitLab reports no available macOS Runner.

Fastest fix: Sign in to the Mac as the account that owns the runner, confirm its user-level LaunchAgent is running, then verify the runner can connect to GitLab. A self-managed GitLab macOS Runner relies on a logged-in user session. Don’t enable automatic login until you’ve assessed the security trade-off.

This guide is for digital nomads and remote developers restoring iOS builds after a Mac restart, independent developers whose jobs need a user session or simulator environment, and small teams deciding who will maintain a self-managed runner.

Separate a scheduling problem from an offline host

Start in the GitLab project before changing anything on the Mac. “No available runner” can describe different failures: the runner may be offline, the job may request tags that the runner does not have, or the runner may not be available to that project. Each calls for a different fix.

Open the project’s CI/CD runner settings and check whether the intended runner is listed, available, and assigned at the right scope. Then open the pending job and compare its tags with the runner’s tags. GitLab’s runner tag matching rules require a runner to match the tags requested by a job. A runner can appear healthy and still be ineligible for that job.

Check the registration scope as well. A project runner, group runner, or instance runner may not be available in the project you are inspecting. The Runner API fields can help distinguish a runner’s status and configuration from what a particular job can use.

Why can a GitLab macOS Runner be offline after a restart?
The Mac may have restarted without logging the runner’s user back in. Because the macOS service uses a user-level LaunchAgent, the runner’s availability depends on the applicable user session. If that account is logged out, focus on session and service startup before editing job tags.

If GitLab shows the runner as online but the job stays pending, investigate eligibility first. If GitLab shows it offline, move to the host and check the account session and runner process.

Check the Mac’s user session and LaunchAgent

A LaunchAgent is associated with a user session. It is not interchangeable with a system-level LaunchDaemon. GitLab’s macOS installation instructions describe its runner service as a LaunchAgent and explain its relationship to the logged-in user. Do not convert it to a daemon based on the assumption that any background service will behave the same way.

On the Mac, connect through your usual remote access method and sign in with the account used to install the runner. Then check the service using the GitLab Runner command available in that account’s environment:

gitlab-runner status

If the command is not found, check whether you are using the expected account and whether the runner executable is on that account’s PATH. A command run from a different shell, account, or installation location may not tell you whether the installed service is healthy.

Use launchctl to inspect the logged-in user’s launch services if the status command is inconclusive. Avoid guessing a service label: inspect the installed launch agent and identify its actual label before querying it. GitLab’s Runner command reference documents the available runner commands. Use the matching command for the installed version rather than copying a command intended for a different service setup.

How do you confirm that the GitLab macOS Runner has started?
Check the runner’s service status on the Mac, then confirm its status in GitLab. Both checks matter: a local process can be running while the runner cannot reach GitLab, and a GitLab status alone does not prove that a new job can execute.

If the service is stopped, start it using the documented command for your installation. If it stops again, check the runner’s logs and the account session before repeatedly restarting it. Record the time of the restart, the time the user signs in, the service state, and what GitLab displays. Those observations show whether the failure occurs during login, service startup, or connection.

Logout, reboot, and disconnect are different tests

Do not infer logout behavior from an SSH disconnect. Closing an SSH client does not, by itself, prove that the runner’s graphical user session ended. Likewise, rebooting tests whether the host and service return after startup; it does not isolate what happens when a user logs out. Check the actual account and service state for each event.

Will the runner keep working after you log out of the remote Mac?
Do not assume so. The GitLab macOS service is a user-level LaunchAgent with a user-session dependency. Confirm what happens in your deployment by observing the logged-in account, service status, and GitLab runner state after a controlled logout. If your jobs need a GUI session, simulator, or other user-bound resources, test those jobs separately too.

Decide whether automatic login is an acceptable recovery method

GitLab’s macOS installation documentation discusses automatic login as a way to have the user session available after a restart. That can help a user-session-dependent runner return without someone signing in manually. It is a recovery option, not a universal recommendation. Apple’s automatic login guidance explains how the setting affects access to the Mac.

Before enabling it, consider who can physically access the host, what credentials and project secrets are available to the account, and what controls protect remote access. Automatic login changes the point at which a local session becomes accessible. It does not remove the need to secure accounts, limit credentials, and review the host’s physical-access arrangements.

Recovery option What to verify Choose it when Main trade-off
Sign in manually, then start the LaunchAgent The runner account is logged in; service status and GitLab status recover A person can restore the session after planned maintenance or restart Jobs may wait for someone to sign in
Enable automatic login The runner returns after a controlled restart; local access and credential exposure are acceptable Unattended recovery is necessary and the security owner approves the exposure A user session becomes available without a manual sign-in
Keep a fallback build path A real project build completes on the alternative path and artifacts are usable The primary Mac cannot meet recovery or security requirements The fallback needs its own setup and maintenance

If you cannot accept automatic login and cannot provide a person to restore the session, do not call the runner “unattended” simply because its service is installed. Use a tested fallback or change the operating model.

Trace the failure when the runner is online but jobs still fail

A visible online state is only one checkpoint. Separate the workflow into three outcomes:

  • Runner service started: the local runner service is running.
  • GitLab job accepted and executed: GitLab scheduled a job to that runner and the job began.
  • Xcode build passed: the job found the required developer tools, project inputs, permissions, and signing setup, then completed the intended build.

A failure at one checkpoint does not prove the next one works. First compare the job’s tags with the runner’s tags and recheck the runner’s project or group availability. Then inspect the job trace for the first concrete error rather than treating every failed pipeline as a runner outage.

Next, verify the job runs under the expected account and can read its checkout, scripts, certificates, and other required files. Avoid printing secret values in logs. Check the active developer directory with:

xcode-select -p

Use Apple’s Xcode command-line tools instructions to confirm the tools installation and selection process. If a build depends on additional Xcode components, consult Apple’s additional Xcode components instructions. A successful path check does not prove that every project dependency or signing requirement is ready; run a representative project job and inspect its trace.

For an iOS build, include the real steps that matter to your release flow. A runner that accepts a simple shell job may still fail when a project needs a particular Xcode installation, simulator, signing identity, or access to protected variables. Fix the first failing step, then rerun the project job. Don’t change runner registration, user permissions, and Xcode selection all at once; doing so makes the result harder to diagnose.

Run a restart test before trusting the recovery plan

Use a controlled maintenance window and record each observation. This acceptance test checks the host, session, runner, scheduler, and build as separate stages.

  1. Record the baseline. Note the runner account, service installation method, runner tags, registration scope, and current GitLab status. Confirm which project job you will use for the test.
  2. Restart the Mac. Do not treat a successful shutdown or a persistent remote connection as proof of recovery.
  3. Check the login state. Verify whether the runner account has an active user session. If your plan depends on automatic login, test only after the relevant security review.
  4. Check the service. Run the documented status check as the runner account. If the service is absent or stopped, record that before taking corrective action.
  5. Check GitLab connectivity and eligibility. Confirm the runner appears available and that the test job’s tags and project scope match.
  6. Submit a representative CI job. Confirm the runner accepts it, the job starts, and the job trace shows execution on the expected Mac.
  7. Validate the build result. Run a real project build and confirm the expected output, not just a green shell command.
  8. Test logout separately if it matters. Observe the session and runner state after logout. Record whether manual sign-in is needed before jobs resume.

The test passes only when you know which recovery steps are automatic and which require a person. If the service returns but jobs remain pending, revisit tags, scope, and connection. If jobs run but Xcode fails, keep the runner diagnosis separate from the build-environment diagnosis.

Choose self-managed recovery or a fallback path

Keep the self-managed runner if a user session can be restored predictably, the project can reach the runner, and a representative build passes after your planned restart procedure. Adjust the recovery plan if the runner works only after manual login but your team expects unattended jobs. Choose another build path if automatic login is unacceptable and there is no reliable person or fallback to restore the session.

For a digital nomad, the hidden cost is often not the runner command itself. It is the time-sensitive maintenance window, access to the right account, safe handling of signing material, and the risk that a laptop or network interruption leaves nobody available to restore the session. A runbook should name who can sign in, how they verify recovery, and what happens if that person is unavailable.

A remote Mac can keep a macOS build environment accessible while you travel, but it does not automatically fix LaunchAgent behavior, CI configuration, or Xcode failures. You still need to validate the runner and your project’s recovery steps. If you are comparing that approach with carrying a Mac, review MACCOME’s remote Mac access options alongside your own hardware and fallback requirements.

If your current arrangement depends on a travel laptop staying available, a stable connection from each location, and manual intervention whenever the runner account loses its session, those are real operational costs. Renting a remote Mac through MACCOME’s remote Mac option may suit you when you need an accessible macOS host without carrying your MacBook. It is not a substitute for testing your GitLab Runner, and it may not fit workloads that need local physical interfaces or a permanently controlled on-premises machine. Choose it only after your restart test shows that a remote Mac matches the way your team needs to recover.