Skip to main content


Sitecore Headless DevOps Best Practices – Part 2

Web API Using Azure

Welcome back to my series on DevOps Best Practices related to Sitecore Headless implementations. In Part 2 we are going to create the build pipelines for our Sitecore and Next.js solutions and see how they can be included in the PR process. If you haven’t checked-out Part 1 yet, I recommend doing so before continuing on.


Create Sitecore Build Pipeline

First, open the Azure DevOps portal, click on Pipelines and click on New Pipeline.

Create Build Pipeline

Next, select the service your repository is hosted with. We will be using YAML to create our pipelines so they can be source controlled and since the repository for this example is hosted in Azure, we will select the first choice.

Build Pipeline Repo

You will then be prompted to select a repository for the YAML to be stored in. I recommend using the same repository that the Sitecore and Next.js solutions are stored in. We will create a dedicated folder for the pipeline YAMLs in a later step.

After selecting the desired repository, scroll down to select Starter pipeline configuration.

Configure Pipeline Step

We’re almost ready to start adding tasks to our pipeline but first, let’s give the file a descriptive name and ensure the file will be stored in a dedicated folder. Click on the file name at the top of the editor and replace the default with devops/pipelines/sitecore-backend-build.yml. Adding the devops/pipelines path to the file name will create our dedicated folder in the repository.

Pipeline Name Location

Now we can build out the logic of our pipeline. A copy of the YAML used for this example can be found below. Some things to take note of: 

  1. On Line 1, variables can be used to customize the numbering of pipeline runs. More details can be found here.
  2. On Line 2, we have a trigger for the dmz and releases/* branch to ensure the build runs for completed PRs to dmz and anytime a new releases/* branch is created.
  3. We are using Unicorn for item serialization so there are several copy tasks to add those files/dependencies to our artifact.
  4. The TargetFolder property of the copy tasks is important to ensure the artifact has the correct structure. Specifically, Line 53 as this structure will be crucial in the delivery of the serialized items to the CM which we will see in Part 3.
  5. On Line 54, we publish the artifact $(Build.ArtifactStagingDirectory) that has been built via the VSBuild task on Line 25 and all the copy tasks. This artifact will be used in the release pipelines covered in Part 3.
  6. The last task for the pipeline is the fast-forward of master to dmz that we covered in Part 1. 
name: $(SourceBranchName).$(Rev:r)
  batch: true
    - dmz
    - releases/*
  vmImage: 'windows-latest'
- job: Job_1
  displayName: Agent job 1
    vmImage: windows-latest
  - checkout: self
    clean: true
    persistCredentials: true
    fetchDepth: 0
  - task: NuGetCommand@2
    displayName: NuGet restore
      solution: windows/src/HeadlessExample.sln
      selectOrConfig: config
      nugetConfigPath: windows/src/nuget.config
  - task: VSBuild@1
    displayName: Build solution windows/src/HeadlessExample.sln
      solution: 'windows/src/HeadlessExample.sln'
      msbuildArgs: '/p:DeployOnBuild=true /p:PublishProfile=IISProfile.pubxml /p:DeployDefaultTarget=WebPublish /p:DesktopBuildPackageLocation=$(Build.ArtifactStagingDirectory)'
      platform: 'Any CPU'
      configuration: 'Release'
      clean: true
  - task: CopyFiles@2
    displayName: Copy Unicorn Assembly
      SourceFolder: '$(Build.SourcesDirectory)/windows/src/Project/HeadlessExample/bin'
      Contents: |
      TargetFolder: '$(Build.ArtifactStagingDirectory)'
  - task: CopyFiles@2
    displayName: Copy Unicorn Module
      SourceFolder: '$(Build.SourcesDirectory)/devops'
      Contents: |
      TargetFolder: '$(Build.ArtifactStagingDirectory)'
  - task: CopyFiles@2
    displayName: Copy Unicorn Files
      SourceFolder: '$(Build.SourcesDirectory)/windows/src/Serialization'
      Contents: |
      TargetFolder: '$(Build.ArtifactStagingDirectory)/Serialization/App_Data/Unicorn'
  - task: PublishPipelineArtifact@1
    displayName: Publish Pipeline Artifact
      targetPath: '$(Build.ArtifactStagingDirectory)'
      artifact: 'BackendOutput'
      publishLocation: 'pipeline'
  - script: |
      ECHO clean
      git add . && git reset --hard
      ECHO git checkout master --quiet
      git checkout master --quiet
      ECHO git merge origin/dmz --ff-only --quiet
      git merge origin/dmz --ff-only --quiet
      ECHO git push origin master --quiet
      git push origin master --quiet
    displayName: Fast-forward master to dmz
    failOnStderr: true
    condition: and(eq(variables['Build.SourceBranch'], 'refs/heads/dmz'), ne(variables['Build.Reason'], 'PullRequest'), not(canceled()))

After adding/configuring all the tasks to the pipeline, click the arrow next to Save and run in the top right corner and select Save.

Save Pipeline

In the dialog that will open, select Create a new branch for this commit and make sure Start a pull request is selected. Update the commit message and add a description if desired then save. 

Save Pipeline Pr

Finally, go to Pull requests and complete the PR created by the save action above.

Pull Request

Next.js Pipeline

As I have mentioned previously, our Next.js application is going to be hosted with Vercel. When we deploy our application to Vercel, it will be complied on their build agents before being distributed to the network of edge nodes. To catch any potential build issues before pushing to Vercel, we will create a simple pipeline to check the compilation of the Next.js application as part of the PR process.

Follow the same steps from the section above to create a new pipeline. Make sure to store the YAML in the devops/pipelines folder of the repository and give it a descriptive name. A copy of the YAML used for this example is below. Some things to take note of: 

  1. On Line 5, we are using ubuntu-latest for our build agent to align with the build agents used by Vercel.
  2. On Line 20, we have the working directory set to frontend which is the name of the folder in our repository where the Next.js application is stored.
  3. Since this pipeline is purely used for validation of new code contributed to the Next.js application, there is no artifact generated.
- job: Job_1
  displayName: Agent job 1
    vmImage: ubuntu-latest
  - checkout: self
    clean: true
  - task: NodeTool@0
    displayName: Install Node.js
      versionSpec: 14.x
  - task: PowerShell@2
    displayName: PowerShell Script
      targetType: inline
      script: |
        npm install
        npm run next:build
      workingDirectory: frontend

Follow the same steps outlined above to save the pipeline, create a PR, and complete the PR. 

Use Pipeline for PR Validation

Now that we have both pipelines created, we need to revisit the branch policies for dmz and require these pipelines to be successful before new code can be merged in. If you need a refresher on how to access the branch policies, see Part 1.

Once you get to the branch policies setting for dmz, scroll down and click the +’ icon in the Build Validation section.

Build Validation

Add a policy for both pipelines we created with the following settings: 

  • Path filter:
    • Sitecore Build Pipeline: /windows/src/* 
    • Next.js Pipeline: /frontend/*

The path filter setting is important to make sure the correct pipeline(s) runs when changes are detected to either or both of the applications.

  • Trigger: Automatic 
  • Policy requirement: Required 
  • Build expiration: Immediately when dmz is updated

Build Policy

Lastly, make sure the two build validation policies added are enabled.

Enabled Build Policy


At this point, we have a single repository for both our applications and DevOps resources with the proper branches, policies, and security to align with Git DMZ Flow. We have a build pipeline that will validate any code added to the Next.js application prior to it being sent to Vercel. We have a second build pipeline that will validate any code added to the Sitecore application, output an artifact of the complied code and serialized Unicorn items, and perform the fast-forward of master automatically per Git DMZ Flow rules. 

In Part 3, our final part in this series, we will take a look at how to deploy both applications using Azure DevOps Releases and the Vercel CLI while following a zero downtime model. See you there!

Thoughts on “Sitecore Headless DevOps Best Practices – Part 2”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Luke Pace

Luke is a Lead Technical Consultant who started his Sitecore career as a certified back-end developer but has transitioned to DevOps for the last 6 years.

More from this Author

Follow Us