Comprehensive guide for installing and configuring Azure DevOps Server (formerly Team Foundation Server) on-premises.
- Introduction
- System Requirements
- Pre-Installation Planning
- Installation Steps
- Post-Installation Configuration
- High Availability Setup
- Upgrade and Migration
- Backup and Restore
- Troubleshooting
Azure DevOps Server is the on-premises version of Azure DevOps Services, providing:
- Complete control over data and infrastructure
- Compliance with data residency requirements
- Integration with on-premises systems
- Offline capabilities
| Feature | Azure DevOps Server | Azure DevOps Services |
|---|---|---|
| Hosting | On-premises | Cloud-hosted by Microsoft |
| Maintenance | Self-managed | Managed by Microsoft |
| Updates | Manual | Automatic |
| Cost | License required | Free for up to 5 users |
| Integration | On-premises systems | Azure services |
| Data Residency | On-premises | Microsoft datacenters |
- Express: Free for up to 5 users (single server)
- Professional: For teams requiring advanced features
- Enterprise: For large organizations with complex needs
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 4-core 64-bit | 8-core 64-bit |
| RAM | 8 GB | 16 GB |
| Disk (Application Tier) | 100 GB | 200 GB SSD |
| Disk (Data Tier) | 500 GB | 1 TB SSD |
| Network | 1 Gbps | 1 Gbps |
| Component | Recommended |
|---|---|
| CPU | 16-core 64-bit |
| RAM | 32 GB |
| Disk (Application Tier) | 500 GB SSD |
| Disk (Data Tier) | 2 TB SSD |
| Network | 10 Gbps |
Supported Windows Server Versions:
- Windows Server 2022 (Standard/Datacenter)
- Windows Server 2019 (Standard/Datacenter)
- Windows Server 2016 (Standard/Datacenter)
Note: Windows Server Core is not supported.
Supported SQL Server Versions:
- SQL Server 2022 (Express/Standard/Enterprise)
- SQL Server 2019 (Express/Standard/Enterprise)
- SQL Server 2017 (Express/Standard/Enterprise)
SQL Server Express Limitations:
- Maximum database size: 10 GB
- Limited CPU and memory
- No high availability features
- Microsoft Edge (latest version)
- Google Chrome (latest version)
- Mozilla Firefox (latest version)
- .NET Framework 4.8 or later
- Windows PowerShell 5.1 or later
- IIS 10.0 or later
┌─────────────────────────────────┐
│ Azure DevOps Server (Single) │
│ ┌───────────────────────────┐ │
│ │ Application Tier │ │
│ │ - Web Services │ │
│ │ - API Services │ │
│ │ - Background Services │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ Data Tier │ │
│ │ - SQL Server │ │
│ │ - Reporting Services │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
Use Case: Small teams (< 50 users), development/testing
┌─────────────────────┐
│ Load Balancer │
└──────────┬──────────┘
│
┌──────┴──────┐
│ │
┌───▼────┐ ┌───▼────┐
│ App 1 │ │ App 2 │
└────────┘ └────────┘
│ │
└──────┬──────┘
│
┌──────▼────────┐
│ SQL Server │
│ (Always On) │
└───────────────┘
Use Case: Large organizations, production environments
| Port | Protocol | Purpose |
|---|---|---|
| 80 | HTTP | Web access (non-SSL) |
| 443 | HTTPS | Web access (SSL) |
| 1433 | TCP | SQL Server |
| 1434 | UDP | SQL Server Browser |
| 9192 | TCP | Build Controller |
| 9191 | TCP | Build Agent |
# Create DNS A record for Azure DevOps Server
Add-DnsServerResourceRecordA -Name "tfs" -ZoneName "yourdomain.com" -IPv4Address "192.168.1.10"
# Create CNAME record for friendly URL
Add-DnsServerResourceRecordCName -Name "devops" -HostNameAlias "tfs.yourdomain.com" -ZoneName "yourdomain.com"| Service | Account Type | Permissions |
|---|---|---|
| Azure DevOps Server | Domain User | Local Administrator |
| SQL Server | Domain User | Local Administrator, SQL sysadmin |
| Build Service | Domain User | Local Administrator |
| Reporting Services | Domain User | Local Administrator |
# Create service account for Azure DevOps Server
New-ADUser -Name "TFS_Service" `
-SamAccountName "tfs_service" `
-UserPrincipalName "tfs_service@yourdomain.com" `
-Path "OU=Service Accounts,DC=yourdomain,DC=com" `
-AccountPassword (ConvertTo-SecureString "SecurePassword123!" -AsPlainText -Force) `
-Enabled $true `
-PasswordNeverExpires $true
# Add to local administrators group
Add-LocalGroupMember -Group "Administrators" -Member "YOURDOMAIN\tfs_service"
# Create SQL Server service account
New-ADUser -Name "SQL_Service" `
-SamAccountName "sql_service" `
-UserPrincipalName "sql_service@yourdomain.com" `
-Path "OU=Service Accounts,DC=yourdomain,DC=com" `
-AccountPassword (ConvertTo-SecureString "SecurePassword123!" -AsPlainText -Force) `
-Enabled $true `
-PasswordNeverExpires $true# Download SQL Server 2022 Express
# From: https://www.microsoft.com/en-us/sql-server/sql-server-downloads
# Or use PowerShell to download
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/?linkid=866658" -OutFile "SQLServer2022-x64-ENU.exe"# Extract SQL Server
.\SQLServer2022-x64-ENU.exe /Q /x:SQLSetup
# Install SQL Server with configuration file
.\SQLSetup\setup.exe /ConfigurationFile=SQLConfig.iniSQLConfig.ini:
;SQL Server 2022 Configuration File
[OPTIONS]
ACTION="Install"
IACCEPTSQLSERVERLICENSETERMS="True"
QUIET="True"
QUIETSIMPLE="False"
INSTANCENAME="MSSQLSERVER"
INSTANCEID="MSSQLSERVER"
SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"
AGTSVCACCOUNT="YOURDOMAIN\sql_service"
AGTSVCPASSWORD="SecurePassword123!"
SQLSVCACCOUNT="YOURDOMAIN\sql_service"
SQLSVCPASSWORD="SecurePassword123!"
ISSVCACCOUNT="NT AUTHORITY\Network Service"
SQLSYSADMINACCOUNTS="YOURDOMAIN\tfs_service","BUILTIN\Administrators"
SECURITYMODE="SQL"
SAPWD="SecurePassword123!"
TCPENABLED="1"
NPENABLED="0"
BROWSERSVCSTARTUPTYPE="Automatic"# Enable TCP/IP protocol
Import-Module SqlServer
$smo = 'Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer'
$wmi = New-Object ($smo) $env:COMPUTERNAME
$uri = "ManagedComputer[@Name='$env:COMPUTERNAME']/ServerInstance[@Name='MSSQLSERVER']/ServerProtocol[@Name='Tcp']"
$Tcp = $wmi.GetSmoObject($uri)
$Tcp.IsEnabled = $true
$Tcp.Alter()
# Restart SQL Server service
Restart-Service -Name "MSSQLSERVER" -Force
# Configure firewall rules
New-NetFirewallRule -DisplayName "SQL Server" -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow
New-NetFirewallRule -DisplayName "SQL Server Browser" -Direction Inbound -Protocol UDP -LocalPort 1434 -Action Allow# Download Azure DevOps Server 2022
# From: https://visualstudio.microsoft.com/downloads/
# Or use PowerShell
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/?linkid=2208958" -OutFile "AzureDevOpsServer2022.exe"# Run prerequisites checker
.\AzureDevOpsServer2022.exe /layout
.\AzureDevOpsServer2022.exe /prereqOption 1: Basic Installation (Single Server)
# Run installer
.\AzureDevOpsServer2022.exe
# Follow the wizard:
# 1. Click "Install Azure DevOps Server"
# 2. Accept license terms
# 3. Choose "Basic" installation type
# 4. Configure:
# - Application Tier URL: https://devops.yourdomain.com
# - Database: Local SQL Server
# - Authentication: NTLM or Kerberos
# - Search: Install search service (optional)
# 5. Click "Install"
# 6. Wait for installation to completeOption 2: Advanced Installation (Multi-Server)
# Run installer
.\AzureDevOpsServer2022.exe
# Follow the wizard:
# 1. Click "Install Azure DevOps Server"
# 2. Accept license terms
# 3. Choose "Advanced" installation type
# 4. Select components:
# - Application Tier
# - Database (on separate server)
# - Reporting Services (optional)
# - Search Service (optional)
# 5. Configure each component separately
# 6. Click "Install"# Open Azure DevOps Server Administration Console
# Navigate to: Start > Azure DevOps Server Administration Console
# Configure application tier:
# 1. Click "Application Tier"
# 2. Configure:
# - URL: https://devops.yourdomain.com
# - Port: 443 (HTTPS)
# - Authentication: Negotiate (Kerberos)
# - Service Account: YOURDOMAIN\tfs_service
# 3. Click "Apply"# Request certificate from internal CA
$certDistinguishedName = "CN=devops.yourdomain.com, OU=IT, O=YourCompany, L=YourCity, S=YourState, C=US"
$certTemplate = "WebServer"
Get-Certificate -Template $certTemplate -DnsName "devops.yourdomain.com" -SubjectName $certDistinguishedName -CertStoreLocation "Cert:\LocalMachine\My"# Import PFX certificate
$certPath = "C:\Certificates\devops.yourdomain.com.pfx"
$certPassword = ConvertTo-SecureString "CertificatePassword" -AsPlainText -Force
Import-PfxCertificate -FilePath $certPath -CertStoreLocation "Cert:\LocalMachine\My" -Password $certPassword# Bind certificate to IIS
$certThumbprint = (Get-ChildItem -Path "Cert:\LocalMachine\My" | Where-Object {$_.Subject -like "*devops.yourdomain.com*"}).Thumbprint
# Configure IIS binding
Import-Module WebAdministration
New-WebBinding -Name "Default Web Site" -Protocol https -Port 443 -HostHeader "devops.yourdomain.com"
# Bind certificate
$binding = Get-WebBinding -Name "Default Web Site" -Protocol https
$binding.AddSslCertificate($certThumbprint, "My")# Open Azure DevOps Server Administration Console
# Navigate to: Project Collections
# Click "Create Collection"
# Configure:
# - Collection Name: DefaultCollection
# - Description: Default project collection
# - Database: Create new database
# - Database Name: TFS_DefaultCollection
# - Reporting Services: Configure if needed
# Click "Create"# Configure collection properties
# Navigate to: Project Collections > DefaultCollection > General
# Configure:
# - Collection URL: https://devops.yourdomain.com/DefaultCollection
# - Service Account: YOURDOMAIN\tfs_service
# - Permission Model: Project Collection
# Click "Save"# Open Azure DevOps Server web portal
# Navigate to: Project Settings > Agent Pools
# Click "Add pool"
# Configure:
# - Pool Name: Default
# - Type: Self-hosted
# Click "Create"# Create agent directory
mkdir C:\Agent
cd C:\Agent
# Download agent package
# Navigate to: https://devops.yourdomain.com/_admin/_AgentPool
# Download Windows agent package
# Extract agent
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory("agent.zip", ".")
# Configure agent
.\config.cmd
# Follow prompts:
# - Enter server URL: https://devops.yourdomain.com
# - Enter authentication type: Integrated
# - Enter agent pool: Default
# - Enter agent name: (accept default)
# - Enter work folder: _work
# - Run as service: Y
# - Service account: YOURDOMAIN\tfs_service
# Start agent
.\run.cmd# Open Azure DevOps Server Administration Console
# Navigate to: Application Tier > Search
# Click "Install Search Service"
# Configure:
# - Search Service Account: YOURDOMAIN\tfs_service
# - Search Service URL: https://devops.yourdomain.com
# Click "Install"# Configure search settings
# Navigate to: Application Tier > Search > Indexes
# Configure:
# - Code Search: Enable
# - Work Item Search: Enable
# - Wiki Search: Enable
# Click "Save"# Install SQL Server Reporting Services
# Download from: https://www.microsoft.com/en-us/sql-server/sql-server-downloads
# Run installer
.\SSRSSetup.exe
# Follow wizard:
# 1. Install Reporting Services
# 2. Configure Report Server
# 3. Connect to Azure DevOps Server# Open Azure DevOps Server Administration Console
# Navigate to: Project Collections > DefaultCollection > Reporting
# Configure:
# - Report Server URL: https://reports.yourdomain.com/ReportServer
# - Report Manager URL: https://reports.yourdomain.com/Reports
# Click "Apply"# Open Azure DevOps Server Administration Console
# Navigate to: Application Tier > Email Settings
# Configure:
# - SMTP Server: smtp.yourdomain.com
# - SMTP Port: 587
# - From Address: devops@yourdomain.com
# - Use SSL: Yes
# - SMTP User: devops@yourdomain.com
# - SMTP Password: [password]
# Click "Test Email"
# Click "Save"# Enable Always On Availability Groups
Enable-SqlAlwaysOn -ServerName "SQL01" -Force
# Create availability group
New-SqlAvailabilityGroup `
-Name "TFS_AG" `
-Path "SQLSERVER:\SQL\SQL01\Default" `
-AvailabilityReplica (
New-SqlAvailabilityReplica `
-Name "SQL01" `
-EndpointUrl "TCP://SQL01.yourdomain.com:5022" `
-FailoverMode "Automatic" `
-AvailabilityMode "SynchronousCommit" `
-ConnectionModeInSecondaryRole "AllowAllConnections" `
-AsTemplate
),
(New-SqlAvailabilityReplica `
-Name "SQL02" `
-EndpointUrl "TCP://SQL02.yourdomain.com:5022" `
-FailoverMode "Automatic" `
-AvailabilityMode "SynchronousCommit" `
-ConnectionModeInSecondaryRole "AllowAllConnections" `
-AsTemplate
)
# Add database to availability group
Add-SqlAvailabilityDatabase `
-Path "SQLSERVER:\SQL\SQL01\Default\AvailabilityGroups\TFS_AG" `
-Database "TFS_Configuration"# Create availability group listener
$sqlAG = Get-SqlAvailabilityGroup -Path "SQLSERVER:\SQL\SQL01\Default\AvailabilityGroups\TFS_AG"
New-SqlAvailabilityGroupListener `
-Name "TFS_Listener" `
-StaticIp "192.168.1.100/255.255.255.0" `
-Port 1433 `
-Path $sqlAG.Path# Install Network Load Balancing (NLB) on Windows Server
# Or use hardware load balancer
# Configure NLB cluster
Import-Module NetworkLoadBalancingClusters
New-NlbCluster -ClusterName "TFS_NLB" -ClusterPrimaryIP "192.168.1.200" -SubnetMask "255.255.255.0"
Add-NlbClusterNode -ClusterName "TFS_NLB" -HostName "TFS01"
Add-NlbClusterNode -ClusterName "TFS_NLB" -HostName "TFS02"
# Configure port rules
Add-NlbClusterPortRule -ClusterName "TFS_NLB" -StartPort 80 -EndPort 80 -Protocol TCP -Mode Multiple
Add-NlbClusterPortRule -ClusterName "TFS_NLB" -StartPort 443 -EndPort 443 -Protocol TCP -Mode Multiple# On each application tier server:
# Install Azure DevOps Server (Application Tier only)
# Configure to use same SQL Server availability group
# Configure with same URL and settings# Backup databases
# Backup encryption keys
# Check system requirements
# Review release notes
# Test upgrade in staging environment# Download latest Azure DevOps Server version
# From: https://visualstudio.microsoft.com/downloads/
# Run upgrade wizard
.\AzureDevOpsServer2022.exe
# Follow wizard:
# 1. Click "Upgrade Azure DevOps Server"
# 2. Accept license terms
# 3. Review upgrade summary
# 4. Click "Upgrade"
# 5. Wait for upgrade to complete
# 6. Verify upgrade success# Install TFS Migration Tools
# Download from: https://github.com/microsoft/tfsmigrationtools
# Configure migration
# Create configuration.json fileconfiguration.json:
{
"Source": {
"Collection": "https://oldtfs.yourdomain.com/DefaultCollection",
"Project": "OldProject",
"ReflectedWorkItemIDFieldName": "TfsMigrationTool.ReflectedWorkItemId"
},
"Target": {
"Collection": "https://devops.yourdomain.com/DefaultCollection",
"Project": "NewProject",
"ReflectedWorkItemIDFieldName": "TfsMigrationTool.ReflectedWorkItemId"
},
"WorkItems": {
"Enabled": true
}
}# Run migration tool
.\TfsMigrationTool.exe configuration.json
# Monitor migration progress
# Verify migration results# Open Azure DevOps Server Administration Console
# Navigate to: Application Tier > Backup
# Click "Backup Encryption Key"
# Save to secure location# Open Azure DevOps Server Administration Console
# Navigate to: Application Tier > Scheduled Backup
# Configure:
# - Backup Location: \\backup-server\AzureDevOps
# - Schedule: Daily at 2:00 AM
# - Retention: 30 days
# Click "Save"# Backup all Azure DevOps databases
$databases = @("TFS_Configuration", "TFS_DefaultCollection", "TFS_Warehouse", "ReportServer", "ReportServerTempDB")
foreach ($db in $databases) {
$backupFile = "C:\Backups\$db_$(Get-Date -Format 'yyyyMMdd').bak"
Backup-SqlDatabase -ServerInstance "localhost" -Database $db -BackupFile $backupFile
Write-Host "Backed up $db to $backupFile"
}# Backup Azure DevOps Server configuration
$backupPath = "C:\Backups\FileSystem_$(Get-Date -Format 'yyyyMMdd')"
Copy-Item "C:\Program Files\Azure DevOps Server" -Destination "$backupPath\AzureDevOpsServer" -Recurse
Copy-Item "C:\Program Files\Microsoft Team Foundation Server" -Destination "$backupPath\TFS" -Recurse# Stop Azure DevOps Server services
Stop-Service -Name "TFSJobAgent" -Force
Stop-Service -Name "IISADMIN" -Force
# Restore databases
Restore-SqlDatabase -ServerInstance "localhost" -Database "TFS_Configuration" -BackupFile "C:\Backups\TFS_Configuration_20240101.bak" -ReplaceDatabase
Restore-SqlDatabase -ServerInstance "localhost" -Database "TFS_DefaultCollection" -BackupFile "C:\Backups\TFS_DefaultCollection_20240101.bak" -ReplaceDatabase
# Start services
Start-Service -Name "IISADMIN"
Start-Service -Name "TFSJobAgent"# Open Azure DevOps Server Administration Console
# Navigate to: Application Tier > Backup
# Click "Restore Encryption Key"
# Select backup file
# Enter password (if encrypted)
# Click "Restore"Solution:
# Check SQL Server service status
Get-Service -Name "MSSQLSERVER"
# Check SQL Server configuration
Get-SqlServerNetworkConfiguration -ServerInstance "localhost"
# Test SQL connection
Test-NetConnection -ComputerName localhost -Port 1433
# Verify SQL authentication
sqlcmd -S localhost -U sa -P passwordSolution:
# Check IIS status
Get-Service -Name "IISADMIN"
Get-Service -Name "W3SVC"
# Check application pool
Import-Module WebAdministration
Get-WebAppPoolState -Name "TFSAppPool"
# Restart IIS
iisreset
# Check event logs
Get-EventLog -LogName Application -Source "TFS Job Agent" -Newest 50Solution:
# Check agent service
Get-Service -Name "VSOAgent*"
# Test network connectivity
Test-NetConnection -ComputerName devops.yourdomain.com -Port 443
# Reconfigure agent
cd C:\Agent
.\config.cmd remove
.\config.cmdSolution:
# Check database size
sqlcmd -S localhost -Q "SELECT name, size/128.0 AS SizeInMB FROM sys.master_files WHERE database_id > 4"
# Check database fragmentation
sqlcmd -S localhost -Q "SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED')"
# Rebuild indexes
sqlcmd -S localhost -Q "ALTER INDEX ALL ON [TFS_Configuration].[tbl_AccessControlEntry] REBUILD"
# Update statistics
sqlcmd -S localhost -Q "UPDATE STATISTICS [TFS_Configuration].[tbl_AccessControlEntry]"Solution:
# Configure SQL Server memory limits
sqlcmd -S localhost -Q "EXEC sp_configure 'max server memory', 8192; RECONFIGURE"
# Configure application pool memory limits
Set-WebConfigurationProperty -Filter "system.applicationHost/applicationPools/add[@name='TFSAppPool']" -Name "processModel.privateMemoryMemoryLimit" -Value 1048576- Monitor backup status
- Check service health
- Review error logs
- Review disk space
- Check database growth
- Update statistics
- Review security logs
- Update SSL certificates
- Test restore procedures
# Create monitoring script
$services = @("TFSJobAgent", "IISADMIN", "W3SVC", "MSSQLSERVER")
foreach ($service in $services) {
$status = Get-Service -Name $service -ErrorAction SilentlyContinue
if ($status.Status -ne "Running") {
Write-Host "Service $service is not running" -ForegroundColor Red
# Send alert
}
}- Azure DevOps Server Documentation
- Azure DevOps Server Installation Guide
- SQL Server Documentation
- High Availability Configuration
