Azure Boards

Azure DevOps Marketplace extension.

BranchDeploy / Guides / Deploy a Feature Branch to Staging in Azure DevOps

How to deploy a feature branch to staging in Azure DevOps

Deploying a feature branch to staging before it is merged lets testers and product owners see a change in a realistic environment while it is still cheap to fix. Azure Pipelines supports this well, but it does not decide for you how the branch gets chosen, who is allowed to choose it, or which branches should never reach staging.

This guide compares three approaches, from the simplest to the most controlled, with YAML you can adapt.

Before you start: one staging, many branches

A single shared staging environment can only run one branch at a time. Each feature-branch deploy replaces the last one. That is fine when the team agrees who is using staging, but it means the deploy step should always make it obvious which branch and ticket is about to go out. If you need several branches live at once, you need ephemeral or per-branch environments, which is a bigger change than this guide covers.

Approach 1: manual run with the branch selected

The pipeline deploys whatever branch it runs on, and the person deploying picks the branch in the Run pipeline dialog.

  1. Open the staging deployment pipeline in Azure Pipelines.
  2. Click Run pipeline.
  3. Change Branch/tag from the default to the feature branch, for example feature/1482-invoice-export.
  4. Click Run.

Good for: small teams where developers deploy their own branches. Watch out for: the default branch left selected, look-alike branch names, and anyone with queue permission being able to deploy any branch, including main.

Approach 2: runtime parameters and branch conditions in YAML

Add guard rails to the pipeline itself: a runtime parameter for the target environment, and a stage condition that only deploys feature and bugfix branches.

trigger: none
pr: none

parameters:
  - name: targetEnvironment
    displayName: Target environment
    type: string
    default: staging
    values:
      - staging
      - uat

stages:
  - stage: Deploy
    condition: |
      or(
        startsWith(variables['Build.SourceBranch'], 'refs/heads/feature/'),
        startsWith(variables['Build.SourceBranch'], 'refs/heads/bugfix/')
      )
    jobs:
      - deployment: DeployWeb
        environment: ${{ parameters.targetEnvironment }}
        pool:
          vmImage: ubuntu-latest
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
                - script: ./scripts/deploy.sh ${{ parameters.targetEnvironment }}
                  displayName: Deploy $(Build.SourceBranchName)

Good for: teams that want the pipeline to enforce rules no matter who queues it. Watch out for: the branch is still chosen by hand, and a skipped stage can look like a successful run if nobody checks the stage list.

Approach 3: deploy from the work item with BranchDeploy

If feature branches are linked to Azure Boards work items, the branch can come from the ticket instead of a person. BranchDeploy adds a BranchDeploy action to the work item toolbar that:

  1. Reads the work item's linked branch, or a linked PR's source branch.
  2. Shows the work item, repository, branch, and environment in a confirmation step.
  3. Blocks the deploy if the branch does not match the environment's allowed patterns, such as feature/* and bugfix/*.
  4. Queues the staging pipeline with that branch as the run's source branch, as the signed-in user.

BranchDeploy passes context as queue-time variables (for example environment and workItemId), not YAML runtime parameters. If you use runtime parameters as in Approach 2, they keep their default values when BranchDeploy queues the run. For a pipeline built for BranchDeploy, read a variable instead:

trigger: none
pr: none

# "environment" is defined in the pipeline's Variables panel, not here,
# so it can be set at queue time.

stages:
  - stage: Deploy
    jobs:
      - deployment: DeployWeb
        environment: staging
        pool:
          vmImage: ubuntu-latest
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
                - script: ./scripts/deploy.sh $(environment)
                  displayName: Deploy $(Build.SourceBranchName)

Define environment in the pipeline's Variables panel (Edit → Variables) with Let users override this value when running this pipeline ticked. Variables declared in the YAML file itself cannot be overridden at queue time. See queue-time variables vs runtime parameters for the difference. Environment approvals and checks on staging still apply.

Comparison

Manual run YAML parameters + conditions BranchDeploy
Who picks the branch The person deploying The person deploying The work item's Development link
Unsafe branches Not blocked Stage skipped after the run starts Blocked before any run is queued
Confirmation before deploy No No (approvals optional) Yes, with work item and branch shown
Link to the ticket None None unless passed manually Work item ID passed as a variable
Setup None YAML changes Install extension, enter pipeline ID

The approaches combine well. Many teams keep the YAML condition from Approach 2 as a final safety net and use BranchDeploy so testers can deploy from the ticket.

Common problems

The run succeeds but staging did not change

The deploy stage was probably skipped by a condition. Open the run and check whether the stage shows as skipped, then compare the branch with the condition.

The wrong environment value was used

If the pipeline reads a runtime parameter but the run was queued with a variable (by BranchDeploy, the CLI --variables flag, or the REST API variables object), the parameter default is used. Read the value from a variable instead, or pass it as a template parameter.

Feature branches stay on staging for days

Agree a simple rule, such as "staging shows the ticket in the In QA column", so people know when it is safe to deploy over someone else's branch. See the UAT deployment workflow for a board flow that works.

Frequently asked questions

Can Azure Pipelines deploy a feature branch automatically on push?

Yes. Add the branch pattern to the pipeline's trigger, for example feature/*. With one shared staging environment this means the last push wins, so most teams prefer on-demand deploys for staging.

How do I stop main being deployed to staging?

Use a stage condition in YAML, an environment check, or a BranchDeploy allowlist that leaves main out. The allowlist stops the run before it is queued; the YAML condition stops it inside the run.

Do I need a separate pipeline for staging and UAT?

Not necessarily. One pipeline can deploy to either based on a parameter or variable. BranchDeploy can point several environments at the same pipeline ID and pass a different environment value for each (multiple environments are part of Pro).

No clipboard. No tab switching. No branch-name guesswork.

BranchDeploy adds a deploy action to Azure Boards work items. Free for one project and one environment.

Install Free forever for one project.

Ready to deploy?

Install BranchDeploy from the Marketplace, open Project Settings, add your pipeline ID, and deploy from a work item in minutes.

$ az devops extension install --extension-id branchdeploy --publisher-id PixelFunnelLtd
Install free
Requirements
  • Azure Repos + Azure Pipelines.
  • Permission to queue the pipeline.
  • No BranchDeploy account needed (Free).
Setup
  • Install the extension.
  • Open Project Settings → BranchDeploy.
  • Enter your pipeline ID and save.
Free tier
  • One project, one environment.
  • Queues as your Azure DevOps session.
  • Completely free, forever.
Pro
BranchDeploy // © 2026 Pixel Funnel Ltd // Azure DevOps Marketplace extension // No clipboard. No tab switching. No branch-name guesswork.