How would you use the Cache task in Azure Pipelines to speed up dependency installation, and what determines whether your cache key is actually effective?
Short Answer
Use the Cache task keyed on a hash of your dependency lockfile (e.g., key: 'npm | "$(Agent.OS)" | package-lock.json'), pointing at the package manager's download/store directory as the cached path — the key must change exactly when your actual dependencies change (via the lockfile hash) so the cache is reused whenever nothing's changed and correctly misses, falling back to a full install, the moment the lockfile does change.
Detailed Explanation
The Cache task's effectiveness and safety both come down entirely to the cache key design — a key that's too static risks serving genuinely stale content after a real dependency change; a key that changes too often (or on the wrong thing) gives you no caching benefit despite the added complexity.
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(npm_config_cache)
displayName: Cache npm packages
- script: npm ci
displayName: Install dependencies
Include the lockfile's content in the key, not just its filename: Azure Pipelines' Cache task supports keying on file content (effectively hashing the specified file), which is what actually ties the cache to your real dependency set — if the lockfile's content changes (a version bump, a new dependency), the key changes and the cache correctly misses, forcing a fresh install rather than silently reusing stale cached packages.
Include the OS/agent image in the key if your pipeline runs on multiple agent types — dependencies that involve compiled native modules can produce OS-specific artifacts, and a cache built on one OS restored on another can cause subtle, hard-to-diagnose failures; keying on $(Agent.OS) keeps caches scoped correctly per platform.
Use restoreKeys as a fallback, not a substitute for a precise primary key — a partial match (same OS, different lockfile) can still warm part of the cache for a faster partial restore, but the actual install command (npm ci, or equivalent) should still run and reconcile against the current lockfile, rather than trusting a restoreKeys fallback hit as if it were a complete, current cache.
Cache the package manager's own store/download directory, not the fully-installed dependency tree directly, letting the normal install command still do its real dependency resolution and linking against the cached downloads — this is generally more robust than trying to cache and restore an entire installed node_modules-equivalent directory as-is.
Interview Follow-Up Questions
- How would you debug a build that appears to be using a stale cache despite a lockfile-hash-based key?
- How would you measure whether your caching strategy is actually saving meaningful build time, versus adding overhead for a marginal benefit?
- How would this caching strategy change for a monorepo with multiple independently-versioned lockfiles?
Key Takeaways
- The
Cachetask's safety and effectiveness depend entirely on the key design — hash the actual lockfile content so the cache correctly invalidates exactly when dependencies genuinely change. - Include the agent OS in the key if running across multiple platforms, since compiled/native dependencies can produce OS-specific artifacts that shouldn't cross-contaminate caches.
- Use
restoreKeysfor partial-match fallback warming, not as a substitute for the install command still reconciling against the current lockfile. - Cache the package manager's download/store directory rather than a fully-installed dependency tree, letting normal install resolution still run against the cache.
References
Related Questions
- You have near-identical build logic copy-pasted across 15 Azure Pipelines YAML files. How would you use templates to share it, and what are the different template types actually for?SuggestedIntermediate
- When would you store a pipeline secret in an Azure Pipelines variable group directly versus linking the variable group to Azure Key Vault?SuggestedIntermediate
Last updated August 22, 2026 · Last reviewed August 22, 2026