Connecting to a VPS with SSH is the first thing you do after your server is delivered, and from then on it is almost the only door you'll walk through to manage it. The server has been created, and its IP address and root password are right there in the panel — now what? This guide takes you step by step through your first login from Windows, Linux, macOS and mobile, replaces password login with keys, shows you file transfer, tunnels and tmux, and finishes with a troubleshooting table for common SSH errors.
What is SSH, and why can't you manage a server without it?
SSH (Secure Shell) is an encrypted channel between your computer and the server; every command and every line of output travels inside this tunnel, and unlike older tools such as Telnet, nothing — neither your password nor your data — crosses the network in plain text.
A small but useful distinction: SSH is the name of the protocol, and OpenSSH is the name of its most widely used implementation, which runs on almost every Linux distribution, on macOS and on modern Windows. Its default port is 22.
What exactly happens on the first connection?
Every SSH connection goes through three stages. First, the two sides exchange their protocol versions. Second, the key exchange takes place: the client and the server agree on a shared encryption key, and in the same stage the server signs the result with its own "host key"; the client compares that host key with the copy stored in ~/.ssh/known_hosts, and because nothing is stored there the first time, you see the "authenticity of host" message. Third, it's your turn to authenticate — with a password or with a key. Common SSH errors map onto these three stages, and working out which stage an error occurred in is half of the troubleshooting.
What do you need before your first connection?
Exactly four things: the server's public IP address, the SSH port (22 by default), a username (usually root on a fresh server) and a way to authenticate, which is either a password or a key. In the Mehran Host customer panel, the IP address and the initial root password are shown on that server's page.
Before you blame SSH, check whether the SSH port is reachable from your side. On Linux and macOS:
nc -zv 203.0.113.10 22
Connection to 203.0.113.10 22 port [tcp/ssh] succeeded!
And on Windows, without installing anything, in PowerShell:
Test-NetConnection 203.0.113.10 -Port 22
ComputerName : 203.0.113.10
RemoteAddress : 203.0.113.10
RemotePort : 22
InterfaceAlias : Ethernet
SourceAddress : 192.168.1.20
TcpTestSucceeded : True
If this test succeeds, the network path is fine, and any error after that is about authentication, not the firewall.
How to connect to a VPS with SSH from Windows
Windows 11 and Windows 10 (build 1809 and later) include the OpenSSH client as an "optional feature", and on most installations it is already enabled, so you don't need PuTTY. Open PowerShell or Windows Terminal and type:
ssh root@203.0.113.10
Replace the example address with your own server's IP. The output of the first connection looks like this:
The authenticity of host '203.0.113.10 (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:2xJ8h1Qm0kZ9Rr7vC4pLd6TyWnAe5BsUgXf3NqMoIkE.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '203.0.113.10' (ED25519) to the list of known hosts.
root@203.0.113.10's password:
This message is normal: the host key fingerprint is saved for next time. Type yes and enter the password. Nothing is displayed while you type the password, not even asterisks; that's intentional.
If the ssh command isn't recognized, check whether the feature is present, and if it isn't, add OpenSSH Client under Settings › Optional Features:
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
Name : OpenSSH.Client~~~~0.0.1.0
State : NotPresent
PuTTY is still a good choice if you prefer to keep your saved connections in a graphical interface. Just be aware that PuTTY stores its keys in its own .ppk format, and to use the same key with OpenSSH you have to convert it with PuTTYgen.
Connecting from Linux and macOS
Open a terminal; the command is exactly the same:
ssh root@203.0.113.10
If you've changed the server's SSH port (to 2222, for example), specify it with the -p switch:
ssh -p 2222 root@203.0.113.10
If you've created a regular user on the server, use that user's name instead of root: ssh ali@203.0.113.10. Remember one more switch as well: -v. Adding it to any SSH command prints the connection stages line by line:
ssh -v root@203.0.113.10
debug1: Connecting to 203.0.113.10 [203.0.113.10] port 22.
debug1: Connection established.
debug1: Authentications that can continue: publickey,password
debug1: Next authentication method: publickey
debug1: Offering public key: /home/ali/.ssh/id_ed25519 ED25519 SHA256:8Kd2...q4 explicit
These few lines tell you that the TCP connection was established (so the firewall isn't the problem), which key was offered, and which methods the server accepts.
SSH from Android or iPhone
When you're away from your computer and the site goes down, your phone is a lifesaver. On Android, Termux (install it from F-Droid or GitHub — its Google Play build has been discontinued and is incomplete; pkg install openssh gives you the same standard OpenSSH client), ConnectBot or Termius, and on iPhone, Termius or Blink Shell, are popular choices.
If you put a private key on your phone, always protect it with a passphrase and create a separate key for the phone. That way, if the phone is lost, you remove just that one key from authorized_keys and every other device stays untouched.
sshd_config or lose the password, you can still reach the server from your browser. On international servers, whether there is a console depends on the provider. Try it once before you make any change to your SSH settings.The first minute after login: get to know your server
The root@srv1:~# prompt is in front of you. Before you install anything, run three commands:
root@srv1:~# uptime
09:41:02 up 7 min, 1 user, load average: 0.02, 0.06, 0.02
root@srv1:~# free -h
total used free shared buff/cache available
Mem: 3.8Gi 243Mi 3.3Gi 1.0Mi 311Mi 3.4Gi
Swap: 0B 0B 0B
root@srv1:~# df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 39G 2.1G 35G 6% /
These three outputs show, in order, the server's load, its free RAM and the disk space left. On a fresh server the load should be close to zero and the disk almost empty; if you see something else, something is already running. The next step is updating the packages:
apt update && apt upgrade -y
On the RHEL family (such as AlmaLinux and Rocky), the equivalent is dnf upgrade -y. Do this on day one: operating system images are usually a few weeks behind the latest security patches, and a server that has just been given a public IP address is scanned from the very first minute. If you're not comfortable with basic Linux commands, keep our list of essential Linux commands open next to you.
SSH keys: saying goodbye to passwords
Key login is both more secure and more convenient. A key pair is created: the private half never leaves your device, and the public half sits on the server, so nothing that could be intercepted or guessed travels over the network.
Generate an SSH key with ed25519
ssh-keygen -t ed25519 -C "ali@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ali/.ssh/id_ed25519):
Enter passphrase for "/home/ali/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ali/.ssh/id_ed25519
Your public key has been saved in /home/ali/.ssh/id_ed25519.pub
Choose the ed25519 type; it is shorter and faster than RSA. The -C switch adds a text label, so that later you can tell which device each key in the server's authorized_keys belongs to. Don't leave the passphrase empty except on keys used for automation; a private key without one means that anyone who gets at your files even once has reached your server too.
Copy the public key to the server
ssh-copy-id root@203.0.113.10
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ali/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@203.0.113.10's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@203.0.113.10'"
and check to make sure that only the key(s) you wanted were added.
On Windows, which has no ssh-copy-id, run this alternative in PowerShell:
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh root@203.0.113.10 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
From now on, ssh root@203.0.113.10 logs you in without typing the server password.
File permissions: the most common reason a key doesn't work
If you've sent the key and you're still asked for a password, file permissions are usually to blame: sshd ignores the key if the user's home directory, the ~/.ssh directory or authorized_keys itself is writable by the group or by others — being readable is fine, since the file only holds public keys. Nothing is printed on your side, you're simply asked for the password again, but the server writes the reason to its log: with journalctl -u sshd (or tail /var/log/auth.log) you'll see a line like Authentication refused: bad ownership or modes for directory /home/ali/.ssh. Run this on the server:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh
On your own device, the private key must be 600 as well, or the client refuses to use it. On Windows, chmod does nothing, so you have to fix the file's ownership and ACL instead:
icacls "$env:USERPROFILE\.ssh\id_ed25519" /inheritance:r /grant:r "$env:USERNAME:R"
.pub extension) never goes anywhere: not into a support ticket, not into Telegram, not into a repository. And before you disable password login, always test key login in a second window; disabling it while the key still doesn't work is the classic way to lock yourself out of your own server.Once you're sure the key works, our Linux server hardening checklist shows you how to disable password login and direct root login.
The SSH config file: shorter commands, steadier connections
Typing the IP address, port and username for every connection is tedious and error-prone. The ~/.ssh/config file (on Windows, inside the user's .ssh folder) solves this for good:
Host srv1
HostName 203.0.113.10
User root
Port 22
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
ServerAliveCountMax 5
From now on you just type ssh srv1, and the same alias works in scp, rsync and sftp too. But the two most important lines are the last two. The default value of ServerAliveInterval is zero, which means the client sends no keepalive packets at all; so if you don't type anything for a few minutes, your router or a NAT device along the path closes the idle connection and you get Broken pipe. ServerAliveCountMax says how many unanswered packets to tolerate; its default is 3, and with 5 the connection rides out about 150 seconds of temporary outage without being closed.
Another feature of this file is jumping through an intermediate server. If the destination is reachable only from inside another server's network, instead of running SSH twice, write:
Host db1
HostName 10.0.0.12
User root
ProxyJump srv1
Now ssh db1 first connects to srv1 and goes on from there to the destination, without your private key ever being stored on the intermediate server.
Copy files with scp, rsync and sftp
The same connection doubles as a file transfer channel, with no separate software needed. The simplest option is scp:
scp backup.zip srv1:/root/
scp -r ./site srv1:/var/www/
scp srv1:/var/log/nginx/error.log ./
Transfers work in either direction: whichever side is the destination is written second. Since OpenSSH 9.0, scp no longer uses its own legacy protocol behind the scenes; it runs over SFTP instead.
For anything bigger than a single small file, rsync is the better choice, because it sends only the differences, and with the -P switch it keeps a partly transferred file at the destination:
rsync -avzP ./site/ srv1:/var/www/site/
Watch the trailing slash on the source path: with the slash, the contents of the folder are copied; without it, the folder itself is created inside the destination. That one-character difference is behind countless unwanted nested folders. If a transfer is interrupted, run the same command again; because -P (short for --partial --progress) kept the incomplete file at the destination, rsync carries on from where it stopped. Without -P, rsync deletes the partial file and sends it again from scratch. And if you'd rather browse the files the way you would in a file manager, sftp srv1 opens an interactive session with ls, cd, get and put.
tmux and SSH tunnels: two things only SSH can do
tmux: when the internet drops, your work doesn't
Any command you run in an ordinary SSH session is killed when the connection drops. If your internet cuts out in the middle of an apt upgrade, the update is left half-finished, which is exactly the kind of thing that breaks a server. The fix is one line (if tmux isn't installed, run apt install tmux first, or dnf install tmux on the RHEL family):
tmux new -s work
# ... do your work; the connection drops ...
tmux attach -t work
tmux creates a persistent session on the server itself; your connection is only a window looking at it, so losing the connection closes the window, not the work.
SSH tunnel port forwarding: open a server's local service in your own browser
Many services on a server listen only on 127.0.0.1 and are deliberately unreachable from outside: databases, admin panels, dashboards. Instead of exposing them to the internet, look at them through the SSH tunnel itself:
ssh -L 8080:127.0.0.1:8080 srv1
Now http://localhost:8080 in your browser is really port 8080 on the server, encrypted and without a single open port on the firewall. Databases work the same way, but the number on the left is a port on your device: with ssh -L 3306:127.0.0.1:3306 srv1 and a local MySQL running, you get bind: Address already in use and, without realizing it, connect to your own local database. So pick a free port: ssh -L 3307:127.0.0.1:3306 srv1, and connect to localhost:3307. The -D switch creates a local SOCKS proxy; just remember that this traffic comes out of the server's bandwidth, and for permanent tunnels dedicated tools are a better fit — we compare the differences in our tunnel server guide.
Common SSH connection errors and how to fix them
The table below links each error message to its likely cause and the most useful first step:
| Error message | Likely cause | First step |
|---|---|---|
| Connection timed out | A firewall is blocking the port, the server is off, or the IP address is wrong | Check that the server is on, and test the port with nc -zv |
| Connection refused | The network path is open, but sshd isn't running or is listening on a different port | Check the ssh service status from the server's graphical console |
| Permission denied (publickey) | The right key wasn't offered, or the file permissions are wrong | Fix the permissions and use -v to see which key is being sent |
| Permission denied (password) | The password is wrong, or root login with a password has been disabled | Reset the password from the panel, or log in as a regular user |
| REMOTE HOST IDENTIFICATION HAS CHANGED | The host key has changed, usually after an operating system reinstall | ssh-keygen -R 203.0.113.10, then connect again |
| Too many authentication failures | The agent offers too many keys and the server disconnects before reaching the right one | Send only the specified key with -o IdentitiesOnly=yes |
| Broken pipe | A NAT device or the network path closed the idle connection | Add ServerAliveInterval 30 to your config file |
| kex_exchange_identification: Connection closed | Your IP address has been blocked temporarily, or the server is under heavy load | Wait a few minutes; if Fail2ban is installed, unban your IP |
Connection timed out
If ping gets a reply but the port isn't open, it's a firewall problem; if ping doesn't get a reply either, the server is off or you copied the wrong IP address. Keep in mind that the firewall may have two layers: one inside the operating system (such as UFW) and one at the network level.
Permission denied (publickey,password)
The text in the parentheses tells you exactly which methods the server accepts; if password isn't among them, a key is the only way in, and you have to add your public key to authorized_keys by hand from the server's graphical console.
We cover all 9 causes of this error, how to read ssh -v output line by line, and the way back in through the console in a separate guide to fixing Permission denied (publickey).
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED
This means the fingerprint of the server's host key is different from last time. If you rebuilt (reinstalled) the server yourself, or the IP address has been reassigned to another server, that's expected; remove the old entry and connect again:
ssh-keygen -R 203.0.113.10
# Host 203.0.113.10 found: line 14
/home/ali/.ssh/known_hosts updated.
Original contents retained as /home/ali/.ssh/known_hosts.old
But if you haven't changed anything on the server, take this message seriously: SSH is warning you that something may be sitting in the middle of the path. Before you remove the entry, get the server's real fingerprint from its graphical console with ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub and compare the two.
Stable SSH connections from Iran
One variable you won't find in most SSH guides is an unstable network path. If you're in Iran and your server is inside the country too — as Mehran Host cloud servers are, running in a datacenter in Iran — the path is short and SSH usually works without trouble. A server abroad is a different story: every connection you make has to cross Iran's international gateway, the limited set of links that carry all traffic between Iran and the rest of the internet, which means high latency, packet loss and short dropouts.
Three measures make this bearable: the same ServerAliveInterval setting, which absorbs drops of a few seconds; running every long task inside tmux; and, if you also have a server inside Iran, reaching the server abroad through it with ProxyJump — sometimes the path from a datacenter to the destination is better than the one from your home.
There's also a tool called mosh, which takes over after a normal SSH login and carries the session over UDP, so it survives IP address changes and dropouts — provided mosh is installed on both ends and the UDP port range 60000 to 61000 is open in the server's firewall; otherwise, after the mosh-server is running message, the session hangs forever. But to be honest, UDP quality on some international paths is worse than TCP, and mosh may do worse than plain SSH. For the root causes of this latency, see our guide to reducing ping, and if you can't decide between a server inside Iran and one abroad, our guide to buying a VPS outside Iran puts the criteria side by side.
What's next?
Now that you're in, it's time to lock things down. Security comes first: disable password login, create a non-root user, and set up a firewall and Fail2ban. After that, the path depends on your goal: to get a website up, set up a website with Nginx on Ubuntu; to run several services side by side, run Docker on a cloud server. Don't have a server yet? Create one in a minute.
Frequently asked questions
How do I connect to a VPS with SSH?
Open PowerShell on Windows 10 or 11, or a terminal on Linux and macOS, and run ssh root@IP-SERVER, replacing IP-SERVER with your server's IP address. The first time, you will see a message about the host's fingerprint, which you confirm by typing yes; then you enter the user's password, which is not displayed as you type. If the SSH port has been changed, add it with the -p switch.
Why does SSH say Connection timed out?
It means your request never reached the SSH service at all, and there are three common causes: the server is powered off, a firewall is blocking the SSH port, or you entered the wrong IP address. To tell them apart, test whether the port is open with nc -zv IP 22, or with Test-NetConnection on Windows. If the port is open but you still cannot connect, sshd is probably not running, and you should check it through out-of-band access: the server's graphical console.
What is the difference between password login and SSH key login?
Password login rests on a string that can be guessed, while key login rests on a key pair whose private half never leaves your device. Bots try passwords against public IP addresses around the clock; with a key, the server verifies your identity using only the public half. The result is both more secure and more convenient, because you no longer type the server password. Always protect the key itself with a passphrase; ssh-agent asks for it only once per session.
Can I SSH into a server from my phone?
Yes, and it is very useful in an emergency. On Android, Termux and ConnectBot are common choices, and on iPhone, Termius; enter the IP address, username and password and you have the same familiar terminal on your phone. If you keep a private key on the phone, always give it a passphrase, and preferably create a separate key just for the phone, so that if the device is lost you revoke only that one key.
What should I do if SSH won't connect at all and I'm locked out of the server?
Use the web console in the customer panel. It connects directly to the server's virtual screen and does not depend on the network SSH uses, so it works even when the port is closed, sshd is broken or you mis-edited the configuration file. It is always available on cloud servers inside Iran; on international servers it depends on the provider. Log in there, fix the firewall rule or sshd_config, and restart the service to get SSH access back.