What to Do When Multiple GitLab CI iOS Signing Jobs Conflict: 2026 Troubleshooting Guide
If signing or archive jobs interfere on a shared Mac, first serialize the signing resource, then verify Runner concurrency, workspace paths, and credential boundaries. This guide separates scheduler locks from host and credential isolation, gives you a decision matrix, and outlines a safe pre-release test.
Table of Contents
- Start with the failure boundary
- Resource groups coordinate jobs, not machines
- Runner concurrency and checkout paths
- Compare the controls before changing capacity
- Keychain access and credential boundaries
- Cross-project conflicts and release ordering
- Pre-release acceptance and routing decision
- Frequently asked questions
- Preventing simultaneous signing jobs
- Resource groups across projects
- Build directory collisions on a Mac Runner
- Verifying shared-Mac credential access
A signing job waits while another archive starts, or a release finishes with the wrong build state.
Fastest fix: serialize jobs that use the same signing resource, then check Runner concurrency, workspace paths, and credential access. A GitLab CI resource group controls eligible jobs; it is not a host-wide lock or an operating-system isolation boundary.
This guide is for iOS release owners managing overlapping signing or archive jobs.
It is also for platform engineers checking concurrent jobs on a shared Mac Runner.
IT and security teams can use the validation steps to test signing credential boundaries before production release.
Start with the failure boundary
Do not begin by changing certificates or lowering every Runner limit. First determine whether the conflict is in GitLab scheduling, the checkout workspace, or the signing environment. Those failures can look similar in a pipeline but require different controls.
Look for concrete evidence in the affected pipeline and job logs:
- Scheduling collision: signing jobs overlap, wait unexpectedly, or run in an order that does not match release policy.
- Workspace collision: a job reads files produced by another checkout, overwrites an archive, or sees cache contents that do not belong to its commit.
- Signing-state collision: the expected certificate or private key is unavailable, the wrong identity is selected, or Keychain access changes between jobs.
- Build failure without a collision: the same job fails when run alone, with the same commit and credentials. Investigate the build or signing configuration before blaming concurrency.
Record the pipeline and job identifiers, Runner assignment, commit, job start and finish times, and the signing-stage log. Redact secrets and private key material. Compare a failing run with an isolated run using the same inputs. Your team’s own records should determine whether this is a recurring conflict; do not assume a failure rate from a few incidents.
Also note exactly which job performs each action. A pipeline may install credentials in one job, archive in another, and publish in a third. A lock placed only on the archive job will not protect a shared credential setup or publishing resource if those stages also modify state.
Resource groups coordinate jobs, not machines
A resource_group is the first control to check when multiple jobs must not use the same logical resource at the same time. Add it to every job that must serialize around that resource, and use the same group name for those jobs within the relevant project. GitLab documents resource groups as a way to control concurrent jobs that share a group; its process mode determines how queued jobs are selected. GitLab’s resource group documentation describes the lock and process modes.
The documented process modes are unordered, oldest_first, newest_first, and newest_ready_first. Choose based on release semantics, not on which mode sounds fastest. If an older pipeline must never publish after a newer release, test the queue behavior against that rule. The resource group API documentation also exposes the process mode, which can help you verify the configured state rather than relying on an assumption about the YAML.
A resource group is not a global lock on the physical Mac. It does not by itself prevent another project from using the same signing host, stop a process started outside GitLab, create a unique checkout path, or restrict Keychain access. Treat it as a scheduler control with a defined scope.
When reviewing the pipeline, check:
- The signing, archive, or release job that actually touches the shared resource has the group.
- Related jobs that mutate the same signing state use the intended group as well.
- The group name is consistent where serialization is required.
- The selected process mode matches the order your release process needs.
- The job log and queue behavior confirm the lock is active during an overlap test.
The CI/CD variable security guidance is relevant when the group also handles credentials: a scheduler lock does not decide which jobs can receive a secret.
Runner concurrency and checkout paths
Even with a resource group in place, independent jobs may still run concurrently on the same Runner. GitLab Runner’s advanced configuration distinguishes a global concurrent setting from a per-runner limit; both affect how much work a Runner can accept. The Runner configuration reference documents these controls. Inspect the effective configuration on the Runner that handled the failing job, not just a template or a different host.
Next, review the build directory and checkout path. GitLab warns that concurrent jobs can conflict when they share a build directory or use paths that are not unique. Its Runner configuration guidance explains how concurrent job identifiers can be used in paths. Check the Runner documentation for build directory and checkout path configuration.
For a controlled test, run two non-production jobs that check out distinct commits at the same time. Have each job write a harmless marker file into its workspace and produce an archive with a unique test name. Then verify the resolved checkout paths, marker contents, archive ownership, and cache behavior. Do not use production signing credentials for this test.
Watch for these less obvious sources of overlap:
- A custom
GIT_CLONE_PATHthat points multiple jobs to the same directory. - A cleanup script that removes a path while another job is still using it.
- A shared cache key that causes one job to restore files created for a different build context.
- A build script that writes to a fixed archive or export path outside the checkout directory.
- A Runner setting that permits more jobs than the workspace layout was designed to support.
A lower concurrency limit can reduce overlap, but it is not proof that directories are isolated. Conversely, unique workspace paths do not serialize access to a shared signing identity. Test both controls separately so you know which boundary addresses the failure.
Compare the controls before changing capacity
Use the comparison below to choose the smallest control that addresses the evidence. “Fit” is a qualitative assessment, not a performance score.
| Option | What it controls | Best fit | Fit |
|---|---|---|---|
resource_group on signing jobs |
Scheduling among jobs using the same group in its scope | Same-project jobs that must serialize around signing or release state | Strong for scoped job ordering; weak for host isolation |
| Runner concurrency limit | How many jobs the Runner accepts at once | A Runner whose accepted workload exceeds what its workspace or signing setup can safely support | Conditional; reduces overlap but does not establish credential boundaries |
| Unique workspaces and output paths | Checkout and generated-file collisions | Jobs that can build in parallel but must not share directories or fixed output names | Strong for path collisions; does not serialize signing |
| Separate signing Runner or Mac | A boundary for a dedicated signing workload | Different trust domains, cross-project coordination needs, or persistent shared-host conflicts | Strongest operational separation, with added capacity and administration needs |
Do not treat the last option as automatically necessary. If the evidence points only to a shared checkout path, fix the path. If unrelated projects can reach the same signing account or host state, a project-scoped resource group is not enough; design a shared coordination mechanism or separate the signing workload.
Keychain access and credential boundaries
A signing job can have a scheduling lock and a unique workspace yet still use credentials that are available too broadly. Trace the certificate, private key, Keychain state, and CI service account separately. Identify which job receives each secret and which process can access the signing identity.
Apple documents Keychain access control through access-control lists, which govern access to protected keychain items. Use Apple’s Keychain access control documentation to review the relevant access model, and consult its code signing and verification documentation when checking how the signing identity is used.
Do not print secret values, private key contents, or unredacted credential files into job logs. Instead, use a dedicated test identity and record non-secret evidence: which job ran, which account executed it, which identity was selected, and whether an intentionally unauthorized test was denied. Review the CI variable scope and pipeline trust rules alongside the macOS account and Keychain configuration. GitLab’s pipeline security guidance provides additional controls for limiting exposure in pipeline execution.
A command returning success proves only that the command completed under the current conditions. It does not prove that another project, job, or process cannot access the same identity. Test the negative case: a job outside the allowed trust boundary should not receive the credential or be able to use it. Keep that test repeatable and reviewable without retaining secrets.
Important: Do not use production certificates to find out whether an isolation boundary works. Start with test credentials, then promote the control only after both permitted and denied access paths behave as intended.
Cross-project conflicts and release ordering
When separate projects share one Mac Runner, assume each project’s GitLab resource group is not a universal lock for the host. The Runner scope documentation explains how Runner assignment relates to project and group scope; Runner sharing and job coordination are separate questions. A Runner may accept jobs from a broader set of projects than the resource-group design was intended to coordinate.
Map the shared resource before choosing a remedy. Include the Runner, macOS account, checkout and output paths, signing identity, Keychain, and any publish destination. Mark which projects can schedule onto the host and which jobs can use the signing identity. If a release order spans projects, document the coordination mechanism that enforces it; do not infer a cross-project lock from similar group names.
Check the queue using actual pipeline records. Confirm whether an older pipeline can continue to the publishing stage after a newer one has started, and whether a retry can reuse or overwrite an output. Where the release process depends on artifact identity, retain and verify the artifact-to-commit relationship. GitLab’s job artifacts documentation describes artifact handling; your own pipeline should still verify that the artifact being published belongs to the intended commit and job.
Pre-release acceptance and routing decision
Use a non-production test to establish the fault boundary before editing the production release path. Run each test with recorded pipeline and job identifiers, redacted logs, and a clear expected result.
- Serialized signing: launch overlapping test signing jobs that use the same resource group. Confirm that the protected jobs do not execute together and that their queue order matches the configured process mode.
- Parallel ordinary builds: run independent builds with unique workspace and output paths. Confirm that checkout files, archives, and caches remain attributable to their own jobs.
- Failure and retry: make a test job fail after creating a harmless output, then retry it. Check that the retry does not publish a stale archive or consume another job’s files.
- Cross-project scheduling: run test pipelines from each project that can reach the shared Mac. Confirm whether a shared coordination layer actually serializes the signing work.
- Credential denial: use separate test credentials to confirm that an out-of-scope job cannot read or use the signing identity. Retain only redacted audit evidence.
Then select the remedy from the observed failure:
- Keep the shared Runner when job scheduling is controlled, workspaces are unique, and credential access has passed the boundary tests.
- Restrict Runner concurrency when the accepted workload is unsafe for the current workspace or signing setup, and verify that this restriction resolves the observed overlap.
- Add shared coordination when independent projects must honor one release order on the same host.
- Use a dedicated signing node when projects cross trust boundaries or when the shared host’s credential and state cannot be isolated to an acceptable standard.
For a team assessing whether remote Mac capacity could host a dedicated signing workload, first establish the required trust boundary, access model, and operational ownership. You can review VPSMAC’s available Mac node options as an infrastructure option, but do not assume a node is suitable until its actual access, delivery, and credential handling meet your acceptance criteria.
Frequently asked questions
Preventing simultaneous signing jobs
Put the same resource_group on every job that uses the shared signing resource, including the job that actually signs or publishes. Select a process mode that matches your release policy, then test simultaneous pipelines and inspect the queue. If unrelated projects share the Mac, add coordination across those projects or separate the signing workload; the project-level job lock does not isolate the host.
Resource groups across projects
Do not rely on matching resource-group names in separate projects as a cross-project lock for one Mac. Establish which projects can reach the Runner and where the signing state lives. If those projects need one release order, add an explicit shared coordination mechanism and prove it with concurrent test pipelines, or assign the sensitive signing work to a separate boundary.
Build directory collisions on a Mac Runner
Concurrent jobs can collide when their checkout or output paths resolve to the same location. Inspect the Runner’s build directory, any custom clone path, cache keys, cleanup scripts, and fixed archive destinations. Overlap harmless test jobs and compare their resolved paths and output ownership. Reducing concurrency may help, but unique paths are still necessary when jobs are intended to build independently.
Verifying shared-Mac credential access
Use test identities and check both allowed and denied access. Review the CI variables available to each job, the macOS account, and Keychain access controls. Keep logs and audit records free of secret values. A successful signing command only shows that the current job could sign; it does not establish that other projects or processes are unable to access the same credential.
If the test shows only a path collision, correct the workspace and output layout before buying more capacity. If the failure crosses project boundaries or exposes shared signing credentials, a shared Mac with only a job-level lock is not a sound long-term boundary; a dedicated Mac can give you a clearer operational separation, but it adds infrastructure and access-management work. For a temporary or pilot signing environment, compare that overhead with a remote Mac rental and review the actual node and access terms before committing through VPSMAC’s Mac node options.