This guide explains how to set up DotnetDeployer for any .NET project. Follow these steps to create the necessary configuration files.
To deploy a .NET project using DotnetDeployer, you need:
deployer.yaml- Configuration file in the repository rootazure-pipelines.yml(or equivalent CI config) - Pipeline that invokes the toolGitVersion.yml(optional) - For automatic semantic versioning
Place this file in the repository root (same directory as the .sln or .slnx file).
version: 1
nuget:
enabled: true
source: https://api.nuget.org/v3/index.json
apiKey:
from: env
name: NUGET_API_KEYversion: 1
# NuGet deployment - packs and pushes all packable projects
# Each generated .nupkg automatically includes a README.md with a
# changelog of git commits between the previous tag and HEAD.
nuget:
enabled: true
source: https://api.nuget.org/v3/index.json
apiKey:
from: env
name: NUGET_API_KEY
# GitHub Releases - builds platform-specific packages and uploads as assets
github:
enabled: true
owner: YourGitHubUsername # GitHub username or org
repo: YourRepoName # Repository name
token: # Flexible value source
from: env
name: GITHUB_TOKEN
outputDir: artifacts # Optional: where to save packages locally
packages:
- project: src/MyApp.Desktop/MyApp.Desktop.csproj
formats:
- type: appimage
arch: [x64, arm64]
- type: deb
arch: [x64]
- type: rpm
arch: [x64]
- type: exe-setup
arch: [x64]
- type: dmg
arch: [x64, arm64]
# GitHub Pages - deploys WebAssembly apps
githubPages:
enabled: true
owner: YourGitHubUsername
repo: MyApp-Pages # Dedicated repo for GitHub Pages
token:
from: env
name: GITHUB_TOKEN
branch: main # Branch to push to (default: main)
customDomain: myapp.example.com # Optional: custom domain
project: src/MyApp.Browser/MyApp.Browser.csproj| Section | Property | Description |
|---|---|---|
nuget |
enabled |
Enable/disable NuGet deployment |
nuget |
source |
NuGet feed URL |
nuget |
apiKey |
NuGet API key — flexible value source |
github |
enabled |
Enable/disable GitHub Releases |
github |
owner |
GitHub username or organization |
github |
repo |
Repository name |
github |
token |
GitHub token — flexible value source |
github |
outputDir |
Local directory for generated packages |
github.packages[].formats[].type |
Package type: appimage, deb, rpm, exe-sfx, exe-setup, msix, dmg, apk, aab |
|
github.packages[].formats[].arch |
Architectures: x64, arm64, x86 |
|
githubPages |
enabled |
Enable/disable GitHub Pages deployment |
githubPages |
project |
Path to the WebAssembly project to deploy |
githubPages |
token |
GitHub token — flexible value source |
githubPages |
customDomain |
Optional custom domain for the site |
android |
signing |
Top-level Android signing configuration |
android.signing |
keystore |
Keystore source block (see below) |
android.signing.keystore |
from |
Source type: file, env, secret |
android.signing.keystore |
path |
File path (when from: file) |
android.signing.keystore |
name |
Env var name (when from: env) |
android.signing.keystore |
key |
Secret key name (when from: secret) |
android.signing.keystore |
encoding |
Encoding: raw, base64 |
android.signing |
storePassword |
Store password — flexible value source |
android.signing |
keyAlias |
Key alias — flexible value source |
android.signing |
keyPassword |
Key password — flexible value source |
Place this file in the repository root.
trigger:
- master
- main
variables:
- group: api-keys # Variable group with secrets
- name: Agent.Source.Git.ShallowFetchDepth
value: 0 # Required for GitVersion
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
fetchDepth: 0 # Required for GitVersion
# Dry run for PRs (no actual deployment)
- pwsh: |
dotnet tool install -g DotnetDeployer.Tool
dotnetdeployer --dry-run
displayName: Dry run (PRs)
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))
env:
NUGET_API_KEY: $(NugetApiKey)
GITHUB_TOKEN: $(GitHubToken)
ANDROID_KEYSTORE_BASE64: $(AndroidKeystoreBase64)
ANDROID_STORE_PASS: $(AndroidStorePass)
ANDROID_KEY_PASS: $(AndroidKeyPass)
# Actual deployment for master/main branch
- pwsh: |
dotnet tool install -g DotnetDeployer.Tool
dotnetdeployer
displayName: Deploy
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
env:
NUGET_API_KEY: $(NugetApiKey)
GITHUB_TOKEN: $(GitHubToken)
ANDROID_KEYSTORE_BASE64: $(AndroidKeystoreBase64)
ANDROID_STORE_PASS: $(AndroidStorePass)
ANDROID_KEY_PASS: $(AndroidKeyPass)fetchDepth: 0: Required for GitVersion to calculate the version correctlyAgent.Source.Git.ShallowFetchDepth: 0: Ensures full history is fetched- Variable group
api-keys: Must contain secrets likeNugetApiKey,GitHubToken - Condition: Only deploys on
masterormainbranch; PRs do a dry run
If the project IS DotnetDeployer or you want to run from source:
- pwsh: dotnet run --project src/DotnetDeployer.Tool/DotnetDeployer.Tool.csproj
displayName: Deploy
env:
NUGET_API_KEY: $(NugetApiKey)Place this file in the repository root for semantic versioning configuration.
mode: ContinuousDelivery
branches:
master:
regex: ^master$|^main$
tag: ''
increment: Patch
feature:
regex: ^feature[/-]
tag: alpha
increment: Minor
hotfix:
regex: ^hotfix[/-]
tag: beta
increment: Patch
pull-request:
regex: ^(pull|pr)[/-]
tag: pr
increment: InheritIf not present, GitVersion uses sensible defaults.
In Azure DevOps, create a variable group named api-keys with:
| Variable | Description | Secret? |
|---|---|---|
NugetApiKey |
NuGet.org API key | Yes |
GitHubToken |
GitHub Personal Access Token with repo scope |
Yes |
AndroidKeystoreBase64 |
Android keystore file encoded in base64 | Yes |
AndroidStorePass |
Android keystore store password | Yes |
AndroidKeyPass |
Android signing key password | Yes |
Only define the variables needed for your configuration. NuGet-only projects only need NugetApiKey.
- DotnetDeployer reads
deployer.yamlfrom the repository root - GitVersion calculates the semantic version from git history
- The tool packs/builds with the calculated version
- Packages are pushed to configured destinations
- Azure Pipelines build number is updated via
##vso[build.updatebuildnumber]
version: 1
nuget:
enabled: true
source: https://api.nuget.org/v3/index.json
apiKey:
from: env
name: NUGET_API_KEYversion: 1
github:
enabled: true
owner: MyOrg
repo: MyDesktopApp
token:
from: env
name: GITHUB_TOKEN
packages:
- project: src/MyApp/MyApp.csproj
formats:
- type: exe-setup
arch: [x64]
- type: appimage
arch: [x64, arm64]
- type: dmg
arch: [x64, arm64]version: 1
github:
enabled: true
owner: MyOrg
repo: MyApp
token:
from: env
name: GITHUB_TOKEN
outputDir: artifacts
packages:
- project: src/MyApp.Desktop/MyApp.Desktop.csproj
formats:
- type: appimage
arch: [x64, arm64]
- type: exe-sfx
arch: [x64]
- type: exe-setup
arch: [x64]
- project: src/MyApp.Android/MyApp.Android.csproj
formats:
- type: apk
arch: [x64]
signing:
keystore:
from: env
name: ANDROID_KEYSTORE_BASE64
encoding: base64
storePassword:
from: env
name: ANDROID_STORE_PASS
keyAlias: release-key
keyPassword:
from: env
name: ANDROID_KEY_PASS
android:
signing:
keystore:
from: env
name: ANDROID_KEYSTORE_BASE64
encoding: base64
storePassword:
from: env
name: ANDROID_STORE_PASS
keyAlias: release-key
keyPassword:
from: env
name: ANDROID_KEY_PASSversion: 1
githubPages:
enabled: true
owner: MyOrg
repo: myapp-demo
token:
from: env
name: GITHUB_TOKEN
project: src/MyApp.Browser/MyApp.Browser.csprojAll sensitive or configurable tokens (nuget.apiKey, github.token, githubPages.token, android.signing.storePassword, android.signing.keyAlias, android.signing.keyPassword) use the same flexible value source model. Each accepts either:
- A plain string (shorthand for a literal value):
keyAlias: release-key - An expanded block specifying the source type:
from |
Required field | Description |
|---|---|---|
literal |
value |
Value written directly in YAML |
env |
name |
Read from an environment variable |
secret |
key |
Read from deployer.secrets.yaml; if missing, read from the system keyring |
file |
path |
Read from a file on disk |
All sources except literal and file support an optional encoding field (raw or base64).
# Plain string → literal
keyAlias: release-key
# Environment variable
token:
from: env
name: GITHUB_TOKEN
# Secret from deployer.secrets.yaml
keyPassword:
from: secret
key: android_key_pass
# File on disk
apiKey:
from: file
path: ./secrets/nuget-key.txt
# Base64-encoded env var (decoded at runtime)
storePassword:
from: env
name: ENCODED_PASS
encoding: base64All signing parameters (storePassword, keyAlias, keyPassword) use the flexible value source model described above.
android:
signing:
keystore:
from: file
path: ./android/release.keystore
storePassword:
from: env
name: ANDROID_STORE_PASS
keyAlias: release-key
keyPassword:
from: env
name: ANDROID_KEY_PASSandroid:
signing:
keystore:
from: env
name: ANDROID_KEYSTORE_BASE64
encoding: base64
storePassword:
from: env
name: ANDROID_STORE_PASS
keyAlias: release-key
keyPassword:
from: env
name: ANDROID_KEY_PASSandroid:
signing:
keystore:
from: secret
key: android_keystore_base64
encoding: base64
storePassword:
from: secret
key: android_store_pass
keyAlias:
from: secret
key: android_key_alias
keyPassword:
from: secret
key: android_key_passandroid:
signing:
keystore:
from: env
name: ANDROID_KEYSTORE_BASE64
encoding: base64
storePassword:
from: env
name: ANDROID_STORE_PASS
keyAlias: release-key # literal shorthand
keyPassword:
from: secret
key: android_key_passandroid:
signing:
keystore:
from: file
path: ./android/debug.keystore
storePassword: android
keyAlias: androiddebugkey
keyPassword: androidThe secrets file (deployer.secrets.yaml) is a flat YAML file in the repo root:
android_keystore_base64: <base64-encoded-keystore>
android_key_alias: myalias
android_key_pass: secretpassword
android_store_pass: secretpasswordImportant: Add deployer.secrets.yaml to .gitignore.
Secrets can also be stored in the system keyring and referenced with the same from: secret syntax:
dotnetdeployer secrets set nuget_api_key
dotnetdeployer secrets check nuget_api_key
dotnetdeployer secrets delete nuget_api_keyThe keyring backend is Windows Credential Manager, macOS Keychain, or Linux Secret Service via secret-tool. from: secret resolves deployer.secrets.yaml first for backward compatibility, then falls back to the system keyring.
To encode a keystore as base64:
base64 -w 0 < your-release.keystore| Issue | Solution |
|---|---|
Version is always 1.0.0 |
Ensure fetchDepth: 0 in checkout step |
| GitVersion not found | Tool auto-installs, but ensure .NET SDK 8.0+ is available |
| NuGet push fails | Check NUGET_API_KEY is set correctly in variable group |
| GitHub release fails | Ensure GITHUB_TOKEN has repo scope |
| Android keystore invalid base64 | Re-encode with base64 -w 0 < your.keystore |
| Android signing env var missing | Check all ANDROID_* vars are mapped in pipeline env: block |
| Secret key not found | Ensure deployer.secrets.yaml contains the key, or store it with dotnetdeployer secrets set <key> |