
September 09, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
You need a repeatable path from git push to a running site, and Azure Pipelines: Build Your First CI/CD Pipeline is the fastest way to get there inside Microsoft Azure DevOps. Unlike ad-hoc FTP uploads or manual SSH deploys, a YAML pipeline version-controls every build, test, and release step. If you already run GitLab CI for Laravel or Jenkins on a VPS, the concepts transfer cleanly—triggers, agents, artifacts, and environments just live under Azure DevOps names. This guide walks through a real PHP/Laravel-style pipeline you can copy, adapt, and ship today on PHP 8.3+ with Composer 2.10.
What do you need before you start Azure Pipelines: Build Your First CI/CD Pipeline?
Azure Pipelines sits inside Azure DevOps. You can use it free for small teams on public or private repos. Before writing YAML, gather four things: source control, a target server or Azure resource, secrets stored outside git, and a clear branch strategy.
Source control can live in Azure Repos, GitHub, or Bitbucket. Azure DevOps connects to external remotes with a service connection. Your deploy target might be an Ubuntu VPS over SSH, Azure App Service, or Azure Kubernetes Service (AKS). For Laravel 12 or 13.x apps, plan PHP 8.3 minimum (8.2 still works on Laravel 12), Composer 2.10, and optionally Node.js 26 LTS plus npm 12 if you compile front-end assets with Vite 8.x.
Prerequisites checklist
- An Azure DevOps organisation at dev.azure.com (free tier works for small teams).
- A repository with application code and a runnable test suite.
- Deploy credentials stored as pipeline variables or Azure Key Vault—not in YAML.
- An agent pool: Microsoft-hosted
ubuntu-latestis fine for most PHP builds.
On client projects I maintain with Deployer 7 and GitLab CI, the mental model is identical. Azure just names things differently: service connections replace deploy keys, and environments gate production releases. Read the companion Azure DevOps YAML pipelines practical guide if YAML schema details trip you up.
How do you create your first Azure Pipelines YAML file?
Start inside your Azure DevOps project. Go to Pipelines → New pipeline, pick your repo, and choose YAML. Azure generates a starter file. Replace it with a multi-stage pipeline that separates build, test, and deploy concerns.
Save this as azure-pipelines.yml at the repository root. It targets a Laravel-style PHP app on PHP 8.3 with Composer 2.10:
trigger:
branches:
include:
- main
pool:
vmImage: ubuntu-latest
variables:
phpVersion: '8.3'
composerFlags: '--no-interaction --prefer-dist --optimize-autoloader'
stages:
- stage: Build
displayName: Build and test
jobs:
- job: BuildJob
steps:
- checkout: self
fetchDepth: 1
- script: |
sudo update-alternatives --set php /usr/bin/php$(phpVersion)
php -v
displayName: Select PHP version
- script: |
curl -sS https://getcomposer.org/installer | php
php composer.phar install $(composerFlags)
displayName: Composer install
- script: php artisan test
displayName: Run PHPUnit
env:
APP_ENV: testing
- task: PublishPipelineArtifact@1
inputs:
targetPath: $(System.DefaultWorkingDirectory)
artifact: drop
publishLocation: pipeline
- stage: Deploy
displayName: Deploy to production
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployProd
environment: production
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: drop
- task: SSH@0
inputs:
sshEndpoint: ProductionVps
runOptions: inline
inline: |
cd /var/www/myapp/current
php artisan migrate --force
php artisan config:cache
Commit and push. Azure DevOps detects the YAML file and offers to run the pipeline. The first run often fails on missing service connections—that is normal. Fix connections, re-run, and iterate.
Official reference for triggers, pools, and tasks lives in the Azure Pipelines YAML schema documentation. Bookmark it. You will open it weekly.
Understand the core YAML blocks
- trigger — which branches start a run.
- pool — which agent image executes steps.
- stages / jobs / steps — the execution hierarchy.
- variables — reusable values; mark secrets as secret variables in the UI.
- environments — approval gates and deployment history for production.
How do you run tests and cache dependencies in Azure Pipelines?
Fast pipelines keep developers trusting CI. Slow ones get ignored. Cache Composer and npm directories so repeat runs skip redundant downloads—same principle as CI/CD caching for Composer and npm.
- task: Cache@2
inputs:
key: 'composer | "$(Agent.OS)" | composer.lock'
restoreKeys: |
composer | "$(Agent.OS)"
path: $(COMPOSER_CACHE_DIR)
displayName: Cache Composer
- script: |
export COMPOSER_CACHE_DIR=$(Pipeline.Workspace)/.composer-cache
php composer.phar install $(composerFlags)
displayName: Composer install with cache
Add a Node build step when your app ships Vite 8.x assets:
- task: NodeTool@0
inputs:
versionSpec: '26.x'
displayName: Install Node.js 26 LTS
- script: |
npm ci
npm run build
displayName: Build front-end assets
Gate merges on tests, not on vanity metrics. A failing php artisan test should block deploy. Optional coverage gates belong in a later iteration—see code coverage gates in CI when you are ready.
How do you deploy from Azure Pipelines to a server or Azure App Service?
Deployment is where pipelines earn their keep. Pick one primary target per app. Mixing SSH tarball deploys with App Service zip deploy in the same pipeline creates confusion.
Option A: SSH deploy to Ubuntu VPS
Create an SSH service connection under Project settings → Service connections → New → SSH. Name it ProductionVps to match the YAML above. The SSH task runs remote shell commands on your Linux box—the same post-deploy steps I run after Deployer releases on sister legal-tech sites.
For symlink-based zero-downtime deploys, rsync a release folder over SSH, flip the current symlink, then reload PHP-FPM. Detailed VPS patterns appear in deploy a Laravel app with GitLab CI/CD to a VPS; swap GitLab job syntax for Azure tasks.
Option B: Azure App Service
Replace the SSH task with AzureWebApp@1 when hosting on App Service:
- task: AzureWebApp@1
inputs:
azureSubscription: MyAzureSubscription
appType: webAppLinux
appName: my-laravel-app
package: $(Pipeline.Workspace)/drop
App Service handles PHP runtime selection in the portal. Set WEBSITE_RUN_FROM_PACKAGE or deploy extracted files depending on your opcache strategy.
Option C: Azure Kubernetes Service
Container deploys belong in a separate pipeline stage. Follow deploy to AKS with Azure Pipelines once your Dockerfile is stable. Do not containerise on day one unless the team already runs Kubernetes in production.
Store database URLs, API keys, and SSH keys in Azure DevOps variable groups or Key Vault. Never commit them. The CI/CD secrets management best practices article covers rotation and least-privilege scopes that apply here too.
How does Azure Pipelines compare with Jenkins and GitLab CI?
Teams often ask which platform to standardise on. The answer depends on where your code and cloud budget already live—not feature checklists alone.
| Criteria | Azure Pipelines | GitLab CI | Jenkins |
|---|---|---|---|
| Hosting model | Azure DevOps SaaS + optional self-hosted agents | GitLab.com or self-managed GitLab | Self-hosted controller + agents you maintain |
| Config format | YAML in azure-pipelines.yml | YAML in .gitlab-ci.yml | Jenkinsfile (Declarative or Scripted) |
| Free tier | 1 parallel job, 1,800 min/month on Microsoft-hosted agents | 400 CI minutes/month on GitLab.com free | Free software; you pay for servers |
| Azure integration | Native service connections, Key Vault, AKS, App Service | Works via tokens and scripts | Plugins; more wiring required |
| Best fit | Teams on Azure or .NET + polyglot stacks | Single-platform git + CI (my default for Laravel VPS deploys) | Maximum control, legacy estates, plugin ecosystems |
For a deeper vendor shootout, read GitHub Actions vs GitLab CI in 2026. Azure Pipelines wins when Active Directory, Azure subscriptions, and compliance boundaries already centre on Microsoft. GitLab CI remains my go-to for Laravel on Ubuntu VPS with Deployer 7—see the Adventure Third Pole Trek booking platform portfolio entry for a Livewire + CI example.
What mistakes break your first Azure Pipelines CI/CD setup?
Most first-pipeline failures are configuration, not code. These five issues appear on nearly every greenfield Azure DevOps project I review.
Wrong PHP version on the agent
Microsoft-hosted ubuntu-latest ships multiple PHP versions side by side. Your app may need 8.3 while the default binary is older. Always pin with update-alternatives or a setup script and print php -v in the log.
Missing environment approval on production
Without an environment: production block, every green build deploys immediately. Add approvers under Pipelines → Environments → production → Approvals. Blue/green patterns come later—start with a hard gate.
Secrets echoed in logs
Mark variables as secret in the UI. Never echo connection strings in bash steps. Azure masks secret variables automatically when referenced correctly.
Artifacts too large or wrong path
Publishing the entire repo including vendor/ bloats artifacts. Publish only deployable output, or rebuild on the deploy agent with a cached Composer lock. Validate artifact contents with the JSON formatter tool when debugging API manifest files—not pipeline logs themselves, but the same discipline applies.
Branch triggers firing on every feature branch deploy
Limit production deploys with branch conditions:
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
Feature branches should run build and test only. Wire staging environments to develop if you use GitFlow.
Solo developers and small Nepal teams should keep pipelines boring. One staging environment, one production gate, nightly backups on the server—that matches advice in CI/CD best practices for small teams. Need someone to wire this on your stack? Support and maintenance services and Linux system administration cover pipeline plus server hardening together.
Broader automation patterns live in build pipeline automation best practices. If you are new to the Azure DevOps UI itself, start with the Azure DevOps complete beginner guide before editing YAML.
Key Takeaways
- Put
azure-pipelines.ymlin the repo root with separate build, test, and deploy stages. - Pin PHP 8.3+ and Composer 2.10 on agents; cache dependencies to keep runs under five minutes.
- Store secrets in variable groups or Key Vault—never in committed YAML.
- Gate production with an Azure DevOps environment approval and a
mainbranch condition. - Pick one deploy target (SSH VPS, App Service, or AKS) and master it before adding complexity.
- Compare Azure Pipelines with your existing CI; migrate incrementally rather than big-bang.
People Also Ask
Is Azure Pipelines free for small projects?
Yes. Azure DevOps includes free Microsoft-hosted parallel jobs with monthly minute quotas for private projects. Public repos receive more generous limits. Self-hosted agents are unlimited—you supply the VM. For a side project, the free tier is usually enough until you need multiple parallel jobs.
Can Azure Pipelines deploy to a non-Azure Linux server?
Yes. Create an SSH service connection and use the SSH task or copy files over SCP/rsync in a bash step. This is how most Laravel VPS deploys work. You do not need App Service or AKS unless you want managed hosting.
YAML or classic editor—which should beginners use?
Always YAML. Classic release pipelines still exist but Microsoft invests in YAML-first features—multi-stage pipelines, templates, and branch policies all assume code in git. Store pipeline changes in pull requests like application code.
How do Azure Pipelines fit with Laravel 13?
Laravel 13.x requires PHP 8.3 minimum. Select that version on the agent, run composer install, php artisan test, then deploy. Queue workers and schedulers remain server-side cron jobs after deploy—they are not pipeline steps unless you explicitly restart systemd units over SSH.
Ship your first pipeline this week
Azure Pipelines: Build Your First CI/CD Pipeline boils down to one YAML file, one service connection, and one successful green run. Start with build plus test only. Add deploy after tests pass three times in a row. Branch from the Jenkins or GitLab examples in build a CI/CD pipeline with Jenkins if you are migrating—then delete duplicate steps until Azure owns the path to production.
When you want hands-on help wiring Azure DevOps to a Laravel app, VPS, or App Service—or comparing CI options for a Nepal startup budget—custom software development and enterprise application development cover architecture through deploy. Read blue/green deployment explained before your second pipeline iteration. About me lists the stacks I ship daily.
Contact us with your repo layout and deploy target. We will sketch a pipeline YAML you can paste into Azure DevOps and run the same day.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

