Logo

SSH on Linux (Debian / Ubuntu)

SSH on Linux (Debian / Ubuntu)

This tutorial explains how to connect over SSH to a Debian or Ubuntu machine, and how to install, configure, and secure the SSH service on the server. It also covers key generation, ssh-agent usage, UFW firewall rules, file transfers, SSH tunnels, and troubleshooting common issues.

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.

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members