Updated and tested 7 October 2026
Almost every "I can't reach SQL Server from my laptop" ticket comes down to one of seven things, and they fail in a predictable order. Walking that order from the top takes five minutes. Guessing takes an afternoon and usually ends with somebody switching the firewall off to see if that helps, discovering that it does, then leaving it off. Let's not do that.
Here's the sequence, then each step with the commands for Linux and for Windows.
The order a SQL Server remote connection fails in
- The service is running at all
- TCP/IP is enabled as a protocol on the instance
- The instance is listening on an address the client can reach
- The host firewall allows the port
- The provider or cloud firewall allows the port
- The name resolves to the address you think it does
- The login exists and is allowed to authenticate
Steps one to three are server side and cost nothing to check. Four and five are the network. Six and seven are the ones people check first, and they sit at the bottom for a reason: a DNS or password problem produces a completely different failure from a dropped packet, so proving the path first tells you which half of the problem you are in.
Four and five are interchangeable, so check whichever is quicker to reach from where you are sitting. Neither one tells you anything about the other, and the order between them has never mattered to me once.
Six I skip. I connect by IP address on the first attempt every time, which takes name resolution out of the picture, and I only go back to DNS when the IP works and the hostname does not. That combination is rare and it is never the first thing wrong.
Confirm the service is running and listening
On Linux:
systemctl status mssql-server --no-pager
sudo ss -lntp | grep 1433
Both of those want root before they will show you the process column.
When nothing shows on 1433 at all, rule out a different TCP port before anything else, and on a named instance that is the normal case. Otherwise the ss output tells you what you need. A listener on 0.0.0.0:1433 is bound to every interface. A listener on 127.0.0.1:1433 answers nothing from outside the box no matter how open your firewall is.
On Windows, from an elevated PowerShell prompt:
Get-Service MSSQLSERVER
Get-NetTCPConnection -State Listen -LocalPort 1433
Enable TCP/IP on Windows
Fresh Windows installs of SQL Server frequently have TCP/IP disabled, especially Express. Shared memory works, so local tools connect fine and everything looks healthy from the console. Remote clients get nothing.
Configuration Manager
Run SQLServerManager17.msc (SQL Server 2025) or SQLServerManager16.msc (2022) from C:\Windows\SysWOW64. Expand SQL Server Network Configuration, select Protocols for your instance name, right-click TCP/IP and select Enable. Then select SQL Server Services, right-click SQL Server for that instance and select Restart.
Microsoft's page on enabling a server network protocol makes two points worth carrying with you. The engine has to be stopped and restarted before the change takes effect, and named instances want the SQL Server Browser service restarted as well.
PowerShell with the SqlServer module
The click path doesn't script, so for anything repeatable use the SMO management objects:
Import-Module SqlServer
$wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer
$uri = "ManagedComputer[@Name='SQL01']/ServerInstance[@Name='MSSQLSERVER']/ServerProtocol[@Name='Tcp']"
$Tcp = $wmi.GetSmoObject($uri)
$Tcp.IsEnabled = $true
$Tcp.Alter()
Restart-Service -Name MSSQLSERVER -Force
Swap SQL01 for the machine name and MSSQLSERVER for the instance name if it isn't the default. Running $wmi on its own prints the object including the instance names it can see, which is the quickest way to get the string right on a server you didn't build.
That snippet lives in a file I paste from. The rest of the ManagedComputer object is a mystery to me and I have been happy to leave it that way.
Linux network settings in mssql-conf
There's no protocol switch to flip on Linux. TCP is how SQL Server talks there, it's on from the first start and the settings that exist live in /var/opt/mssql/mssql.conf under the network section. network.tcpport sets the TCP port. network.forceencryption takes 0 or 1 and makes TLS mandatory for every client. network.tlscert and network.tlskey take absolute paths to a certificate and its private key. Read the current state with:
sudo cat /var/opt/mssql/mssql.conf
If the file has no network section at all, that's fine and expected. Defaults don't get written out until you set something.
SQL Server firewall ports: open port 1433 on the host
ufw on Ubuntu
sudo ufw allow from 203.0.113.10 to any port 1433 proto tcp
sudo ufw status numbered
Microsoft's Ubuntu quickstart gives no firewall command at all, only prose saying you may need to open TCP 1433, so treat the syntax above as ufw's and not as Microsoft's. And yes, sudo ufw allow 1433/tcp is shorter. It also opens the port to the entire internet, and I've lost count of how many times I've found that rule on a server whose owner believed the database was private. Scope it to the source address you connect from.
firewalld on RHEL
sudo firewall-cmd --zone=public --add-port=1433/tcp --permanent
sudo firewall-cmd --reload
That pair is verbatim from Microsoft's Red Hat quickstart, and it is wide open by design. The scoped version goes through a rich rule:
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="1433" protocol="tcp" accept'
sudo firewall-cmd --reload
Windows Defender Firewall
Straight from Microsoft's guidance on configuring the Windows firewall for SQL Server access:
New-NetFirewallRule -DisplayName "SQLServer default instance" -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow
New-NetFirewallRule -DisplayName "SQLServer Browser service" -Direction Inbound -LocalPort 1434 -Protocol UDP -Action Allow
Add -RemoteAddress 203.0.113.10 to either line to restrict the source. The same page also offers a netsh one-liner, and this is where a lot of people get stuck:
netsh advfirewall firewall add rule name = SQLPort dir = in protocol = tcp action = allow localport = 1433 remoteip = localsubnet profile = DOMAIN
Read the tail of that command. It's scoped to localsubnet and to the DOMAIN profile. Paste it onto a standalone VPS that isn't domain joined, connecting from an address on a different subnet, and the rule is technically present and doing precisely nothing for you. That line gets copied constantly in SQL Server firewall troubleshooting, and it's why so many of the copies don't work.
The provider firewall in front of the server
Host firewall open and still timing out? There's usually a second one. Most providers put a network filter ahead of the instance, so a packet gets dropped before it ever reaches iptables. On LumaDock VPS plans the firewall rules and the private networking both live in the control panel, and what I'd push for is putting the database on the private interface and never giving it a public listener at all. Two rules to write, and the second one is why nobody scans your database.
Test the connection from the client
Test the transport before you test the credentials:
Test-NetConnection 203.0.113.5 -Port 1433
nc -vz 203.0.113.5 1433
Test-NetConnection prints ComputerName, RemoteAddress, RemotePort, InterfaceAlias, SourceAddress and a TcpTestSucceeded boolean, and that boolean is the field you read. nc -vz prints one line per port and exits 0 on success, so it drops into a shell script with no parsing.
Then a real login:
sqlcmd -S 203.0.113.5,1433 -U sa -C -Q "SELECT @@SERVERNAME"
Test-NetConnection is slower than it looks. It resolves the name and pings before it ever tests the port, so on a host that drops ICMP the ping stage sits there for its full timeout before the useful part runs. -InformationLevel Quiet trims the output and not the wait. Different tools, different decades.
Timeout, refused and login failed
Each one points somewhere different, and telling them apart saves you from changing the wrong thing:
- Hangs then times out (
TcpTestSucceeded : Falseafter a long pause). Packets are being dropped before they arrive, which means a firewall on the host or in front of it, and nothing on the server ever saw your connection. - Connection refused, immediately. You reached the machine and something actively said no. The service is down, or it's listening on a different port, or it's bound to loopback only.
- Login failed for user. Congratulations, the network works. You're now in authentication territory, which is error 18456 and its states.
A fourth one shows up with named instances. Error 26, error locating server or instance specified, means the client's UDP request to the Browser went unanswered, and connecting with an explicit port sidesteps it without touching the Browser at all.

