← Wiki

From Obsidian to the site: publishing with Azure Pipelines

  • 4 min read
On this page

Azure Pipelines is the automation service in Microsoft’s Azure DevOps. It does the same job as GitHub Actions on the previous page: on every push of the vault that touches the blog or the site, it builds the site and publishes it to Cloudflare. The vault stays on GitHub; Azure DevOps only reads the repository.

Use this page instead of the GitHub Actions one, not alongside it: with both in place, every push would publish the site twice.

The complete pipeline

trigger:
  branches:
    include: [main]
  paths:
    include: [blog, .site, azure-pipelines.yml]

pr: none

pool:
  vmImage: ubuntu-latest

steps:
  - checkout: self
    displayName: Download only blog/ and .site/
    fetchDepth: 1
    fetchFilter: blob:none
    sparseCheckoutPatterns: /blog/ /.site/

  - task: UseNode@1
    displayName: Set up Node
    inputs:
      version: 22.x

  - script: |
      npm ci
      npm run build
    displayName: Build the site
    workingDirectory: .site

  - script: npx wrangler@4 deploy --assets ./dist --name my-blog --compatibility-date 2026-10-15
    displayName: Publish to Cloudflare
    workingDirectory: .site
    env:
      CLOUDFLARE_API_TOKEN: $(CLOUDFLARE_API_TOKEN)
      CLOUDFLARE_ACCOUNT_ID: $(CLOUDFLARE_ACCOUNT_ID)

The file is named azure-pipelines.yml and sits at the root of the vault. Replace my-blog and the date with the ones you used for your first publish to Cloudflare.

What each part does

When it runs (trigger). On a push to main that touches blog/, .site/ or the pipeline itself. pr: none turns off runs for pull requests, which a vault does not use.

Download only blog/ and .site/. checkout downloads only the latest commit (fetchDepth: 1), does not download file contents up front (fetchFilter: blob:none) and leaves only the two folders on disk (sparseCheckoutPatterns, with the patterns separated by spaces). The content of your other notes never reaches the pipeline’s machine; only the list of file names does, because it is part of the commit. By default, Azure Pipelines does not leave the repository credential saved in the downloaded folder.

Set up Node. The UseNode@1 task installs the latest Node 22, the release line the template requires.

Build the site and Publish to Cloudflare. The same commands you run on your computer. If the template refuses a note, the build step fails and the publish step does not run. Azure secret variables only reach a command when they are listed in its env, as in the last step.

About the free tier

On private projects, Azure Pipelines gives you one free job of up to 60 minutes at a time, capped at 1,800 minutes a month. To get that free tier, the organization must be linked to a valid Azure subscription with billing set up. GitHub Actions does not ask for that extra step, which is why the guide recommends GitHub Actions if you don’t already use Azure DevOps.

Create the pipeline

  1. At dev.azure.com, create an organization and, inside it, a private project.
  2. Create the file in the vault repository on the GitHub website: Add file → Create new file, name it azure-pipelines.yml, paste the pipeline and click Commit changes. Then bring the change down to your computer with Fetch origin and Pull origin in GitHub Desktop.
  3. In the project, go to Pipelines → New pipeline, choose GitHub and authorize Azure Pipelines to read your repositories.
  4. Pick the vault repository. Azure DevOps finds azure-pipelines.yml and shows the file.
  5. Before running it, click Variables and create the two variables from the Cloudflare page: CLOUDFLARE_API_TOKEN, with Keep this value secret checked, and CLOUDFLARE_ACCOUNT_ID.
  6. Save and run.

On the first run, Azure DevOps may ask you to authorize the pipeline to use a resource. The run waits until someone clicks Authorize resources and confirms.

Check the first run

Move a note into blog/ and push the vault. Under Pipelines, a run appears with the log for each step.

If it fails at Likely cause
Build the site A note that does not match what the template requires. The log shows the note and the property
Publish to Cloudflare A missing variable, a variable not mapped in env, or an expired token
Nothing ran The push did not touch blog/, .site/ or the pipeline, or the pipeline’s trigger was turned off in its settings

Whatever fails, the live site stays as it was.