Configuring CI/CD for Your Website with Azure DevOps
Azure DevOps Pipelines is Microsoft's CI/CD platform with tight integration with Azure cloud. YAML pipelines, built-in environments with approval gates, Azure Artifacts for storing artifacts.
Basic Pipeline Structure
# azure-pipelines.yml
trigger:
branches:
include: [main, develop]
paths:
exclude: ['*.md', 'docs/**']
pr:
branches:
include: [main]
pool:
vmImage: 'ubuntu-latest'
variables:
nodeVersion: '20.x'
artifactName: 'web-app'
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- task: NodeTool@0
inputs: { versionSpec: '$(nodeVersion)' }
- script: npm ci
displayName: Install dependencies
- script: npm run build
displayName: Build
env:
VITE_API_URL: $(API_URL) # from Library
- task: CopyFiles@2
inputs:
sourceFolder: dist
contents: '**'
targetFolder: $(Build.ArtifactStagingDirectory)
- task: PublishBuildArtifacts@1
inputs:
artifactName: $(artifactName)
- stage: Test
dependsOn: Build
jobs:
- job: UnitTests
steps:
- script: npm ci && npm test -- --ci --coverage
displayName: Unit Tests
- task: PublishTestResults@2
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: 'test-results.xml'
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: 'coverage/cobertura-coverage.xml'
- stage: DeployStaging
dependsOn: Test
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
jobs:
- deployment: DeployToStaging
environment: staging
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: 'Azure-Service-Connection'
appType: webApp
appName: 'myapp-staging'
package: $(Pipeline.Workspace)/$(artifactName)
- stage: DeployProduction
dependsOn: DeployStaging
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployToProd
environment: production # requires manual approval
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: 'Azure-Service-Connection'
appType: webApp
appName: 'myapp-prod'
package: $(Pipeline.Workspace)/$(artifactName)
deploymentMethod: zipDeploy
Deploy to VPS via SSH
- task: SSH@0
displayName: 'Deploy to VPS'
inputs:
sshEndpoint: 'production-server'
runOptions: 'commands'
commands: |
cd /var/www/app
git fetch origin main
git reset --hard origin/main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache && php artisan route:cache
sudo systemctl reload php8.3-fpm nginx
Variables and Secrets
# Using variables from Library
variables:
- group: 'production-secrets' # Variable Group from Azure DevOps Library
- name: 'APP_VERSION'
value: '$(Build.BuildNumber)'
steps:
- script: |
echo "Deploying version $(APP_VERSION)"
echo "DB_HOST is $(DB_HOST)" # from secret variable group
Docker Deploy to Azure Container Registry
- task: Docker@2
displayName: Build and push
inputs:
containerRegistry: 'myapp-acr'
repository: 'myapp/web'
command: buildAndPush
Dockerfile: 'Dockerfile'
tags: |
$(Build.BuildId)
latest
- task: AzureContainerApps@1
inputs:
azureSubscription: 'Azure-Service-Connection'
containerAppName: 'myapp-web'
resourceGroup: 'myapp-rg'
imageToDeploy: 'myapp.azurecr.io/myapp/web:$(Build.BuildId)'
Approval Gates for Production
In Azure DevOps → Environments → production → Approvals and checks → Add → Approvals. Assign approvers. Deployment to production will pause until manual confirmation.
Implementation Timeline
Basic pipeline with staging/production and approval gates: 2–4 days.







