Quick answer: Don’t delete the output directory before building. Unity uses the previous .manifest files to skip unchanged bundles. For real incremental control, switch to the Scriptable Build Pipeline (Addressables uses it).

CI builds 600 asset bundles every time even when nothing changed. Build wall-clock: 22 minutes. The fix is to stop deleting the output and to switch to SBP for fine control.

The Symptom

Bundle build takes the full duration each time. CI logs show every bundle being processed. Cache hit rate (visible in Console with verbose logging) is near zero.

What Causes This

Legacy BuildPipeline.BuildAssetBundles uses the output directory’s previous .manifest files to determine what already-built bundles can be reused. If you wipe the directory before building, every bundle is “new” and Unity rebuilds everything.

The Library/AssetBundleCache cache also helps but only when the build pipeline trusts the prior output.

The Fix

Step 1: Preserve output between builds.

// Bad — rebuilds everything
Directory.Delete(outputPath, recursive: true);
Directory.CreateDirectory(outputPath);
BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions.None, target);

// Good — uses cache
if (!Directory.Exists(outputPath))
    Directory.CreateDirectory(outputPath);
BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions.None, target);

Step 2: Cache the Library/ folder in CI. Library/AssetBundleCache holds intermediate hash data. Cache it across CI runs (GitHub Actions cache, Fly volumes, etc.) for 5–10x faster builds on consecutive commits.

Step 3: Switch to Scriptable Build Pipeline. Add com.unity.scriptablebuildpipeline via Package Manager. Use ContentPipeline.BuildAssetBundles instead.

var input = ContentPipeline.GenerateBundleBuilds(...);
var parameters = new BundleBuildParameters(target, group, outputPath);
var result = ContentPipeline.BuildAssetBundles(parameters, new BundleBuildContent(input.results), out var results);

SBP gives you proper incremental builds, deterministic outputs (same content => same bytes), and CacheServer integration.

Hash Stability

Bundle hashes are computed from referenced asset bytes, dependencies, and the bundle name. Renaming a referenced texture changes the bundle hash even though the texture bytes are the same. Be careful with case sensitivity (Foo.png vs foo.png) on cross-platform CI — Linux is case-sensitive and may produce different hashes than your Mac/PC dev box.

Verifying

Build, change one file, build again. Console output (verbose) should mention “Bundle X up to date.” Time should be a fraction of the full build. If still rebuilding everything, the cache or the output isn’t being preserved.

“Don’t wipe output. Cache the Library. SBP for real control. Builds get fast.”

Related Issues

For Addressables handle leak, see handle leak. For Android NDK errors, see NDK errors.

Preserve output. Cache Library. SBP. Fast builds.