Quick prerequisites
- Administrative access to the remote Debian or Ubuntu host
- TCP port 22 open on the network or firewall. If you change the port, adjust commands accordingly
- A non-root account on the server, preferably in the sudo group
- On the client: OpenSSH is available by default on Linux and macOS. On Windows 10/11, the ssh tool is built in
1. Install and enable the SSH server (OpenSSH) on Debian / Ubuntu
Install OpenSSH on the server, enable it at boot, and start the service now.
sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable ssh --now
sudo systemctl status ssh --no-pager
If UFW is active, allow SSH. The OpenSSH profile usually exists by default.
sudo ufw allow OpenSSH
# alternative if the profile is missing
sudo ufw allow 22/tcp
sudo ufw reload
sudo ufw status
Quick server-side check to confirm it is listening on port 22.
ss -tnlp | grep :22
2. Connect via SSH from a client
Basic login using a non-root account.
ssh username@ip_or_domain
Specify a particular private key.
ssh -i ~/.ssh/id_ed25519 username@ip_or_domain
If the server listens on a non-standard port, for example 2222.
ssh -p 2222 username@ip_or_domain
Increase verbosity to diagnose connection issues.
ssh -vvv username@ip_or_domain
3. Generate an SSH key pair on the client
Generate a modern ed25519 key protected by a passphrase.
ssh-keygen -t ed25519 -a 100 -C "client-workstation"
Your public key is typically stored here.
~/.ssh/id_ed25519.pub
Copy your public key to the server automatically with ssh-copy-id.
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@ip_or_domain
Manual alternative if ssh-copy-id is unavailable.
cat ~/.ssh/id_ed25519.pub | ssh username@ip_or_domain 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
Start the SSH agent on the client and load your key so you do not have to retype the passphrase each time.
# Linux / macOS
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
On Windows PowerShell.
Start-Service ssh-agent
Get-Service ssh-agent | Set-Service -StartupType Automatic
ssh-add $env:USERPROFILE\.ssh\id_ed25519
4. Harden the SSH server configuration
Create a dedicated drop-in instead of editing the main file. On recent Debian and Ubuntu releases, sshd_config.d/ is supported.
sudo nano /etc/ssh/sshd_config.d/10-hardening.conf
Recommended baseline settings.
# Listen on the default port unless policy requires otherwise
Port 22
# Do not allow direct root login
PermitRootLogin no
# Prefer key-based auth and disable password auth once keys work
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
# Reduce attack surface
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding yes
# Session safety
ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
LoginGraceTime 30
UseDNS no
# Optionally restrict to specific accounts
# AllowUsers adminops deployer
Test the syntax and reload without dropping existing connections.
sudo sshd -t
sudo systemctl reload ssh
Important: disable PasswordAuthentication only after confirming that public key login works. Keep an SSH session open while applying changes to avoid locking yourself out.
5. Configure the SSH client with ~/.ssh/config
Create a handy connection alias on the client.
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/config
Example entry.
Host prod-web
HostName ip_or_domain
User username
Port 22
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Usage.
ssh prod-web
Connect via a bastion using ProxyJump.
Host bastion
HostName bastion.example.com
User ops
IdentityFile ~/.ssh/id_ed25519
Host prod-web
HostName internal.web.local
User deploy
ProxyJump bastion
6. Transfer files over SSH
Copy with scp.
# to the server
scp file.txt username@ip_or_domain:/home/username/
# from the server
scp username@ip_or_domain:/var/log/syslog ./
Efficient, incremental transfer with rsync over SSH.
rsync -avz -e "ssh -p 22" ./site/ username@ip_or_domain:/var/www/site/
Interactive SFTP session.
sftp username@ip_or_domain
7. SSH tunnels (port forwarding)
Local tunnel. Access a remote service on port 80 via http://localhost:8080.
ssh -L 8080:localhost:80 username@ip_or_domain
Remote tunnel. Expose a local client service to the server on port 9000.
ssh -R 9000:localhost:3000 username@ip_or_domain
Dynamic SOCKS proxy.
ssh -D 1080 -N username@ip_or_domain
8. Extra hardening: UFW, Fail2ban, and a non-standard port
Allow an alternate port if you change 22 to 2222, for example. Changing the port does not replace proper key-based authentication.
sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp
sudo ufw reload
Install Fail2ban and enable the sshd jail to rate-limit brute-force attempts.
sudo apt install -y fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Minimal excerpt to adapt.
[sshd]
enabled = true
bantime = 1h
findtime = 10m
maxretry = 5
Restart Fail2ban.
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
9. Verify the server's host key fingerprint
On first connect, SSH shows the server's host key fingerprint. Compare it with a value measured on the server.
sudo ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
Optionally pre-populate known_hosts on the client.
ssh-keyscan -t ed25519 ip_or_domain >> ~/.ssh/known_hosts
10. Troubleshooting: common problems and fixes
Connection refused. Check service and firewall.
sudo systemctl status ssh
sudo ufw status
ss -tnlp | grep :22
Authentication fails.
# authentication log on Debian/Ubuntu
sudo tail -f /var/log/auth.log
# client-side diagnostics
ssh -vvv username@ip_or_domain
Permissions too open on .ssh or authorized_keys.
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Private key not being used. Force the identity.
ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes username@ip_or_domain
Host behind a cloud firewall. Open the port in the provider console in addition to local UFW.
Port conflict or an existing service already using the port. Change the port in the drop-in and UFW, then reload.
sudo nano /etc/ssh/sshd_config.d/10-hardening.conf
sudo sshd -t && sudo systemctl reload ssh
11. Best-practice recap
- Use ed25519 keys with a strong passphrase and strict permissions in ~/.ssh
- Disable password authentication once keys are confirmed to work
- Forbid direct root login and use sudo with a dedicated user
- Optionally restrict with AllowUsers or AllowGroups
- Monitor auth.log and enable Fail2ban
- Back up private keys and consider a rotation policy
- Document any port change and update associated network rules
Appendices: handy cheat sheets
List server host keys.
sudo ls -l /etc/ssh/ssh_host_*_key.pub
Print your local key's fingerprint.
ssh-keygen -l -f ~/.ssh/id_ed25519.pub
Run a remote command in one go.
ssh username@ip_or_domain 'sudo systemctl status --no-pager ssh'
Connection multiplexing to speed up repeated SSH sessions.
Host *
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 5m
End of guide. You can now connect to your Debian/Ubuntu servers over SSH reliably and securely.


















