Case studies

Homelab build · Homelab implementation · 2025

SQL Server 2022 Failover Cluster Instance: Dual-Layer HA Homelab

SQL Server 2022 FCI on Windows Server 2022 Hyper-V, with WSFC and iSCSI shared storage. Validated through planned failover, a forced node crash and live migration.

~30 s recovery time, zero data loss

  • SQL Server 2022
  • High Availability
  • Hyper-V

01. Overview

PlatformHyper-V on Windows Server 2022
GoalImplement SQL Server 2022 FCI with dual-layer high availability
StorageiSCSI shared disk via Windows Server iSCSI Target
ClusteringWindows Server Failover Clustering (WSFC)
RPOZero — synchronous shared storage
RTO~30 seconds — automatic SQL resource failover
OutcomeFCI validated — automatic failover confirmed across both layers

This project builds a dual-layer high availability architecture for SQL Server 2022 entirely inside a Hyper-V homelab. Layer 1 is Hyper-V's own VM failover and live migration — a node running a cluster VM can fail and the VM moves to another host automatically. Layer 2 is a SQL Server Failover Cluster Instance sitting on top, where the SQL resource group moves between two Windows Server VMs without dropping the connection endpoint. Together they protect the database from both physical host failure and OS-level SQL instance failure independently.

The starting point was a database already running in Azure SQL. The goal was to migrate it into an on-premises FCI, prove the architecture holds under failure conditions, and document the full process — including the parts that did not go smoothly on the first attempt.

02. Architecture

Dual-Layer Design

The architecture stacks two independent failover mechanisms. Each operates at a different layer of the infrastructure and protects against different failure types.

Dual-layer HA architecture diagramFig. 1 — Dual-layer HA: Hyper-V VM failover (Layer 1) + SQL Server FCI (Layer 2)
LayerTechnologyProtects AgainstRecovery
Layer 1Hyper-V VM Failover / Live MigrationPhysical host failureVM moved to surviving Hyper-V host
Layer 2SQL Server FCI (WSFC)OS / SQL instance failureSQL resource group moves to passive node

How SQL Server FCI Works

An FCI is not replication — both nodes access the same physical storage. At any given time one node owns the SQL Server resource group (active) and the other sits idle (passive). The shared disk, SQL Server service, and a Virtual Network Name (VNN) travel together as a unit when failover occurs. Clients connect to the VNN and never address either node directly, so the connection endpoint does not change during a failover event.

Shared storage is provided by an iSCSI target. Both cluster nodes connect to the same iSCSI LUN, but only the active node mounts it at any given time. WSFC coordinates exclusive ownership — it brings the disk online for the active owner and keeps it strictly offline on the passive node.

Full homelab topology diagramFig. 2 — Full homelab topology: Hyper-V host, SQL nodes, storage server, and networking

Quorum

With two cluster nodes a quorum witness is essential. Without it, if one node loses connectivity both nodes believe they are the surviving majority and both attempt to bring resources online — a split-brain scenario that causes data corruption. A file share witness hosted on a third machine provides the tiebreaker vote. This lab uses a file share witness on the storage server.

03. Homelab Environment

Virtual Machines

VMOSvCPURAMRole
SQLNode1Windows Server 202248 GBSQL FCI — primary node
SQLNode2Windows Server 202248 GBSQL FCI — secondary node
StorageSrvWindows Server 202224 GBiSCSI target & file share witness

Networking & Virtual Switches

Each VM has two virtual network adapters: one for management traffic and one dedicated to cluster heartbeat communication. Separating these is a WSFC best practice — cluster heartbeat traffic on a dedicated network prevents management congestion from masking node health and prevents false-positive failovers.

Hyper-V Host — Create Virtual Switches (PowerShell)
New-VMSwitch -Name "Management" -NetAdapterName "Ethernet" -AllowManagementOS $true
New-VMSwitch -Name "ClusterHeartbeat" -SwitchType Internal

# Assign adapters to each SQL node
Add-VMNetworkAdapter -VMName "SQLNode1" -SwitchName "Management"
Add-VMNetworkAdapter -VMName "SQLNode1" -SwitchName "ClusterHeartbeat"
Add-VMNetworkAdapter -VMName "SQLNode2" -SwitchName "Management"
Add-VMNetworkAdapter -VMName "SQLNode2" -SwitchName "ClusterHeartbeat"
Single-NIC Constraint

The homelab runs on a single physical NIC, which means management, iSCSI, cluster heartbeat, and Hyper-V live migration traffic all share the same physical link. In production this is a known anti-pattern — iSCSI benefits from a dedicated 10GbE NIC and heartbeat should be isolated from data traffic. In this lab it works, but it represents a single physical point of failure that a production design must address.

04. iSCSI Shared Storage

Storage Server — iSCSI Target

The storage server presents a block device (LUN) to the cluster nodes over the network as if it were locally attached disk. Windows Server 2022 includes an iSCSI Target Server role that handles this without any third-party software. The virtual disk is a .vhdx file on the storage server — WSFC cluster nodes see it as a raw disk once connected.

StorageSrv — Install iSCSI Target & Create LUN (PowerShell)
Install-WindowsFeature FS-iSCSITarget-Server -IncludeManagementTools

# Create the virtual disk that will be the shared cluster disk
New-IscsiVirtualDisk -Path "C:\iSCSI\SQLClusterDisk.vhdx" -Size 50GB

# Create the iSCSI target, allow only the two SQL nodes to connect
New-IscsiServerTarget -TargetName "sql-fci-target" `
    -InitiatorIds "IQN:iqn.1991-05.com.microsoft:sqlnode1",`
              "IQN:iqn.1991-05.com.microsoft:sqlnode2"

# Map the virtual disk to the target
Add-IscsiVirtualDiskTargetMapping -TargetName "sql-fci-target" `
    -Path "C:\iSCSI\SQLClusterDisk.vhdx"

SQL Nodes — iSCSI Initiator

Both SQL nodes connect to the same target. The key point is that both must see the same disk before the cluster is formed — WSFC handles exclusive ownership at the cluster level after that. The iSCSI initiator service needs to be running and set to auto-start on both nodes.

SQLNode1 & SQLNode2 — Connect iSCSI Initiator (PowerShell)
Start-Service MSiSCSI
Set-Service MSiSCSI -StartupType Automatic

# Discover the target portal (StorageSrv IP on the iSCSI network)
New-IscsiTargetPortal -TargetPortalAddress "10.10.0.10"

# Connect persistently so the connection survives reboots
Connect-IscsiTarget `
    -NodeAddress "iqn.1991-05.com.microsoft:storagesrv-sql-fci-target" `
    -IsPersistent $true

# Verify session is connected
Get-IscsiSession | Select-Object IsConnected, TargetNodeAddress

Once both nodes are connected, the LUN appears in Disk Management on both. Before forming the cluster, the disk needs to be initialised and formatted on one node only — WSFC takes exclusive control of it once the cluster is created.

Do Not Format on Both Nodes

Initialising and formatting the shared disk simultaneously on both nodes will corrupt it. Bring the disk online and format it on SQLNode1 only. SQLNode2 must see the disk in Disk Management but leave it offline until WSFC owns it.

05. Windows Server Failover Clustering

Install Failover Clustering Feature

SQLNode1 & SQLNode2 — Install Feature (PowerShell)
Install-WindowsFeature Failover-Clustering -IncludeManagementTools
Install-WindowsFeature RSAT-Clustering-PowerShell

Restart-Computer -Force

Cluster Validation

Before creating the cluster, WSFC requires a validation run across all candidate nodes. This tests storage access, networking, and system configuration and flags anything that would prevent the cluster from functioning. Skipping this step is not an option for a supported configuration — Microsoft will not provide support for a cluster that bypassed validation.

SQLNode1 — Run Cluster Validation (PowerShell)
Test-Cluster -Node "SQLNode1","SQLNode2" `
    -Include "Storage","Network","System Configuration" `
    -ReportName "C:\ClusterValidation\report"
Cluster validation reportCluster validation report — all storage and network tests passed

Create the Cluster

SQLNode1 — Create Failover Cluster (PowerShell)
# Cluster name and IP must be unique in DNS / Active Directory
New-Cluster -Name "SQLCLUSTER" `
    -Node "SQLNode1","SQLNode2" `
    -StaticAddress "192.168.1.50" `
    -NoStorage

# Add the shared iSCSI disk to the cluster
Get-ClusterAvailableDisk | Add-ClusterDisk

Quorum — File Share Witness

With two nodes the cluster has an even vote count. A file share witness on the storage server gives the cluster a tiebreaker vote, preventing split-brain if the inter-node link goes down. The cluster computer account needs full control of the share.

SQLNode1 — Configure File Share Witness (PowerShell)
# Create the share on StorageSrv first, grant SQLCLUSTER$ full control
Set-ClusterQuorum -Cluster "SQLCLUSTER" `
    -FileShareWitness "\\StorageSrv\SQLClusterWitness"

Get-ClusterQuorum

06. SQL Server FCI Installation

Node 1 — Create the FCI

The FCI installation runs from SQL Server 2022 setup with the InstallFailoverCluster action. This creates the SQL resource group inside WSFC, claims the shared disk, and registers the Virtual Network Name in DNS. The VNN is the name clients will use to connect — it stays stable across failovers.

SQLNode1 — Install SQL Server 2022 FCI (setup.exe)
setup.exe /ACTION=InstallFailoverCluster `
    /FEATURES=SQLENGINE `
    /INSTANCENAME="MSSQLSERVER" `
    /SQLSVCACCOUNT="DOMAIN\sqlservice" `
    /SQLSVCPASSWORD="P@ssw0rd!" `
    /AGTSVCACCOUNT="DOMAIN\sqlservice" `
    /INSTALLSQLDATADIR="S:\SQLData" `
    /FAILOVERCLUSTERNETWORKNAME="SQLFCI" `
    /FAILOVERCLUSTERIPADDRESSES="IPv4;192.168.1.60;Management;255.255.255.0" `
    /FAILOVERCLUSTERDISKS="Cluster Disk 1" `
    /IACCEPTSQLSERVERLICENSETERMS

The FAILOVERCLUSTERDISKS value must exactly match the disk name as it appears in Failover Cluster Manager — a mismatch here causes a silent failure during setup that is difficult to diagnose. Verify the exact name with Get-ClusterResource | Where-Object ResourceType -eq "Physical Disk" first.

Node 2 — Add to the Existing FCI

SQLNode2 — Add Node to FCI (setup.exe)
setup.exe /ACTION=AddNode `
    /INSTANCENAME="MSSQLSERVER" `
    /FAILOVERCLUSTERNETWORKNAME="SQLFCI" `
    /SQLSVCACCOUNT="DOMAIN\sqlservice" `
    /SQLSVCPASSWORD="P@ssw0rd!" `
    /AGTSVCACCOUNT="DOMAIN\sqlservice" `
    /IACCEPTSQLSERVERLICENSETERMS
Failover Cluster Manager showing SQL Server FCIFailover Cluster Manager — SQL Server resource group active on SQLNode1, SQLNode2 passive

Database Migration from Azure SQL

The existing database in Azure SQL was migrated to the on-premises FCI using a .bacpac export. Azure SQL's export function produces a logical backup containing the full schema and data. This was imported into the FCI instance using SQL Server Management Studio, then the database files were moved to the shared cluster disk so they become part of the SQL resource group.

bacpac vs. Native Backup

Azure SQL exports produce a .bacpac (schema + data, no transaction log history). A native .bak backup preserves transaction log history and allows point-in-time restore. For a lab migration either works. In production, use log shipping or Azure Database Migration Service to minimise the cutover window.

07. Failover Testing

Test 1 — Planned Failover

A planned failover moves the SQL resource group cleanly from the active node to the passive node. All in-flight transactions are committed first, the disk is taken offline on the current owner, resources come online on the target node, and clients reconnect via the VNN automatically. The connection interruption for clients was approximately 8 seconds.

PowerShell — Planned Failover
# Check current owner before failover
Get-ClusterGroup -Name "SQL Server (MSSQLSERVER)" | Select-Object Name, OwnerNode, State

# Move the SQL resource group to Node2
Move-ClusterGroup -Name "SQL Server (MSSQLSERVER)" -Node "SQLNode2"

# Verify new owner after move
Get-ClusterGroup -Name "SQL Server (MSSQLSERVER)" | Select-Object Name, OwnerNode, State

Test 2 — Unplanned Failover

The unplanned test simulates a hard node crash — SQLNode1 was forcibly shut down from Hyper-V Manager without an OS shutdown. WSFC detected the loss of heartbeat within the configured failure threshold and brought the SQL resource group online on SQLNode2 automatically. Total outage from shutdown to SQL available on Node2: approximately 28 seconds.

Test 3 — Layer 1 Hyper-V Live Migration

With the FCI active on SQLNode2, a Hyper-V live migration was performed — the SQLNode2 VM was moved from one Hyper-V host to another while SQL was running and the FCI was active. SQL remained available throughout with zero connection interruption. This validates the dual-layer design: the FCI layer and the Hyper-V layer operate independently and simultaneously without interfering.

Failover Cluster Manager post-failoverPost-failover state — SQL Server resource group now owned by SQLNode2, SQLNode1 passive
TestMethodOutage WindowResult
Planned failoverMove-ClusterGroup~8 secondsPass
Unplanned failoverForce shutdown SQLNode1~28 secondsPass
Hyper-V live migrationVM live migrate with FCI active0 secondsPass

08. Issues & Observations

What Worked Well

  • The VNN approach means application connection strings never change during failover — this is the primary practical advantage over alternatives for legacy applications that cannot handle read-intent routing
  • WSFC cluster validation caught two configuration issues before cluster formation: a mismatched NIC binding order and a DNS registration conflict — both were easy to fix once flagged, but would have been hard to diagnose at runtime
  • File share witness quorum worked reliably across all three test scenarios without manual intervention
  • The dual-layer design held up correctly: Hyper-V live migration and SQL FCI failover are independent and do not interfere with each other

Limitations & What Would Change in Production

IssueImpact in This LabProduction Fix
Single physical NICManagement, iSCSI, and heartbeat share one link — link failure takes everything down at onceDedicated 10GbE NIC for iSCSI; separate NIC for cluster heartbeat
iSCSI storage SPOFStorageSrv is a single VM — if it fails, both SQL nodes lose the shared disk and the cluster haltsReplicated SAN, Windows Storage Spaces Direct (S2D), or cloud witness storage
No redundant quorum witnessIf StorageSrv is unavailable the file share witness is gone — on a two-node cluster this risks losing quorum on the next node failureAzure Cloud Witness removes the dependency on an on-premises VM entirely
No readable secondaryPassive node is completely idle — no reporting traffic offloadAlways On AG with a synchronous secondary for read scale-out (requires Enterprise)

FCI vs. HA Alternatives

SQL Server offers three built-in HA strategies at different complexity and licensing levels. FCI was the right choice here for validating the shared-storage cluster mechanics and for applications that require a single stable connection name. In production the decision depends on RPO requirements, application compatibility, and licensing budget.

OptionRPORTOReadable SecondaryLicense
FCIZero (shared storage)~30sNoStandard or Enterprise
Always On AG (sync)Near-zero~20–30sYes (Enterprise)Enterprise
Azure SQL Managed InstanceSeconds (geo-replication)Minutes (geo-failover)YesPer-vCore + Azure cost

09. References