Deploy your app to Linux virtual machines in Azure using Azure DevOps Services and Azure Pipelines

Applies to: ✔️ Linux VMs

Continuous integration (CI) and continuous deployment (CD) form a pipeline by which you can build, release, and deploy your code after every code commit. This document contains the steps associated with setting up a CI/CD pipeline for doing multi-machine deployments using Azure Pipelines.

Azure Pipelines provides a complete, fully featured set of CI/CD automation tools for deployments to Virtual machines, both on-prem or on any cloud.

In this tutorial, you will set up a YAML based CI/CD pipeline to deploy your app to an Azure Pipelines Environment with Linux Virtual machines as resources, each of which serve as web servers to run the app.

You learn how to:

  • Get a sample app.
  • Create a YAML based Azure Pipelines CI pipeline for building the sample app.
  • Create an Azure Pipelines Environment for the Azure virtual machines
  • Create an Azure Pipelines CD pipeline.
  • Execute manual and CI-triggered deployments.

Before you begin

Get your sample app code

If you already have an app in GitHub that you want to deploy, you can try creating a pipeline for that code.

However, if you are a new user, then you might get a better start by using our sample code. In that case, fork this repo in GitHub:


Petclinic is a Java Spring Boot application built using Maven.

Prerequisites for the Linux VM

Sample apps mentioned above have been tested on Ubuntu 16.04, and we recommend you use the same version of Linux VM for this quickstart. Follow the additional steps described below based on the runtime stack used for the app.

Create an Azure Pipelines environment with Azure virtual machines

Virtual machines can be added as resources within environments and can be targeted for multi-machine deployments. Deployment history views within environment provide traceability from VM to the pipeline and then to the commit.

You can create an environment in the “Environments” hub within the “Pipelines” section.

  1. Sign in to your Azure DevOps organization and navigate to your project.

  2. In your project, navigate to the Pipelines page. Then choose Environments and click Create Environment. Specify a Name (required) for the environment and a Description.

  3. Choose Virtual Machines as a Resource to be added to the environment and click Next.

  4. Choose Operating System (Windows/Linux), and copy PS registration script.

  5. Now run the copied script from an administrator PowerShell command prompt on each of the target VMs to be registered with this Environment.


    • Personal Access Token of the logged in user is pre-inserted in the script which expires on the same day making the copied script unusable thereon.
    • If your VM already has any agent running on it, provide a unique name for “agent” to register with environment.
  6. Once the VM is registered, it will start appearing as an environment resource under “resources” tab of the environment.


  7. For adding more VMs, you can view and copy the script again by clicking on “Add resource” and choosing “Virtual Machines” as resource. This script would remain same for all the VMs to be added to this environment.

  8. Each machine interacts with Azure Pipelines to coordinate deployment of your app.


  9. You can add tags to the VM as part of the interactive PowerShell registration script (or) you can also add/remove the same from the resource view by clicking on the triple dots at the end of each VM resource in the resources view.

    The tags you assign allow you to limit deployment to specific virtual machines when the environment is used in a Deployment job. Tags are each limited to 256 characters, but there is no limit to the number of tags you can use.


Define your CI build pipeline

You'll need a continuous integration (CI) build pipeline that publishes your web application, as well as a deployment script that can be run locally on the Ubuntu server. Set up a CI build pipeline based on the runtime you want to use.

  1. Sign in to your Azure DevOps organization and navigate to your project.

  2. In your project, navigate to the Pipelines page. Then choose the action to create a new pipeline.

  3. Walk through the steps of the wizard by first selecting GitHub as the location of your source code.

  4. You might be redirected to GitHub to sign in. If so, enter your GitHub credentials.

  5. When the list of repositories appears, select your desired sample app repository.

  6. Azure Pipelines will analyze your repository and recommend a suitable pipeline template.

Select the starter template and copy the below YAML snippet that builds your Java project and runs tests with Apache Maven:

- job: Build
  displayName: Build Maven Project
  - task: Maven@3
    displayName: 'Maven Package'
      mavenPomFile: 'pom.xml'
  - task: CopyFiles@2
    displayName: 'Copy Files to artifact staging directory'
      SourceFolder: '$(System.DefaultWorkingDirectory)'
      Contents: '**/target/*.?(war|jar)'
      TargetFolder: $(Build.ArtifactStagingDirectory)
  - upload: $(Build.ArtifactStagingDirectory)
    artifact: drop

For more guidance, follow the steps mentioned in Build your Java app with Maven.

Define CD steps to deploy to the Linux VM

  1. Change the YAML file for the above pipeline to include a deployment job by referencing the environment and the VM resources which you have earlier using the YAML syntax below:

    - deployment: VMDeploy
      displayName: web
        name:  <environment name>
        resourceType: VirtualMachine
        tags: web
  2. You can select specific sets of virtual machines from the environment to receive the deployment by specifying the tags that you have defined for each virtual machine in the environment. Here is the complete YAML schema for Deployment job.

  3. You can specify either runOnce or rolling as deployment strategy.

    runOnce is the simplest deployment strategy wherein all the life cycle hooks, namely preDeploy deploy, routeTraffic, and postRouteTraffic, are executed once. Then, either on: success or on: failure is executed.

    Below is the example YAML snippet for runOnce :

    - deployment: VMDeploy
      displayName: web
        vmImage: 'Ubuntu-latest'
        name: <environment name>
        resourceType: VirtualMachine
              - script: echo my first deployment
  4. Below is an example of the YAML snippet that you can use to define a rolling strategy for Virtual machines updates upto 5 targets in each iteration. maxParallel will determine the number of targets that can be deployed to, in parallel. The selection accounts for absolute number or percentage of targets that must remain available at any time excluding the targets that are being deployed to. It is also used to determine the success and failure conditions during deployment.

    - deployment: VMDeploy
      displayName: web
        name: <environment name>
        resourceType: VirtualMachine
            maxParallel: 5  #for percentages, mention as x%
              - download: current
                artifact: drop
              - script: echo initialize, cleanup, backup, install certs
              - task: Bash@3
                  targetType: 'inline'
                  script: |
                    # Modify deployment script based on the app type
                    echo "Starting deployment script run"
                    sudo java -jar '$(Pipeline.Workspace)/drop/**/target/*.jar'
              - script: echo routing traffic
              - script: echo health check post-route traffic
                - script: echo Restore from backup! This is on failure
                - script: echo Notify! This is on success

    With each run of this job, deployment history is recorded against the <environment name> environment that you have created and registered the VMs.

Run your pipeline and get traceability views in environment

Deployments view of the environment provides complete traceability of commits and work items, and a cross-pipeline deployment history per environment/resource.



Next steps

  • You can proceed to customize the pipeline you just created.
  • To learn what else you can do in YAML pipelines, see YAML schema reference.
  • To learn about how to deploy a LAMP (Linux, Apache, MySQL, and PHP) stack, advance to the next tutorial.