Connecting to a Remote Host

Chapter 2 — Connecting to a Remote Host

Chapter 1 explained the theory — encryption, key pairs, host verification. This chapter is where you actually type things. The ssh command is surprisingly flexible: it can open an interactive shell, run a single command and exit, forward a port, or proxy through an intermediate host. This chapter covers the core syntax, the most useful flags, running remote commands without opening a full shell, and diagnosing the most common connection errors.

The Basic Command

The minimum you need is a username and a hostname or IP address:

ssh  -p 2222  philip@192.168.1.100
ssh The SSH client command
-p 2222 Optional — connect on port 2222 instead of the default port 22
philip The username to log in as on the remote machine
@ Separator between user and host
192.168.1.100 The hostname or IP address of the remote machine
Username shortcut: if your local username matches your remote username, you can omit the user@ part entirely and just type ssh hostname. SSH uses your current username by default.

Your First Connection — Step by Step

philip@debian — connecting to a LAN server
philip@debian:~$ ssh philip@192.168.1.100 The authenticity of host '192.168.1.100 (192.168.1.100)' can't be established. ED25519 key fingerprint is SHA256:abc123XYZdef456GHI... Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.1.100' to the list of known hosts. philip@192.168.1.100's password: (type password — nothing appears on screen) Welcome to Debian GNU/Linux 12 (bookworm) Last login: Tue Jun 17 20:14:32 2026 from 192.168.1.50 philip@server:~$ ← you are now on the remote machine

Notice the prompt changed — philip@debian became philip@server. Every command you type now runs on the remote machine. To return to your local machine, type exit or press Ctrl+D.

philip@server — ending the session
philip@server:~$ exit logout Connection to 192.168.1.100 closed. philip@debian:~$ ← back on your local machine

Connecting by Hostname vs IP Address

You can use either an IP address or a hostname. On a local network, your router usually provides hostname resolution so you can use the machine's name directly:

philip@debian — hostname vs IP
# These may connect to the same machine: philip@debian:~$ ssh philip@192.168.1.100 ← IP address philip@debian:~$ ssh philip@raspberrypi ← hostname (if DNS resolves it) philip@debian:~$ ssh philip@raspberrypi.local ← mDNS hostname (Bonjour/Avahi) # For public servers, use the domain name: philip@debian:~$ ssh philip@osztromok.com
.local hostnames (like raspberrypi.local) use mDNS (multicast DNS), which works on local networks without a DNS server. On Linux this requires avahi-daemon to be running. On Windows, Bonjour (installed with iTunes or Apple devices) provides the same feature.

Essential SSH Flags

FlagWhat it doesExample
-p PORT Connect on a non-default port (default is 22) ssh -p 2222 philip@server
-i FILE Use a specific private key file instead of the default ssh -i ~/.ssh/id_work philip@server
-l USER Specify the login username (alternative to user@host) ssh -l philip server
-v Verbose — show the connection and auth process. Use -vv or -vvv for more detail. Invaluable for debugging. ssh -v philip@server
-A Forward your SSH agent — lets you use your local keys on the remote machine to connect onwards to a third machine ssh -A philip@jumphost
-X Enable X11 forwarding — run graphical apps on the remote machine, display locally ssh -X philip@server
-N Do not execute a remote command — used with port forwarding (Chapter 5) when you only want the tunnel ssh -N -L 8080:localhost:80 philip@server
-f Go to background after connecting — used with -N for background tunnels ssh -fN -L 8080:localhost:80 philip@server
-q Quiet mode — suppress warnings and diagnostic messages. Useful in scripts. ssh -q philip@server uptime
-o OPTION=VALUE Set any config option inline without editing the config file ssh -o StrictHostKeyChecking=no philip@server

Running a Single Remote Command

You don't have to open an interactive shell every time. Append a command after the host and SSH will run it, print the output, and exit — no prompt, no interaction required. This is very useful in scripts.

philip@debian — single remote commands
# Check disk space on the server without opening a shell philip@debian:~$ ssh philip@server df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 12G 36G 25% / /dev/sdb1 2.0T 800G 1.1T 43% /data # Check system uptime philip@debian:~$ ssh philip@server uptime 20:14:32 up 14 days, 3:22, 1 user, load average: 0.01, 0.03, 0.00 # Restart a service on the remote server philip@debian:~$ ssh philip@server sudo systemctl restart apache2 (password prompt appears if using password auth, or silently succeeds with key auth) # Run a multi-word command — quote it or use -- to be safe philip@debian:~$ ssh philip@server "df -h | grep sda" /dev/sda1 50G 12G 36G 25% / # Run a script that exists on the remote machine philip@debian:~$ ssh philip@server "bash /home/philip/backup.sh"
Quote your commands when they contain pipes, redirections, or spaces — otherwise your local shell interprets them before SSH sees them. ssh server "df -h | grep sda" sends the whole string to the remote shell. Without quotes, the pipe runs locally and SSH never sees it.

Running a Local Script on a Remote Machine

You can pipe a local script into SSH and have the remote machine execute it — without copying the file first:

philip@debian — pipe a local script to a remote shell
philip@debian:~$ ssh philip@server 'bash -s' < local_script.sh

Connecting on a Non-Standard Port

Many administrators move SSH away from port 22 to reduce automated scanning noise. If the server listens on a different port, use -p:

philip@debian — non-standard port
philip@debian:~$ ssh -p 2222 philip@server # Or connect to a VPS that uses a custom port philip@debian:~$ ssh -p 4422 philip@myserver.example.com
Security note: moving SSH off port 22 reduces automated brute-force scanning (bots constantly probe port 22) but is not a security measure on its own — a port scan reveals the new port within seconds. Real security comes from key-based authentication and fail2ban, covered in Chapters 3 and the Web Server Security course. That said, fewer log noise entries can be genuinely useful.

Connecting from Windows

Windows 10 and 11 include OpenSSH as an optional feature — the same ssh command works in PowerShell and Command Prompt. If it's not installed, enable it from Settings → Optional Features.

PowerShell on Windows — SSH works the same way
PS C:\Users\Philip> ssh philip@192.168.1.100 philip@192.168.1.100's password: Welcome to Debian GNU/Linux 12 (bookworm) philip@server:~$

GUI SSH clients are also available if you prefer a point-and-click interface:

  • PuTTY — the classic Windows SSH client. Free, lightweight, saves session profiles. Still widely used in enterprise environments.
  • Kitty — a PuTTY fork with extra features (tabs, automatic reconnect, URL highlighting). Philip uses this.
  • MobaXterm — SSH client + X11 server + file browser in one. Popular for Linux admin from Windows.
  • Windows Terminal — Microsoft's modern terminal. Use the built-in ssh command inside it for a clean experience.

Debugging with Verbose Mode

When a connection fails silently or behaves unexpectedly, -v reveals exactly what SSH is doing at each step. Add more vs for progressively more detail:

philip@debian — verbose connection (truncated)
philip@debian:~$ ssh -v philip@server OpenSSH_9.2p1 Debian-2+deb12u5, OpenSSL 3.0.15 debug1: Reading configuration data /home/philip/.ssh/config debug1: Connecting to server [192.168.1.100] port 22. debug1: Connection established. debug1: Server host key: ssh-ed25519 SHA256:abc123... debug1: Host '192.168.1.100' is known and matches the ED25519 host key. debug1: Offering public key: /home/philip/.ssh/id_ed25519 ED25519 SHA256:xyz789 debug1: Server accepts key: /home/philip/.ssh/id_ed25519 debug1: Authentication succeeded (publickey). philip@server:~$

The key lines to look for: which key file SSH is trying, whether the server accepts it, and which authentication method ultimately succeeded.

Troubleshooting Common Errors

ssh: connect to host server port 22: Connection refused
Cause: The SSH service isn't running on the server, the server is listening on a different port, or a firewall is blocking port 22.
Fix: Check the SSH service: sudo systemctl status sshd. Check the port in /etc/ssh/sshd_config. Check UFW: sudo ufw status. If using a custom port, add -p PORT to your ssh command.
ssh: connect to host server port 22: No route to host
Cause: The hostname or IP address doesn't resolve, the server is off, or there's no network path to it.
Fix: Verify the IP: ping 192.168.1.100. Confirm the server is on. Confirm you're on the right network. Check DNS: nslookup hostname.
Permission denied (publickey,password)
Cause: Wrong username, wrong password, or (with key auth) the key is not in the server's authorized_keys.
Fix: Confirm the username matches an account on the server. Try ssh -v to see which keys are being offered. If using keys, verify the public key is in ~/.ssh/authorized_keys on the server with permissions 600.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
Cause: The server's host key doesn't match the one stored in your known_hosts. Could be a reinstalled OS, a reused IP, or — take seriously — a man-in-the-middle attack.
Fix: Verify out-of-band that the server's key legitimately changed. If confirmed, remove the old entry: ssh-keygen -R hostname. Reconnect and accept the new fingerprint.
ssh_exchange_identification: read: Connection reset by peer
Cause: The SSH daemon accepted the TCP connection but then dropped it. Often caused by hosts.deny, AllowUsers / DenyUsers in sshd_config, or fail2ban banning your IP.
Fix: Check /etc/hosts.deny and /etc/hosts.allow. Check sshd_config for AllowUsers. Check fail2ban: sudo fail2ban-client status sshd — your IP may be banned.
Timeout / connection hangs and never connects
Cause: A firewall is silently dropping packets (no rejection, just silence), or the server is unreachable.
Fix: Try ssh -v — if it hangs on "Connecting to..." the problem is network/firewall before the SSH daemon. Check UFW rules on the server. Check that port 22 is open: nc -zv server 22 from your machine.

Keeping a Connection Alive

SSH connections that sit idle too long get dropped — by NAT routers, firewalls, or the server's own timeout settings. Fix this by sending keepalive packets from the client side. Either add this to your ~/.ssh/config (Chapter 4 covers this in full) or pass it inline:

philip@debian — keepalive inline
# Send a keepalive packet every 60 seconds; give up after 3 missed replies philip@debian:~$ ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 philip@server

Quick Reference

TaskCommand
Open a shell sessionssh philip@server
Connect on a custom portssh -p 2222 philip@server
Use a specific key filessh -i ~/.ssh/id_work philip@server
Run one command and exitssh philip@server df -h
Run a piped commandssh philip@server "df -h | grep sda"
Debug a failed connectionssh -v philip@server
Even more debug detailssh -vvv philip@server
Remove stale host keyssh-keygen -R hostname
Test TCP connectivity to port 22nc -zv server 22
End the sessionexit  or  Ctrl+D
Escape sequence (if stuck)Enter, then ~.
Escape sequence ~. — if your SSH session freezes (network dropped, server hung) and Ctrl+C does nothing, press Enter then type ~. (tilde followed by a full stop). This tells the SSH client to drop the connection immediately, returning you to your local prompt.
Next — Chapter 3: Key-Based Authentication. Typing a password every time is tedious and less secure than using a key pair. Chapter 3 covers generating an Ed25519 key pair, copying your public key to a server with ssh-copy-id, the authorized_keys file, setting the correct permissions, and disabling password authentication once keys are working.