TL;DR: SSH (Secure Shell) is a protocol for securely connecting to and controlling remote computers. You type ssh user@server-ip and get a command line on the server as if you were sitting in front of it. Authentication is either by password or, better, by SSH key pair — a private key on your machine matched to a public key on the server. Key auth is the standard for any real deployment.

Why AI Coders Need to Know This

Every AI-generated VPS deployment guide starts with "SSH into your server." If you do not know SSH, you are blocked before you even start. SSH is the gateway to all server management: installing nginx, starting your app, viewing logs, running migrations, updating code.

Beyond basic access, SSH keys are how professional deployments authenticate. GitHub uses SSH keys. CI/CD pipelines use SSH keys. Claude Code's server operations use SSH. Understanding the key pair model takes about 15 minutes — and once you understand it, every deployment guide makes sense.

How SSH Works

SSH creates an encrypted tunnel between your machine and a remote server. Everything you type and everything the server returns travels through this encrypted connection — no one in the middle can read it.

The connection has two parts:

  1. Encryption: After connection, all traffic is encrypted using symmetric keys negotiated at connection time.
  2. Authentication: The server needs to verify that you are who you say you are. Two methods: password or SSH key.

Password authentication

ssh root@192.168.0.174
# → Enter password: ••••••••

Simple, but has problems. Weak passwords can be brute-forced. Password reuse across servers is dangerous. Most VPS providers disable root password auth by default because automated attacks attempt millions of password combinations per hour.

SSH key authentication

Key authentication uses a mathematical pair:

  • Private key (~/.ssh/id_ed25519) — stays on your machine. Never share it, never upload it anywhere.
  • Public key (~/.ssh/id_ed25519.pub) — placed on the server in ~/.ssh/authorized_keys. Safe to share widely.

When you connect, the server sends a random challenge. Your SSH client signs it with your private key. The server verifies the signature using your public key. If it matches, you are in — your private key never leaves your machine.

Setting Up SSH Keys

# Step 1: Generate a key pair (on your local machine)
ssh-keygen -t ed25519 -C "your-email@example.com"
# Saves to ~/.ssh/id_ed25519 (private) and ~/.ssh/id_ed25519.pub (public)
# Ed25519 is the modern, preferred algorithm

# Step 2: Copy your public key to the server
ssh-copy-id user@server-ip
# Or manually: cat ~/.ssh/id_ed25519.pub | ssh user@server-ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Step 3: Test the connection
ssh user@server-ip
# Should connect without password prompt

# Step 4: (Recommended) Disable password auth on server
# Edit /etc/ssh/sshd_config:
# PasswordAuthentication no
# sudo systemctl restart sshd

Real Scenario

Prompt I Would Type

I have a fresh Ubuntu 24.04 VPS from DigitalOcean with IP 203.0.113.1.
Walk me through:
1. First connection as root
2. Creating a deployment user (non-root)
3. Setting up SSH key authentication for the deploy user
4. Disabling root login and password auth
5. Basic firewall setup

What AI Generated

# First connection (password from provider)
ssh root@203.0.113.1

# Create deploy user with sudo privileges
adduser deploy
usermod -aG sudo deploy

# Set up SSH keys for deploy user
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh

# Copy your local public key to the server
# (Run this on your LOCAL machine, not the server)
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@203.0.113.1

# Harden SSH config
nano /etc/ssh/sshd_config
# Set these values:
# PermitRootLogin no
# PasswordAuthentication no
# PubkeyAuthentication yes

sudo systemctl restart sshd

# Test new user (in a NEW terminal, keep old session open)
ssh deploy@203.0.113.1
# Should connect without password

# Basic firewall with UFW
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

Understanding Each Part

Why create a non-root user?

The root user has unlimited privileges — a compromised root account means full server access. A deploy user with sudo can do everything root can, but requires deliberate escalation. Automated attacks specifically target the root username, so disabling root login removes a major attack vector.

File permissions matter

SSH is strict about permissions on key files. If permissions are too open, SSH refuses to use the key:

# Private key — only you should read it
chmod 600 ~/.ssh/id_ed25519

# Public key and authorized_keys
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys

# .ssh directory
chmod 700 ~/.ssh

The error "Permissions too open" means your private key file is group- or world-readable. Fix it with chmod 600.

SSH config file (~/.ssh/config)

Instead of typing ssh deploy@203.0.113.1 -i ~/.ssh/my-key -p 22 every time, save it in ~/.ssh/config:

# ~/.ssh/config
Host myapp
    HostName 203.0.113.1
    User deploy
    IdentityFile ~/.ssh/id_ed25519
    Port 22

Host myapp-staging
    HostName 203.0.113.2
    User deploy
    IdentityFile ~/.ssh/id_ed25519

Now you connect with just: ssh myapp. AI often generates these config files when setting up multi-server deployments.

Copying files with SCP and rsync

# Copy a single file to the server
scp ./dist/index.html deploy@203.0.113.1:/var/www/html/

# Copy a directory recursively
scp -r ./dist/ deploy@203.0.113.1:/var/www/html/

# Sync only changed files (much faster for deployments)
rsync -avz --delete ./dist/ deploy@203.0.113.1:/var/www/html/

rsync is preferred for deployments — it only transfers files that have changed, making redeployments fast.

What AI Gets Wrong About SSH

Testing new auth in the same session

AI sometimes tells you to disable password auth and then test in the same SSH session. If something is wrong, you are locked out. Always open a new terminal and test the new authentication method before ending the old session. Keep two terminal windows open during any SSH hardening work.

Forgetting to copy the public key before disabling passwords

Disabling PasswordAuthentication without first placing your public key in authorized_keys locks you out. Order matters: keys first, hardening second.

Wrong key in authorized_keys

Copying the private key instead of the public key. The file you add to the server is id_ed25519.pub (ends in .pub). The file without .pub is your private key — it never leaves your machine.

Locked Out Recovery

If you lock yourself out of a VPS, most providers offer a web console (DigitalOcean Recovery Console, Hetzner VNC Console) that bypasses SSH. Use it to fix your SSH config. This is why you test before ending the old session.

What to Learn Next

Next Step

Generate an SSH key pair right now if you do not have one: ssh-keygen -t ed25519 -C "your@email.com". Add the public key to your GitHub account settings. The same key works for SSH server access and GitHub authentication — one key pair, used everywhere.

FAQ

SSH (Secure Shell) is a cryptographic protocol for securely connecting to and controlling remote computers. It encrypts all communication between your machine and the server, making it safe to manage servers over the public internet.

An SSH key pair consists of a private key (stays on your computer, never shared) and a public key (placed on the server). When you connect, the server verifies you have the private key matching the stored public key — without the private key ever being transmitted over the network.

The server rejected your connection. Common causes: you are using the wrong username, the wrong key file, your public key is not in the server's authorized_keys, or your private key file has permissions that are too open. Run ssh -v user@server for verbose output that shows exactly where the handshake fails.

Use scp for individual files: scp file.txt user@server:/path/. Use rsync for directories and deployments: rsync -avz ./dist/ user@server:/var/www/html/. rsync is preferred because it only transfers changed files.

Yes — using the same key pair across multiple servers is common practice and simplifies management. The private key stays on your machine; you just place the public key on each server. If you need to revoke access to one server, remove the public key from that server's authorized_keys without affecting the others.