How do you configure multiple YAML pipelines in one Azure DevOps repository with path-based triggers?
Quick Answer
You create separate YAML pipeline files (e.g., pipelines/api.yml, pipelines/worker.yml) in the same repository and register each as a distinct pipeline in Azure DevOps. Each file specifies path-based trigger filters so only changes to relevant directories trigger that specific pipeline, preventing unnecessary builds when unrelated code changes.
Detailed Answer
Think of a mono-repository like a large apartment building where each unit has its own doorbell. When a visitor rings apartment 3B, only apartment 3B's intercom activates — not every unit in the building. Path-based triggers in Azure DevOps work the same way: when a commit touches files in the payments-api directory, only the payments-api pipeline fires, leaving the worker-service and shared-library pipelines dormant. Without path filters, every pipeline in the repository would trigger on every commit, wasting build minutes and creating noise.
Azure DevOps allows multiple pipeline definitions in a single repository by letting you specify which YAML file defines each pipeline. When you create a new pipeline in the Azure DevOps UI, you point it at a specific YAML file path rather than defaulting to azure-pipelines.yml at the repository root. Each YAML file contains its own trigger section with include and exclude paths that determine which file changes activate that pipeline. The trigger evaluation happens server-side before an agent is allocated, so excluded paths never consume compute resources.
The path filter syntax supports glob patterns and directory hierarchies. The include list specifies which paths must be modified for the trigger to fire, while the exclude list removes specific subdirectories or file types from consideration. When both include and exclude are specified, a file must match an include pattern AND not match any exclude pattern to trigger the pipeline. A common pattern is including the service's source directory and its specific infrastructure configuration while excluding documentation and test fixture files that do not affect the build output.
For shared libraries consumed by multiple services, you face a design decision: should changes to shared code trigger all dependent pipelines or only the shared library's own pipeline? The recommended approach is to trigger all dependent pipelines when shared code changes, ensuring consumers are validated against the latest shared code. You accomplish this by including the shared directory path in each consuming pipeline's trigger. Alternatively, you can use pipeline triggers where a downstream pipeline triggers after an upstream pipeline completes, creating a dependency chain without path coupling.
Branch filters combine with path filters using AND logic: a commit must match both the branch filter and the path filter to trigger the pipeline. This enables sophisticated scenarios like triggering the production deployment pipeline only when changes land on the main branch AND touch the service's source code, while the PR validation pipeline triggers on any branch but only for the relevant paths. The batch setting controls whether commits that arrive while a build is running are batched into a single new build or each triggers independently.
The production gotcha is trigger evaluation on merge commits. When a pull request merges into main, the trigger evaluates the merged commit's changed files, which includes all files modified across the entire PR. If a developer's PR touches both the API and the worker service, both pipelines trigger on merge — which is correct behavior but sometimes surprises teams expecting path isolation. Another issue is the 'exclude docs' anti-pattern: if you exclude docs/** from triggers, a PR that ONLY modifies documentation never triggers any pipeline, meaning documentation-only PRs cannot have required build checks. The workaround is a lightweight docs-only pipeline that triggers on documentation paths.