An SSH key is the modern, secure way to log in to servers, push to Git, and run automated deployments without typing a password every time. This guide shows you how to generate an SSH key on Ubuntu (and macOS — the steps are identical), where the files go, how to protect them, and how to install your key on a server, GitHub, or Bitbucket.
What an SSH key actually is
An SSH key is a matched pair of cryptographic files: a private key that stays on your machine and is never shared, and a public key (the one ending in .pub) that you copy to any server or service you want to log in to. When you connect, the server uses your public key to challenge your machine, and only the matching private key can answer. This is far stronger than a password — there is nothing to guess or brute-force — and it is what makes passwordless, automated deployments possible.
Step 1 — Check for an existing key
Before creating a new key, see whether you already have one so you don’t overwrite it:
ls -al ~/.ssh
If you see files like id_ed25519 and id_ed25519.pub (or id_rsa / id_rsa.pub), you already have a key pair you can reuse. If the .ssh directory doesn’t exist yet, that’s fine — ssh-keygen will create it.
Step 2 — Generate the key with ssh-keygen
You need OpenSSH installed. It ships by default on Ubuntu and macOS; if the command is missing on Ubuntu, install it with sudo apt update && sudo apt install openssh-client. Now generate a modern Ed25519 key:
ssh-keygen -t ed25519 -C "[email protected]"
It will prompt you for three things:
- File location. Press Enter to accept the default (
~/.ssh/id_ed25519), or type a custom name like~/.ssh/deploy_keyif you want a dedicated key for a specific server or project. - Passphrase. Optional but strongly recommended for keys on your laptop — it encrypts the private key on disk, so a stolen file is useless without the passphrase.
- Confirm passphrase.
When it finishes you’ll have two files: the private key (no extension) and the public key (.pub). The tool also prints a “randomart” image — that’s just a visual fingerprint, nothing you need to keep.
Ed25519 vs RSA: which type should you choose?
| Key type | Command | When to use |
|---|---|---|
| Ed25519 | ssh-keygen -t ed25519 | The default choice in 2026 — fast, short, very secure. Use this unless something forbids it. |
| RSA 4096 | ssh-keygen -t rsa -b 4096 | Only for older systems or services that don’t support Ed25519 yet. |
Step 3 — Fix file permissions
SSH is strict about permissions and will refuse a private key that other users can read. ssh-keygen sets these correctly, but if you ever copy keys around, reset them with:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
Step 4 — Load the key into ssh-agent
The ssh-agent holds your decrypted key in memory so you only type the passphrase once per session:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
On macOS, store the passphrase in the Keychain so it loads automatically: ssh-add --apple-use-keychain ~/.ssh/id_ed25519.
Step 5 — Install the public key where you want to log in
On a server (Ubuntu/VPS/shared hosting)
The easiest way is ssh-copy-id, which appends your public key to the server’s ~/.ssh/authorized_keys:
ssh-copy-id user@your-server-ip
If ssh-copy-id isn’t available, do it manually — print the public key, then paste it onto a new line in the server’s ~/.ssh/authorized_keys file:
cat ~/.ssh/id_ed25519.pub
From then on, ssh user@your-server-ip logs you in with the key. Once key login works, hardening tip: disable password authentication on the server so brute-force attempts can’t succeed.
On GitHub or Bitbucket
Copy the public key contents and paste them into your account’s SSH keys settings (GitHub: Settings → SSH and GPG keys; Bitbucket: Personal settings → SSH keys). Then verify:
ssh -T [email protected]
ssh -T [email protected]
Bonus — Manage multiple keys with ~/.ssh/config
If you use different keys for different servers or Git accounts, a config file keeps it tidy so you never have to pass -i by hand:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host my-vps
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/deploy_key
IdentitiesOnly yes
Now ssh my-vps connects with the right key automatically. Juggling several keys or two GitHub accounts? See our full guide on using multiple SSH keys per host with ssh config.
Common ssh command flags you’ll actually use
Once your key works, these are the ssh client flags worth knowing. Most day-to-day connections only need -i and -p, but the rest solve real problems — debugging failed logins, jumping through a bastion, or tunneling a port.
| Flag | What it does | Example |
|---|---|---|
-i <file> | Use a specific private key (identity file) | ssh -i ~/.ssh/deploy_key user@host |
-p <port> | Connect to a non-default port (lowercase for ssh) | ssh -p 2222 user@host |
-o IdentitiesOnly=yes | Offer only the key you specified — fixes “Too many authentication failures” when the agent has many keys | ssh -i key -o IdentitiesOnly=yes user@host |
-v / -vvv | Verbose / very verbose — shows exactly which key is offered and why auth fails | ssh -vvv user@host |
-J <jump> | ProxyJump — connect through a bastion/jump host in one command | ssh -J user@bastion user@private-host |
-L <lport>:<host>:<rport> | Local port forward (tunnel a remote service to your machine) | ssh -L 5432:localhost:5432 user@db-host |
-N | Don’t run a remote command — used with -L for pure tunnels | ssh -N -L 8080:localhost:80 user@host |
-A | Forward your ssh-agent (use sparingly — only with trusted hosts) | ssh -A user@host |
-t | Force a pseudo-terminal (needed for interactive remote commands) | ssh -t user@host 'sudo systemctl status nginx' |
-C | Compress the session — handy on slow links | ssh -C user@host |
Anything you can pass as -o Option=value can also live in ~/.ssh/config so you never retype it. The companion scp tool shares most of these flags — with one trap: scp uses a capital -P for the port while ssh uses lowercase -p. See our SCP guide for the full file-copy flag list.
Troubleshooting common errors
- “Permissions 0644 for ‘id_ed25519’ are too open.” Run
chmod 600 ~/.ssh/id_ed25519. - “Could not open a connection to your authentication agent.” Start the agent first:
eval "$(ssh-agent -s)", thenssh-add. - “Permission denied (publickey).” The server doesn’t have your public key, or you’re offering the wrong one. Confirm the key is in
authorized_keysand test withssh -vto see which key is offered. - Passphrase asked every time. Add the key to the agent (above), or on macOS use
--apple-use-keychain.
Next steps
With a working key you can move files securely and automate deployments. See our related guides: copy files over SSH with SCP and set up CI/CD with Bitbucket Pipelines.
Frequently asked questions
How do I generate an SSH key on Ubuntu?
Open a terminal and run ssh-keygen -t ed25519 -C "[email protected]", press Enter to accept the default location, and optionally set a passphrase. This creates ~/.ssh/id_ed25519 (private) and ~/.ssh/id_ed25519.pub (public).
Where are SSH keys stored?
By default in the ~/.ssh directory in your home folder, as id_ed25519 (private) and id_ed25519.pub (public).
Should I set a passphrase on my SSH key?
Yes for keys on a personal machine — it encrypts the private key so a stolen file is useless. Use ssh-agent so you only enter it once per session. Automated deploy keys on a CI server are often passphrase-less and protected by other means instead.
Ed25519 or RSA — which is better?
Use Ed25519. It’s faster, shorter, and at least as secure as RSA 4096. Only fall back to rsa -b 4096 for legacy systems that don’t support Ed25519.