Loosely Decoupled
GuideSSHPowerShell

How to Enable SSH on Windows Server 2025

Enabling SSH on Windows Server 2025 lets only Administrators and the OpenSSH Users group sign in. You turn it on with PowerShell, then choose who’s in it.

A patent-style drawing of an idle machine beside a three-lane gate where only one barrier is raised, watched by a pigeon in a hard hat. The raised barrier is cyan.
On this page

TL;DR

On Windows Server 2025 the OpenSSH server is installed but not running. In an elevated PowerShell session:

PowerShell
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' |
    Format-Table Name, Enabled, Profile

The stock rule covers the Private profile only. Before connecting, add the profile the server’s network is on; the firewall step below shows how to tell. On a domain-joined server:

PowerShell
Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Profile Domain, Private

On a cloud VM or a workgroup server, whose network is usually Public:

PowerShell
Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Profile Public, Private

Run one, not both: -Profile replaces the rule’s list rather than adding to it.

Then connect from a client:

Command
$ ssh Administrator@server-address

If Set-Service reports that the service does not exist, the capability was removed from your image; the first step below puts it back.

The visible change from Server 2022 is that the install step is usually gone: Server 2025 images carry the OpenSSH.Server capability by default. Server Manager gained a Remote SSH Access toggle on the Local Server page that enables the service. The commands below do the same thing from PowerShell, where they can be scripted, and they check each assumption rather than relying on the image being stock.

Confirm the server capability is present

PowerShell
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*' |
    Format-Table Name, State

On a stock image OpenSSH.Server reads Installed. If it reads NotPresent — a hardened or minimised image may have dropped it — add it the same way as on Server 2022:

PowerShell
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

Start sshd and make it start at boot

The service is registered but stopped. Set the start type before starting it, so a reboot cannot leave the server unreachable:

PowerShell
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd
Get-Service -Name sshd

The first start writes the host keys and sshd_config to C:\ProgramData\ssh\.

Confirm the firewall rule

PowerShell
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue |
    Format-Table Name, Enabled, Direction, Action, Profile

The rule is normally present with Enabled set to True. If it is present but disabled, enable it; if it is absent, create it:

PowerShell
Enable-NetFirewallRule -Name 'OpenSSH-Server-In-TCP'

New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' `
    -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22

Run whichever of the two applies, not both. A rule created this way covers every profile.

The stock rule covers the Private profile only, so check which profile the server’s network is on:

PowerShell
Get-NetConnectionProfile | Format-Table InterfaceAlias, NetworkCategory

A domain-joined server usually reads DomainAuthenticated, and a cloud VM or a workgroup server usually reads Public. The stock rule admits neither. For DomainAuthenticated:

PowerShell
Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Profile Domain, Private

For Public:

PowerShell
Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Profile Public, Private

Run one, not both: -Profile replaces the rule’s list rather than adding to it, so the second command would undo the first. A server with more than one network adapter can report more than one category; then name them all in one command, such as -Profile Domain, Public, Private.

Public applies the rule on any network the server joins, so on a public address let the network firewall in front of the server decide who reaches port 22.

Connect from a client

Command
$ ssh Administrator@server-address

Accept the host key and enter the account’s Windows password. You arrive in cmd.exe; that is the default shell for Windows OpenSSH. To land in PowerShell instead:

PowerShell
New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell `
    -PropertyType String -Force `
    -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'

Expected result

Get-Service sshd reports Running, something is listening on port 22:

PowerShell
Get-NetTCPConnection -LocalPort 22 -State Listen

and a password login from a client succeeds for an administrator. Nobody else can sign in yet, and the next section is why.

Choose who can sign in

On Server 2025, the generated C:\ProgramData\ssh\sshd_config already limits SSH to two local groups:

Text
AllowGroups administrators "openssh users"

This is the change from Server 2022, where the generated file has no such line and every account that can sign in to the server can sign in over SSH. On Server 2025, an account that isn’t an administrator gets in only once it’s a member of OpenSSH Users:

PowerShell
Add-LocalGroupMember -Group 'OpenSSH Users' -Member 'deploy'

There is no root account, so PermitRootLogin does nothing. To keep administrators off SSH entirely, deny their group, which leaves OpenSSH Users as the only way in:

Text
DenyGroups administrators

AllowUsers narrows access further rather than widening it. An account has to pass AllowUsers and AllowGroups both, so a name listed in AllowUsers that isn’t in either group is still refused.

And once key-based login works for the accounts that need it, turn passwords off:

Text
PasswordAuthentication no

Put each new line above the Match Group administrators block at the end of the file. A line added after it belongs to that block: it applies only to administrators, or, for a directive the block doesn’t allow, stops sshd from starting.

Restart the service after any change:

PowerShell
Restart-Service -Name sshd

Where an administrator’s public key has to go is the Windows-specific part; the related article scripts it.

Common failures

Administrator gets Permission denied (publickey)

Error

Permission denied (publickey)

Windows OpenSSH doesn’t read ~\.ssh\authorized_keys for members of the Administrators group. The key belongs in C:\ProgramData\ssh\administrators_authorized_keys, with inheritance removed and only Administrators and SYSTEM granted access.

sshd stopped while the toggle says on

Refresh Server Manager. If they still disagree, the service failed to start, and the usual cause is a broken edit to sshd_config. Event Viewer won’t say so: a broken file leaves the OpenSSH Admin and Operational channels empty, and the System log records only that the service terminated unexpectedly. Check the file without starting anything:

PowerShell
sshd -t

It prints the line number and what is wrong with it, and nothing when the file is valid.

Only administrators can sign in

Even after adding an account to OpenSSH Users, on a non-English installation the group can carry a translated name, such as OpenSSH-Benutzer in German, while sshd_config still names openssh users. Find the local name, put it in the AllowGroups line in lower case, quoted if it contains a space, and restart sshd:

PowerShell
Get-LocalGroup | Where-Object Name -like '*SSH*'

Connection times out with the service running

The rule exists but is disabled, or doesn’t cover the network profile the server is on. The firewall drops the packets, so the client waits and times out rather than being refused:

PowerShell
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' | Format-Table Name, Enabled, Profile

Enabled should read True, and Profile must include the profile the server is on; the firewall step earlier covers both.

Tested with

OS
Windows Server 2025 Datacenter (build 26100.33438), OpenSSH.Server removed and added again for a clean install
OpenSSH
9.5p2
Client
OpenSSH on macOS 27
Date
October 2026

New articles, by email.

A short note when something new is published.