SSH explained for home server admins

· in fundamentals

Tested on Debian 13.7 (Docker) with OpenSSH 10.0p2 and fail2ban 1.1.0 on 5 November 2026.

SSH is how you run commands on your home server from another computer. This guide explains what happens when you connect, then sets up key-based login, a short alias for your server, password login switched off, protection against password-guessing bots, and SSH tunnels to reach services you don't want on the internet.

How SSH works

SSH (Secure Shell) opens an encrypted connection between a client, the computer you type on, and a server, the machine you want to control. Everything that passes through it, including your password if you use one, is encrypted. Almost every Linux server and every current desktop operating system ships OpenSSH, the most widely used implementation.

Two kinds of key pair are involved, and keeping them apart makes everything else easier to follow:

  • Host keys belong to the server. They prove to your client that it is talking to your server and not to something pretending to be it. The server creates them when OpenSSH is installed.
  • User keys belong to you. They prove to the server that you are allowed in, in place of a password. You create them on the computer you connect from.

Each key pair has a private half and a public half. The private half never leaves the machine it was made on. The public half can be copied anywhere, because it can only check signatures, not make them.

Install the SSH server

On Debian and Debian-based systems, install the server package on the home server. Any of the machines in choosing home server hardware will do.

sudo apt install openssh-server

Debian starts the service after installing it. On Debian 13, this installs OpenSSH 10.0, which listens on port 22. Your laptop or desktop already has the client: the ssh command is built into Linux, macOS and Windows 10 and later.

Find the server's address on your home network with ip -brief address on the server. It will look like 192.168.1.20. If the address changes after a reboot, give the server a fixed address in your router, as the port forwarding guide describes.

First login and the host key fingerprint

Connect from your computer with your username on the server and its address:

ssh alice@192.168.1.20

The first time, the client cannot know whether the host key it receives is genuine, so it shows a fingerprint and asks whether to continue. To check it, run this on the server itself, at its own keyboard or through a connection you already trust:

ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

If the SHA256: value matches what the client showed, type yes. The client saves the key in ~/.ssh/known_hosts and checks it on every later connection. If it ever changes without a reason you know of (you reinstalled the server, for example), the client refuses to connect and shows a warning. Don't delete the old entry until you know why the key changed.

Create a key pair

On the computer you connect from, create a user key:

ssh-keygen -t ed25519 -C "laptop"

Ed25519 is the default key type in current OpenSSH, so plain ssh-keygen gives the same result. The -C comment is a label that shows up in the server's key list, which helps when you later need to remove the key for a lost laptop.

The command asks where to save the key (accept the default, ~/.ssh/id_ed25519) and for a passphrase. Set one. Without it, anyone who copies the private key file can use it. With it, the file is useless to them without the passphrase. An SSH agent remembers the unlocked key for your session, so you type the passphrase once, not on every connection. The agent runs by default on most desktops.

You now have two files: ~/.ssh/id_ed25519 (private, keep it there) and ~/.ssh/id_ed25519.pub (public, safe to copy).

Copy the public key to the server

The server lets in any key listed in the user's ~/.ssh/authorized_keys file, one public key per line. The easiest way to add yours is ssh-copy-id, which logs in with your password one last time and appends the key:

ssh-copy-id -i ~/.ssh/id_ed25519.pub alice@192.168.1.20

It also creates the ~/.ssh directory with mode 700 and the authorized_keys file with mode 600 if they don't exist yet. Those permissions matter: OpenSSH ignores the file if other users could change it.

On Windows, which has no ssh-copy-id, paste the contents of the .pub file as a new line in ~/.ssh/authorized_keys on the server, then fix the permissions there:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Log out and connect again. If you are asked for the key's passphrase (or nothing at all, when the agent has it) instead of the account password, the key works.

Give the server a short name in ~/.ssh/config

Typing the user and address every time is tedious. The client reads ~/.ssh/config on your computer, where you can give each server a short name and its settings:

Host homeserver
    HostName 192.168.1.20
    User alice
    IdentityFile ~/.ssh/id_ed25519

Now ssh homeserver connects with those settings, and so do scp, sftp and rsync when you use the same name. Add a Port line if the server listens on a different port. The ssh_config manual lists every option.

Turn off password login

Once key login works, turn off password login. A server that accepts passwords can be broken into by anyone who guesses one. A server that only accepts keys cannot be, in practice.

Keep your current SSH session open while you make this change, and test from a second terminal. If something is wrong, you can still fix it from the first session instead of walking to the server with a keyboard.

On Debian, the main file /etc/ssh/sshd_config starts with Include /etc/ssh/sshd_config.d/*.conf. For each setting, sshd uses the first value it reads, so a file in that directory takes priority over the main file and survives package upgrades. Create /etc/ssh/sshd_config.d/10-hardening.conf on the server:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
AllowUsers alice

What each line does:

  • PasswordAuthentication no stops the server from accepting passwords.
  • KbdInteractiveAuthentication no closes the second route to a password prompt. Debian already sets it, and repeating it here costs nothing.
  • PermitRootLogin no stops direct logins as root. Log in as yourself and use sudo.
  • AllowUsers alice lets only the listed accounts log in over SSH. Put your own username here, and add others separated by spaces.

Check the configuration before you apply it. sshd -t prints nothing when the syntax is fine, and sshd -T prints the settings sshd will actually use:

sudo sshd -t
sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin|allowusers'
sudo systemctl reload ssh

From a second terminal, test both ways in. A key login should work as before. A forced password login should fail with Permission denied (publickey):

ssh homeserver
ssh -o PubkeyAuthentication=no alice@192.168.1.20

Password-guessing bots: OpenSSH penalties and fail2ban

As soon as port 22 is reachable from the internet, bots start trying common usernames and passwords. With password login switched off they cannot get in, but they fill your logs and use a little processor time.

OpenSSH now handles much of this itself. Since version 9.8, the server has a PerSourcePenalties setting that makes it refuse connections for a while from addresses that fail to log in, crash it, or connect without authenticating. It is on by default. On Debian 13, sshd -T shows these defaults:

persourcepenalties crash:90 authfail:5 noauth:1 grace-exceeded:10 refuseconnection:10 max:600 min:15 ...

In plain terms, a client that disconnects after failed login attempts earns its address a 5-second penalty, penalties add up to at most 600 seconds, and an address is refused only once its penalty passes 15 seconds. For a home server with key-only login, this is often enough.

fail2ban goes further. It reads the logs, counts failures per address and adds firewall rules that block an address for a set time. It can watch other services too, such as a web server or a mail server. On Debian 13, installing the package is enough to protect SSH:

sudo apt install fail2ban python3-systemd

Debian's own file /etc/fail2ban/jail.d/defaults-debian.conf enables the sshd jail, reads the systemd journal and blocks with nftables. The defaults in jail.conf ban an address for 10 minutes after 5 failures within 10 minutes. To change them, create /etc/fail2ban/jail.local rather than editing jail.conf, then check the jail:

[sshd]
bantime = 1h
maxretry = 3
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Moving SSH to a port other than 22 cuts the noise in the logs, because most bots only try port 22. It does not add real security, and it means adding a Port line to every client config. Key-only login does the real work. The better question is whether SSH needs to face the internet at all. A VPN such as WireGuard or Tailscale lets you reach the server from outside without opening port 22. The port forwarding guide compares the options.

SSH tunnels: reach a service without opening a port

SSH can carry other network traffic through its encrypted connection. This is called port forwarding or tunnelling, and it is the safest way to reach an admin page that should never be on the internet.

Local forwarding (-L) makes a port on your computer lead to a port on, or reachable from, the server. Say an admin page listens only on localhost:8080 on the server:

ssh -N -L 9000:localhost:8080 homeserver

While this runs, opening http://localhost:9000 on your computer shows the server's page. -N means "don't open a shell, just forward". Add -f to send it to the background.

Dynamic forwarding (-D) turns the connection into a SOCKS proxy. ssh -N -D 1080 homeserver lets a browser set to use the SOCKS proxy localhost:1080 browse as if it were on your home network. That is useful for reaching your router's admin page while you are away.

Remote forwarding (-R) works the other way round: a port on the server leads back to your computer. ssh -N -R 8080:localhost:3000 homeserver lets the server reach a service on your laptop's port 3000.

Jump hosts (-J) connect through one server to reach another. ssh -J homeserver pi@192.168.1.30 reaches a second machine on your home network through the server. In ~/.ssh/config, the same thing is a ProxyJump homeserver line.

Tunnels also let you manage an XMPP server's admin interface or any other local-only page without exposing it. The tunnel only stays up while the SSH connection does. Forwarding is allowed by default (AllowTcpForwarding yes). Ports opened with -R listen only on the server's localhost unless you change GatewayPorts, which defaults to no.

When a connection fails

Run the client with -v to see each step of the connection, including which keys it offers and why the server refused them:

ssh -v homeserver

Common causes:

  • Connection refused or timed out: sshd is not running (systemctl status ssh on the server), a firewall blocks port 22, or you have the wrong address.
  • Permission denied (publickey): the server did not accept any key offered. Check that the public key is in the right user's authorized_keys, that the permissions are 700 and 600, and that your username is in AllowUsers.
  • Host key verification failed: the server's host key differs from the one saved in known_hosts. If you know why (a reinstall), remove the old entry with ssh-keygen -R 192.168.1.20. If you don't, stop and find out.

On the server, sudo journalctl -u ssh shows why sshd refused a login.

Frequently asked questions

Which SSH key type should I use?

Use Ed25519. It is the default key type in current OpenSSH, so ssh-keygen -t ed25519 and plain ssh-keygen create the same kind of key.

Where do I put my public SSH key on the server?

Add it as one line in ~/.ssh/authorized_keys in the home directory of the account you log in as. The ~/.ssh directory needs mode 700 and the file needs mode 600, or OpenSSH ignores it.

How do I disable SSH password login on Debian?

Create a file in /etc/ssh/sshd_config.d/ with PasswordAuthentication no and KbdInteractiveAuthentication no, check it with sudo sshd -t, then run sudo systemctl reload ssh. Test key login from a second terminal before you close your current session.

Do I still need fail2ban if password login is off?

Not strictly. OpenSSH 9.8 and later penalize addresses that fail to log in through PerSourcePenalties, which is on by default. fail2ban adds firewall rules that block an address for a set time, and it can watch other services too.

What is the ~/.ssh/config file for?

It stores per-server settings on your computer, such as the address, username, port and key file, under a short name. With an entry called homeserver, ssh homeserver connects with all those settings.

How do I make an SSH tunnel to a service on my server?

Use local forwarding: ssh -N -L 9000:localhost:8080 homeserver makes localhost:9000 on your computer lead to port 8080 on the server. The tunnel lasts as long as the SSH connection.

Sources

  1. sshd_config(5) manual, OpenBSD (OpenSSH)
  2. ssh(1) manual, OpenBSD (OpenSSH)
  3. ssh-keygen(1) manual, OpenBSD (OpenSSH)
  4. ssh_config(5) manual, OpenBSD (OpenSSH)
  5. OpenSSH 9.8 release notes (PerSourcePenalties), 1 July 2024
  6. SSH, Debian Wiki
  7. fail2ban, project repository