How to Set Up GitLab Runner on Windows Server 2025

GitLab Runner is the open-source agent that picks up CI/CD jobs defined in your .gitlab-ci.yml files and executes them on the host where the runner is installed. Running a GitLab Runner on Windows Server 2025 is the right choice when your pipelines build Windows executables, run MSBuild or NuGet restore operations, execute PowerShell-based test suites, or need access to Windows-only dependencies. This guide covers every stage of the setup: downloading the binary, choosing the right executor, registering with your GitLab instance, running as a Windows service, and tuning the configuration file for reliable operation.

Prerequisites

  • Windows Server 2025 with administrator privileges
  • Network access to your GitLab instance (cloud gitlab.com or self-managed)
  • A GitLab project or group with Maintainer or Owner role (to obtain a registration token)
  • PowerShell 5.1 or later
  • Optional: Docker Desktop for Windows if using the docker-windows executor

Step 1: Download gitlab-runner.exe

GitLab publishes pre-built binaries for Windows on its package repository. Download the latest stable 64-bit binary with PowerShell:

# Create a dedicated directory for GitLab Runner
New-Item -ItemType Directory -Path "C:GitLab-Runner" -Force

# Download the latest gitlab-runner binary
$runnerUrl = "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe"
Invoke-WebRequest -Uri $runnerUrl -OutFile "C:GitLab-Runnergitlab-runner.exe"

# Verify the download
Get-FileHash "C:GitLab-Runnergitlab-runner.exe" -Algorithm SHA256

Add C:GitLab-Runner to the system PATH so the binary is available in every PowerShell and Command Prompt session:

$currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
[System.Environment]::SetEnvironmentVariable(
    "PATH",
    "$currentPath;C:GitLab-Runner",
    [System.EnvironmentVariableTarget]::Machine
)

# Reload PATH in current session
$env:PATH += ";C:GitLab-Runner"

# Confirm binary is accessible
gitlab-runner --version

Step 2: Obtain a Registration Token

Registration tokens determine which GitLab scope the runner is available to. Tokens are found in:

  • Project-level: Settings → CI/CD → Runners → New project runner (GitLab 16+) or expand Specific runners section
  • Group-level: Group → Settings → CI/CD → Runners
  • Instance-level: Admin Area → CI/CD → Runners (self-managed only)

For GitLab 16 and later the legacy registration token is replaced by runner authentication tokens generated after creating a runner object in the UI. Copy the token — it is shown only once.

Step 3: Register the Runner

Run the interactive registration command. For unattended registration pass all flags directly:

# Interactive registration
gitlab-runner register

# Unattended registration (shell executor — recommended for Windows builds)
gitlab-runner register `
    --non-interactive `
    --url "https://gitlab.example.com/" `
    --token "glrt-xxxxxxxxxxxxxxxxxxxx" `
    --executor "shell" `
    --shell "powershell" `
    --description "win2025-shell-runner" `
    --tag-list "windows,powershell,dotnet" `
    --run-untagged="false" `
    --locked="false"

For the docker-windows executor (requires Docker Desktop with Windows containers enabled):

gitlab-runner register `
    --non-interactive `
    --url "https://gitlab.example.com/" `
    --token "glrt-xxxxxxxxxxxxxxxxxxxx" `
    --executor "docker-windows" `
    --docker-image "mcr.microsoft.com/windows/servercore:ltsc2025" `
    --description "win2025-docker-runner" `
    --tag-list "windows,docker"

Step 4: Review config.toml

After registration, the configuration is written to C:GitLab-Runnerconfig.toml. Open it to verify and tune settings:

concurrent = 4
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "win2025-shell-runner"
  url = "https://gitlab.example.com/"
  id = 42
  token = "glrt-xxxxxxxxxxxxxxxxxxxx"
  token_obtained_at = 2025-09-01T00:00:00Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "shell"
  shell = "powershell"
  [runners.custom_build_dir]
  [runners.cache]
    MaxUploadedArchiveSize = 0
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]

Key settings to adjust for Windows production use:

  • concurrent — maximum number of simultaneous jobs across all runners on this host
  • shell = "powershell" — use PowerShell rather than cmd for all shell steps
  • builds_dir — set a custom build workspace path (e.g., "D:\GitLab\builds")
  • cache_dir — set a custom cache path to persist dependencies between jobs
[[runners]]
  name = "win2025-shell-runner"
  url = "https://gitlab.example.com/"
  token = "glrt-xxxxxxxxxxxxxxxxxxxx"
  executor = "shell"
  shell = "powershell"
  builds_dir = "D:\GitLab\builds"
  cache_dir = "D:\GitLab\cache"

Step 5: Install the Runner as a Windows Service

Running gitlab-runner as a Windows service ensures it starts automatically at boot and can be managed with standard Windows service tools:

# Install the Windows service (runs as SYSTEM by default)
gitlab-runner install

# Start the service
gitlab-runner start

# Verify it is running
Get-Service -Name "gitlab-runner"

# View live logs
gitlab-runner status
Start-Process "eventvwr.msc"   # check Application log for runner events

To run the service as a specific user account rather than SYSTEM (recommended for domain environments):

gitlab-runner install `
    --user "DOMAINsvc-glrunner" `
    --password "ServiceAccount!Pass25"

Step 6: Configure a PowerShell Executor Pipeline

With the shell executor configured to use PowerShell, your .gitlab-ci.yml jobs run natively in PowerShell. Here is an example pipeline for a .NET application:

variables:
  DOTNET_CLI_TELEMETRY_OPTOUT: "1"

stages:
  - build
  - test
  - package

build:
  stage: build
  tags:
    - windows
    - powershell
  script:
    - Write-Host "Building on $env:COMPUTERNAME"
    - dotnet restore MyApp.sln
    - dotnet build MyApp.sln --configuration Release --no-restore

test:
  stage: test
  tags:
    - windows
  script:
    - dotnet test MyApp.TestsMyApp.Tests.csproj --configuration Release --logger "trx;LogFileName=results.trx"
  artifacts:
    reports:
      junit: "**/*.trx"
    expire_in: 7 days

package:
  stage: package
  tags:
    - windows
  script:
    - dotnet publish MyAppMyApp.csproj -c Release -o publish
    - Compress-Archive -Path publish* -DestinationPath "MyApp-$env:CI_COMMIT_SHORT_SHA.zip"
  artifacts:
    paths:
      - "*.zip"

Step 7: Troubleshoot Authentication Issues

Common registration and authentication problems on Windows Server 2025 include:

  • TLS certificate errors: If your GitLab instance uses a self-signed certificate, add it to the Windows Trusted Root store or pass --tls-ca-file during registration.
  • Token already used: Runner authentication tokens can only be used once. Generate a new runner in the GitLab UI if registration fails.
  • Service account lacks rights: Ensure the service account has write access to builds_dir and cache_dir.
  • PowerShell execution policy: The runner service may run under a policy that blocks scripts. Fix with:
# Set policy for the service account context (Machine scope)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force
  • Antivirus interference: Exclude C:GitLab-Runner, builds_dir, and cache_dir from real-time scanning to avoid file-lock conflicts during builds.

Step 8: Managing and Unregistering Runners

# List registered runners
gitlab-runner list

# Stop and unregister a specific runner by token
gitlab-runner unregister --token "glrt-xxxxxxxxxxxxxxxxxxxx"

# Stop the Windows service
gitlab-runner stop

# Uninstall the Windows service
gitlab-runner uninstall

Conclusion

A GitLab Runner on Windows Server 2025 gives your GitLab CI/CD pipelines direct access to the Windows SDK, PowerShell modules, COM objects, and Windows-only test frameworks that containerized Linux runners cannot provide. By choosing the shell executor with PowerShell, maintaining a well-structured config.toml, and running the runner as a dedicated service account, you get a reliable and auditable build agent. For further scaling, register multiple runners on additional Windows Server 2025 hosts and assign them different tags to route pipeline jobs by capability — for example, separating GPU-intensive tasks from standard .NET builds.