Xcode 26 build database locked: How To Fix Remote Builds in 2026?

Symptom: Xcode 26 build database locked appears after overlapping Build, Test, or Archive jobs.

Fastest fix: stop jobs sharing the same build directory, assign each job its own DerivedData path, then clean only the affected project after all related xcodebuild processes have exited.

This guide is for you if you run xcodebuild over SSH on a remote Mac and a disconnect or retry may have left work running. It also fits small teams running parallel CI jobs on one Mac, and maintainers who share one host for Build, Test, and Archive.

Do not start by deleting all caches. First preserve the evidence, isolate the active writers, and identify the exact database path.

Capture the failure before changing the machine

A short final error is not enough to diagnose this failure. Save the complete command, timestamps, working directory, resolved build paths, process owner, and the first meaningful error. A redacted example should still retain its structure:

2026-09-18T09:14:02Z
cwd=/Users/runner/work/ExampleApp
job=ios-release-1842
scheme=ExampleApp
command:
xcodebuild \
  -workspace ExampleApp.xcworkspace \
  -scheme ExampleApp \
  -configuration Release \
  -derivedDataPath /Users/runner/ci/derived/ios-release-1842-attempt-1 \
  -resultBundlePath /Users/runner/ci/results/ios-release-1842.xcresult \
  build

2026-09-18T09:14:07Z
error: database is locked

Keep the actual project name, username, host address, Bundle ID, Team ID, and private log content redacted when sharing the incident. Do not redact the path shape or the job identifier. Those details reveal whether two jobs resolved to the same location.

Record what each process was doing:

  • Job A: Build, Test, or Archive.
  • Job B: retry, manual build, or a separate pipeline stage.
  • Parent process: shell, CI runner, script, or xcodebuild.
  • Child process: test runner, package tool, signing step, or another build command.
  • Output state: .xcresult, .xcarchive, dSYM, exported package, upload task, or incomplete log.

Apple describes the build system as a dependency-driven system that schedules target work and tracks build products. Use the official Xcode build system documentation as the baseline for interpreting target execution rather than assuming every line in the log represents an independent top-level job.

Establish a timeline

Create a short timeline before terminating anything:

Time Evidence to record Decision
09:14:02 First job starts in the redacted workspace Keep if it is the intended release job
09:14:05 Retry or second job starts Check whether it resolves the same build path
09:14:07 First valid database lock error Preserve this log line and command
09:14:10 onward Parent and child processes remain active Inspect ownership before stopping

The first valid error matters more than a later cascade. A failed Archive may be a consequence of a database conflict, not proof that signing, source code, or the distribution configuration is broken.

Separate process conflicts from path conflicts

A lock can come from two distinct conditions. Multiple processes may be writing the same database, or one process may be using a stale or damaged build state after an abnormal exit. In remote environments, both conditions often appear together.

Check for real concurrent writers

Inspect the process tree using the account that owns the build:

pgrep -af 'xcodebuild|XCBBuildService|xctest|swift|clang'
ps -axo pid,ppid,user,lstart,command | \
  egrep 'xcodebuild|XCBBuildService|xctest|swift|clang'

These commands are evidence-gathering steps, not a recommendation to kill every matching process. Link each process to its parent shell, CI runner, workspace, scheme, and start time. A stale process after an SSH disconnect may still be producing a valid result. A second process may be the job you actually want to keep.

Stop in this order:

  1. Allow the unwanted job's own cancellation handler to run.
  2. Wait for child test and packaging processes to exit.
  3. Confirm that the parent xcodebuild process has ended.
  4. Use a targeted termination only when normal cancellation fails.
  5. Recheck the process tree and output directories.

Stopping a Build can discard its partial logs. Stopping Test can invalidate the test result bundle. Stopping Archive can leave an incomplete .xcarchive, while stopping a later upload task can create uncertainty about delivery status. Never treat a process exit as proof that the artifact is usable.

Important: Do not terminate a release job merely because it is old. First decide whether its Archive, dSYM, result bundle, or upload step is the deliverable you need to preserve.

Check the paths each job actually resolves

The path shown in a CI variable may not be the path used by the final command. Inspect the command line, generated shell script, scheme settings, and build settings. Compare:

  • -derivedDataPath or the GUI-selected DerivedData location.
  • OBJROOT for intermediate object files.
  • SYMROOT for build products.
  • -resultBundlePath for the .xcresult bundle.
  • -archivePath for the .xcarchive.
  • Export and upload directories.

Apple's build settings reference explains the role of these settings. The operational question is simpler: do two jobs write to the same resolved directory at the same time?

A safe arrangement gives each concurrent job a unique temporary workspace while preserving final artifacts separately:

Build item Safe parallel policy Keep after the job
DerivedData Unique per job and attempt Usually no, unless debugging
OBJROOT Unique with the job's build state Usually no
SYMROOT Unique when jobs can overlap Only selected products
xcresult Unique per invocation Yes for test diagnosis
xcarchive Unique per release attempt Yes for distribution recovery
dSYM Tied to the matching Archive Yes for symbolication
Upload log Unique and immutable Yes until delivery is verified

The acceptance test is not merely “the command returned.” Run two jobs at the same time, confirm that their resolved paths differ, and verify that each produces its own result bundle. If both jobs still write to one location, the isolation is incomplete.

Make parallel CI paths explicit

A CI job should construct its build path from a stable identifier, not from a shared project-level default. The exact variable names depend on your runner, but the pattern is portable:

set -euo pipefail

JOB_KEY="${CI_JOB_ID:-local}-attempt-${CI_JOB_ATTEMPT:-1}"
ROOT="$PWD/.ci"
DERIVED="$ROOT/derived/$JOB_KEY"
RESULT="$ROOT/results/$JOB_KEY.xcresult"
ARCHIVE="$ROOT/archives/$JOB_KEY.xcarchive"

mkdir -p "$DERIVED" "$ROOT/results" "$ROOT/archives"

printf 'derivedDataPath=%s\n' "$DERIVED"
printf 'resultBundlePath=%s\n' "$RESULT"
printf 'archivePath=%s\n' "$ARCHIVE"

xcodebuild \
  -workspace ExampleApp.xcworkspace \
  -scheme ExampleApp \
  -configuration Release \
  -derivedDataPath "$DERIVED" \
  -resultBundlePath "$RESULT" \
  build

For an Archive, use the same job-specific DerivedData path but a separate archive path:

xcodebuild \
  -workspace ExampleApp.xcworkspace \
  -scheme ExampleApp \
  -configuration Release \
  -derivedDataPath "$DERIVED" \
  -archivePath "$ARCHIVE" \
  archive

Do not assume that setting -derivedDataPath alone isolates every output. A script can override paths, a scheme can point to a shared location, or a later export step can reuse one directory. Log all important paths at the beginning and end of the job.

Apple's documentation on customizing build schemes is relevant when Build, Test, and Archive actions use different settings. Compare the scheme used by the GUI with the one used by xcodebuild; a visually similar action can still resolve different configuration values.

Inspect hidden nested builds before disabling parallel work

A parent xcodebuild process may start another build through a Run Script phase, package tool, dependency project, or publishing script. This creates a different problem from two independent CI jobs, because the child may inherit the parent's workspace and output paths.

Search build scripts and logs for:

xcodebuild
swift build
make

Then answer four questions:

  1. Which process owns the nested command?
  2. Is the nested build required, or is it duplicating a target dependency?
  3. Which process owns its outputs?
  4. What happens if the child command fails or is cancelled?

Review Run Script input and output declarations rather than simply turning off parallel builds. Apple documents running custom scripts during a build, including how script phases participate in build evaluation. A script with missing or inaccurate output declarations may run more often than intended, but changing the declaration does not automatically make concurrent writes safe.

Add explicit logging around nested calls:

echo "script start: $$"
echo "workspace: $PWD"
echo "derived data: ${DERIVED:-unset}"
echo "nested build boundary: begin"

# Run the nested command only if this stage owns it.

echo "nested build boundary: end"

The stopping condition is clear: the main log must show that the nested command no longer starts unexpectedly, and every child output must belong to a defined owner. If you cannot explain the boundary, do not re-enable parallel jobs yet.

Clean only after all writers have stopped

When no related xcodebuild, test, indexing, or nested build process remains, decide whether the lock is a stale state or a damaged database. Start with the smallest affected scope:

  1. Preserve source, signing assets, logs, .xcresult, .xcarchive, dSYM, and upload records.
  2. Move the affected job's temporary directory aside instead of deleting it immediately.
  3. Recreate that job's DerivedData directory.
  4. Rerun the identical command with a single job.
  5. Expand cleanup to the project-specific DerivedData only if the error remains.
  6. Recreate the workspace checkout only when path or generated-state evidence justifies it.
  7. Avoid deleting all user caches as a first response.

The distinction between intermediate data and release artifacts is essential. DerivedData is disposable only after you have confirmed that the Archive and diagnostic bundles are stored elsewhere. Apple's guidance on creating distribution-signed code for macOS also reinforces why signing and distribution outputs must be treated as release assets, not as disposable build intermediates.

After cleanup, rerun the same workspace, scheme, configuration, and path arguments. Changing several variables at once prevents you from knowing whether the lock was actually fixed.

FAQ: remote build database recovery

The following answers cover the common search paths without treating community reports as universal proof. A forum example can identify a useful symptom, but the process tree and resolved paths on your host remain the deciding evidence.

Can a clean DerivedData directory affect signing?

It should not remove certificates or provisioning assets by itself, but cleanup can expose a separate configuration problem. Verify the signing identity, provisioning selection, archive path, and export settings after the build succeeds. Preserve the original Archive and logs before cleanup, then compare the new Archive rather than relying only on a successful Compile or Build action.

Should you disable parallel build execution?

No. Disabling parallel execution can hide a shared-path or nested-build defect without fixing ownership. Use it temporarily as a diagnostic control if you need to prove that concurrency is involved. The durable repair is to give each job independent paths and correct the script or dependency boundary that creates overlapping writes.

What should you do after an SSH session drops?

Reconnect to the same host, identify the user and parent process associated with the original job, and inspect its output. If it is still progressing, let it finish. If it is stale and unwanted, cancel it in an orderly way, verify child processes have ended, and preserve any valid result bundle or Archive before cleanup.

Validate recovery in layers

Do not jump directly from a successful single Build to a production upload. Use a staged acceptance run:

Stage Test Pass condition Stop if
1 Single-job Build Same command completes with a new isolated path Database lock returns
2 Parallel Build or Test Each job has separate DerivedData and result paths Any path overlaps
3 SSH disconnect and reconnect Job ownership and logs remain recoverable Orphaned processes cannot be identified
4 Job cancellation Child processes exit and temporary paths remain traceable Cancellation leaves active writers
5 Host restart recovery Old jobs do not resume against stale shared state Startup leaves unknown writers
6 Real Archive A complete .xcarchive, dSYM, and log are retained Archive or symbols are incomplete

For Test, preserve the .xcresult bundle and use Apple's test results documentation when interpreting failures. For CI workflows that build packages or apps, compare your runner's path and artifact handling with Apple's continuous integration guidance.

Use this checklist before reopening parallel production jobs:

  • [ ] The original command and first valid error are archived.
  • [ ] The owning parent and child processes are known.
  • [ ] No unrelated xcodebuild process was terminated.
  • [ ] Every concurrent job has a unique DerivedData path.
  • [ ] OBJROOT, SYMROOT, result, and Archive paths were checked.
  • [ ] Nested build calls have an explicit owner and output boundary.
  • [ ] Source, signing assets, Archives, dSYM, and logs are preserved.
  • [ ] A single Build succeeds after targeted cleanup.
  • [ ] Parallel Test produces separate result bundles.
  • [ ] SSH disconnect, cancellation, and host restart behavior were tested.
  • [ ] A real Archive was inspected before upload.

If the issue recurs only when one Mac carries unrelated projects, the next decision is operational rather than cosmetic. Limit concurrency when the host cannot provide clean ownership. Split release work onto a dedicated runner when artifact retention or process control is unreliable. A remote Mac environment is suitable only when you can control paths, users, processes, and persistent storage.

Choose the next remote Mac operating model

Your current setup may be a local Mac, a shared remote Mac, or a CI runner that you manage yourself. Compare the failure surface before changing tools:

Option Best fit Main control Main weakness
Shared local Mac One developer and mostly sequential work Direct access to files and processes Local disk, uptime, and manual retries can interfere
Self-managed CI Mac A team needing custom runners and persistent tooling Full runner and script control You own upgrades, cleanup, monitoring, and recovery
Isolated remote Mac Temporary or continuous builds needing a persistent host Remote access with root-level environment control Requires disciplined path and process isolation
Hosted build service Standardized workflows with limited host control Managed orchestration Custom debugging and host-level recovery may be restricted

Before choosing another host, run the parallel-job, disconnect-reconnect, and restart-recovery tests above. If your present machine cannot provide independent work directories, complete process control, or reliable always-on access, review KVMFLUX remote Mac use cases and compare the available remote Mac plans.

A shared Windows or Linux workstation cannot provide the same native Xcode build environment, while an improvised shared macOS directory leaves the database conflict unresolved. Buying a dedicated Mac gives you physical control but ties up capital and still requires you to design CI isolation. Renting a Mac through KVMFLUX is the more flexible choice when you need a temporary build host, a persistent iOS packaging machine, or a separate environment for testing recovery behavior without buying another computer.

The decision is conditional: keep the current host if it passes isolated-path and recovery acceptance; split the runner if concurrency is the recurring cause; choose a rented remote Mac when you need controlled, always-available macOS access without committing to another physical machine.

Further Reading

Run Your Remote Builds on a Dedicated Mac

Deploy a dedicated KVMFLUX Mac mini M4 to isolate build processes, caches, and project paths from other jobs. Connect over SSH for automated builds or VNC when you need the full remote macOS desktop. Choose a daily, weekly, monthly, or quarterly KVMFLUX plan that matches your build workload. Select a nearby region and get administrator access to your remote Mac within minutes.

Mac Mini M4 · 16GB / 256GB
Daily$19.3 /day
Weekly$52.2 /wk
Monthly$96.7 /mo
Quarterly$263 /qtr