[{"data":1,"prerenderedAt":1035},["ShallowReactive",2],{"blog-how-to-generate-ssh-keys-safely":3},{"id":4,"title":5,"alt":6,"author":7,"body":8,"category":1013,"description":1014,"extension":1015,"faq":1016,"image":1024,"meta":1025,"navigation":695,"path":1026,"publishedAt":1027,"seo":1028,"stem":1029,"tags":1030,"__hash__":1034},"blog\u002Fen\u002Fhow-to-generate-ssh-keys-safely.md","How to Generate an SSH Key Safely (2026 Guide)","generate ssh key — terminal showing ssh-keygen ed25519 command with key fingerprint output","Alex Vibe, Senior Security Dev",{"type":9,"value":10,"toc":999},"minimark",[11,25,32,35,40,51,66,76,88,103,105,109,112,225,231,234,241,243,247,252,288,303,307,313,320,324,330,340,375,386,390,417,424,426,430,433,440,443,458,461,471,488,590,600,611,629,637,639,643,649,655,713,719,770,776,796,808,810,814,817,835,841,889,896,898,930,932,936,941,947,952,955,960,966,971,977,982,995],[12,13,14,15,19,20,24],"p",{},"The command you want is ",[16,17,18],"code",{},"ssh-keygen -t ed25519 -C \"you@example.com\"",". When it asks for a passphrase, ",[21,22,23],"strong",{},"do not press Enter to skip it"," — type a strong one. That's the entire safe-SSH-key recipe in one line, and most tutorials get the second half wrong.",[12,26,27,28,31],{},"Here's why the passphrase matters more than the algorithm: your private key is a file sitting on disk. If a laptop gets stolen, a backup leaks, or malware reads ",[16,29,30],{},"~\u002F.ssh\u002F",", an unencrypted private key is an instant root shell on every server it can reach. The passphrase encrypts that file with the bcrypt KDF, turning a one-second theft into a multi-century brute-force problem. This guide covers the algorithm choice, the key-generation steps, the entropy math behind the passphrase, and the deployment hygiene that keeps your keys from becoming someone else's access.",[33,34],"hr",{},[36,37,39],"h2",{"id":38},"why-ssh-keys-beat-passwords","Why SSH Keys Beat Passwords",[12,41,42,43,46,47,50],{},"Password authentication over SSH is a brute-force target. Every internet-facing server with ",[16,44,45],{},"PasswordAuthentication yes"," gets hammered by bots trying ",[16,48,49],{},"root:123456"," thousands of times per hour. Key-based auth removes that attack surface entirely.",[12,52,53,54,57,58,61,62,65],{},"An SSH key pair is asymmetric cryptography: a ",[21,55,56],{},"public key"," you hand out freely (it goes in ",[16,59,60],{},"~\u002F.ssh\u002Fauthorized_keys"," on the server) and a ",[21,63,64],{},"private key"," you guard with your life. The server encrypts a challenge with your public key; only your private key can answer it. The private key never crosses the network. There's nothing for a network sniffer to capture and nothing for a server breach to leak — the server only ever stores public keys.",[67,68,73],"pre",{"className":69,"code":71,"language":72},[70],"language-text","Client                                    Server\n  │                                          │\n  │  1. \"Log me in as alex\"                  │\n  │ ────────────────────────────────────────►\n  │                                          │\n  │  2. Random challenge, encrypted          │\n  │     with your PUBLIC key                 │\n  ◄────────────────────────────────────────│\n  │                                          │\n  │  3. Decrypt with PRIVATE key             │\n  │     (never leaves this machine),         │\n  │     sign the proof                       │\n  │ ────────────────────────────────────────►\n  │                                          │\n  │  4. Proof valid → access granted         │\n  ◄────────────────────────────────────────│\n","text",[16,74,71],{"__ignoreMap":75},"",[12,77,78,79,82,83,87],{},"The private key stays pinned to your machine through the entire handshake. ",[21,80,81],{},"ssh-agent"," (covered below) holds the ",[84,85,86],"em",{},"decrypted"," key in volatile memory for the session — but even then it never travels over the wire; only signatures do.",[12,89,90,91,94,95,98,99,102],{},"Once keys are working, set ",[16,92,93],{},"PasswordAuthentication no"," in ",[16,96,97],{},"sshd_config",". ",[21,100,101],{},"That single change defeats every credential-stuffing bot on the internet."," It just works.",[33,104],{},[36,106,108],{"id":107},"choosing-the-algorithm-ed25519-vs-rsa-vs-ecdsa","Choosing the Algorithm: ed25519 vs RSA vs ECDSA",[12,110,111],{},"This is where 90% of outdated tutorials fail you. They still tell you to generate RSA-2048. Don't.",[113,114,115,137],"table",{},[116,117,118],"thead",{},[119,120,121,125,128,131,134],"tr",{},[122,123,124],"th",{},"Algorithm",[122,126,127],{},"Key Size",[122,129,130],{},"Security Level",[122,132,133],{},"Speed",[122,135,136],{},"Verdict (2026)",[138,139,140,160,177,193,208],"tbody",{},[119,141,142,148,151,154,157],{},[143,144,145],"td",{},[21,146,147],{},"ed25519",[143,149,150],{},"256-bit",[143,152,153],{},"~128-bit",[143,155,156],{},"Fastest",[143,158,159],{},"✅ Default choice",[119,161,162,165,168,171,174],{},[143,163,164],{},"RSA-4096",[143,166,167],{},"4096-bit",[143,169,170],{},"~140-bit",[143,172,173],{},"Slow",[143,175,176],{},"⚠️ Only for legacy hosts",[119,178,179,182,185,188,190],{},[143,180,181],{},"RSA-2048",[143,183,184],{},"2048-bit",[143,186,187],{},"~112-bit",[143,189,173],{},[143,191,192],{},"❌ Below modern minimum",[119,194,195,198,200,202,205],{},[143,196,197],{},"ECDSA (P-256)",[143,199,150],{},[143,201,153],{},[143,203,204],{},"Fast",[143,206,207],{},"⚠️ NIST-curve trust concerns",[119,209,210,213,216,219,222],{},[143,211,212],{},"DSA",[143,214,215],{},"1024-bit",[143,217,218],{},"Broken",[143,220,221],{},"—",[143,223,224],{},"❌ Removed from OpenSSH",[12,226,227,230],{},[21,228,229],{},"Ed25519 is the answer for new keys."," It's built on the Edwards-curve digital signature algorithm over Curve25519, designed by Daniel J. Bernstein with explicit resistance to the side-channel and bad-randomness failures that have repeatedly broken RSA and ECDSA implementations in the wild. The keys are tiny — a 68-character public key versus RSA-4096's wall of base64.",[12,232,233],{},"Reach for RSA-4096 only when you're connecting to an ancient appliance or a CI system that hasn't seen an OpenSSH update since 2014. ECDSA works but relies on NIST P-curves, whose parameter generation has never fully shaken off the post-Dual_EC_DRBG trust questions. When ed25519 is available, there's no reason to pick it.",[12,235,236],{},[237,238],"img",{"alt":239,"src":240},"generate ssh key — comparison diagram of ed25519 RSA and ECDSA key sizes and security levels","\u002Fimages\u002Fblog\u002Fhow-to-generate-ssh-keys-safely\u002Falgorithm-comparison.webp",[33,242],{},[36,244,246],{"id":245},"step-by-step-generate-your-key-pair","Step-by-Step: Generate Your Key Pair",[248,249,251],"h3",{"id":250},"_1-run-ssh-keygen","1. Run ssh-keygen",[67,253,257],{"className":254,"code":255,"language":256,"meta":75,"style":75},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","ssh-keygen -t ed25519 -C \"you@example.com\"\n","bash",[16,258,259],{"__ignoreMap":75},[260,261,264,268,272,275,278,282,285],"span",{"class":262,"line":263},"line",1,[260,265,267],{"class":266},"sBMFI","ssh-keygen",[260,269,271],{"class":270},"sfazB"," -t",[260,273,274],{"class":270}," ed25519",[260,276,277],{"class":270}," -C",[260,279,281],{"class":280},"sMK4o"," \"",[260,283,284],{"class":270},"you@example.com",[260,286,287],{"class":280},"\"\n",[12,289,290,291,294,295,298,299,302],{},"The ",[16,292,293],{},"-C"," flag adds a comment — use an identifier like your email or ",[16,296,297],{},"laptop-2026"," so you can tell keys apart later in ",[16,300,301],{},"authorized_keys",".",[248,304,306],{"id":305},"_2-choose-a-file-location","2. Choose a file location",[67,308,311],{"className":309,"code":310,"language":72},[70],"Enter file in which to save the key (~\u002F.ssh\u002Fid_ed25519):\n",[16,312,310],{"__ignoreMap":75},[12,314,315,316,319],{},"Press Enter for the default, or specify a name like ",[16,317,318],{},"~\u002F.ssh\u002Fid_ed25519_github"," if you keep per-service keys (a good habit — see below).",[248,321,323],{"id":322},"_3-set-a-strong-passphrase","3. Set a strong passphrase",[67,325,328],{"className":326,"code":327,"language":72},[70],"Enter passphrase (empty for no passphrase):\n",[16,329,327],{"__ignoreMap":75},[12,331,332,335,336,339],{},[21,333,334],{},"This is the step nobody should skip."," The passphrase encrypts your private key at rest using OpenSSH's bcrypt KDF (",[16,337,338],{},"bcrypt_pbkdf","). The KDF runs a fixed number of expansion rounds — 16 by default — and each round multiplies the cost of every guess an attacker has to make. You can crank that higher at generation time:",[67,341,343],{"className":254,"code":342,"language":256,"meta":75,"style":75},"# 100 KDF rounds — barely slower for you, far slower to brute-force\nssh-keygen -t ed25519 -a 100 -C \"you@example.com\"\n",[16,344,345,351],{"__ignoreMap":75},[260,346,347],{"class":262,"line":263},[260,348,350],{"class":349},"sHwdD","# 100 KDF rounds — barely slower for you, far slower to brute-force\n",[260,352,354,356,358,360,363,367,369,371,373],{"class":262,"line":353},2,[260,355,267],{"class":266},[260,357,271],{"class":270},[260,359,274],{"class":270},[260,361,362],{"class":270}," -a",[260,364,366],{"class":365},"sbssI"," 100",[260,368,277],{"class":270},[260,370,281],{"class":280},[260,372,284],{"class":270},[260,374,287],{"class":280},[12,376,377,378,381,382,385],{},"A higher ",[16,379,380],{},"-a"," value adds a near-imperceptible delay when ",[84,383,384],{},"you"," unlock the key but multiplies an attacker's per-guess cost linearly. We'll do the math on this next.",[248,387,389],{"id":388},"_4-confirm-what-got-created","4. Confirm what got created",[67,391,393],{"className":254,"code":392,"language":256,"meta":75,"style":75},"ls -la ~\u002F.ssh\u002F\n# id_ed25519       \u003C- private key (NEVER share, NEVER commit)\n# id_ed25519.pub   \u003C- public key (safe to distribute)\n",[16,394,395,406,411],{"__ignoreMap":75},[260,396,397,400,403],{"class":262,"line":263},[260,398,399],{"class":266},"ls",[260,401,402],{"class":270}," -la",[260,404,405],{"class":270}," ~\u002F.ssh\u002F\n",[260,407,408],{"class":262,"line":353},[260,409,410],{"class":349},"# id_ed25519       \u003C- private key (NEVER share, NEVER commit)\n",[260,412,414],{"class":262,"line":413},3,[260,415,416],{"class":349},"# id_ed25519.pub   \u003C- public key (safe to distribute)\n",[12,418,419,420,423],{},"You now have a key pair. The ",[16,421,422],{},".pub"," file is the only half that ever leaves your machine.",[33,425],{},[36,427,429],{"id":428},"the-math-why-your-passphrase-length-decides-everything","The Math: Why Your Passphrase Length Decides Everything",[12,431,432],{},"An empty passphrase means your private key file is plaintext gold. Steal it, use it — game over. A passphrase changes the threat model completely: an attacker who copies the file still has to brute-force the encryption.",[12,434,435,436,439],{},"Passphrase strength is measured in ",[21,437,438],{},"entropy bits",". For a passphrase built from random dictionary words:",[12,441,442],{},"$$H = W \\times \\log_2(7776) \\approx W \\times 12.9$$",[12,444,445,446,449,450,453,454,457],{},"Where ",[21,447,448],{},"H"," = entropy in bits, ",[21,451,452],{},"W"," = number of words, and ",[21,455,456],{},"7,776"," is the size of the EFF large wordlist (each word contributes ~12.9 bits). For a character-based passphrase the formula is:",[12,459,460],{},"$$H = L \\times \\log_2(R)$$",[12,462,445,463,466,467,470],{},[21,464,465],{},"L"," = length in characters and ",[21,468,469],{},"R"," = the character pool size.",[12,472,473,474,476,477,479,480,483,484,487],{},"Now the part that matters: OpenSSH encrypts the private key with ",[16,475,338],{},", a deliberately slow KDF that runs many internal expansion passes per guess (16 rounds by default, more if you used ",[16,478,380],{},"). This is ",[21,481,482],{},"not"," the standard password-table bcrypt you see quoted at ~184,000 h\u002Fs. Those extra key-expansion passes drag the attack rate down to ",[21,485,486],{},"under 100 attempts per second on a top-tier RTX 4090"," — a glacial crawl next to the ~164 billion\u002Fsec that same card manages against raw MD5. Here's what that buys you:",[113,489,490,503],{},[116,491,492],{},[119,493,494,497,500],{},[122,495,496],{},"Passphrase",[122,498,499],{},"Entropy",[122,501,502],{},"RTX 4090 Crack Time (bcrypt_pbkdf, 16 rounds)",[138,504,505,516,529,540,551,568,579],{},[119,506,507,510,513],{},[143,508,509],{},"(empty)",[143,511,512],{},"0 bits",[143,514,515],{},"Instant — file is plaintext",[119,517,518,523,526],{},[143,519,520],{},[16,521,522],{},"letmein2026",[143,524,525],{},"~28 bits",[143,527,528],{},"Minutes — known dictionary pattern",[119,530,531,534,537],{},[143,532,533],{},"2 random EFF words",[143,535,536],{},"~26 bits",[143,538,539],{},"~3 days",[119,541,542,545,548],{},[143,543,544],{},"3 random EFF words",[143,546,547],{},"~39 bits",[143,549,550],{},"~70 years",[119,552,553,558,563],{},[143,554,555],{},[21,556,557],{},"4 random EFF words",[143,559,560],{},[21,561,562],{},"~52 bits",[143,564,565],{},[21,566,567],{},"~500,000 years",[119,569,570,573,576],{},[143,571,572],{},"5 random EFF words",[143,574,575],{},"~65 bits",[143,577,578],{},"Billions of years",[119,580,581,584,587],{},[143,582,583],{},"16-char random (full ASCII)",[143,585,586],{},"~105 bits",[143,588,589],{},"Heat death of the universe",[12,591,592,595,596,599],{},[21,593,594],{},"Four random words is the comfortable floor."," Three already buys decades against a stolen key file; four pushes it into the hundreds-of-thousands-of-years range, and five is the right call for keys guarding production. And \"random\" is the operative word — ",[16,597,598],{},"correct horse battery staple"," is famous, which means it's in every cracking dictionary. Your brain is a terrible CSPRNG.",[12,601,602,603,610],{},"Generate the passphrase, don't invent it. Our ",[21,604,605],{},[606,607,609],"a",{"href":608},"\u002F","Password Generator"," — runs 100% in your browser, zero data sent to any server — has a passphrase mode that pulls true random words and shows live entropy in bits as you adjust the word count. Set it to 4–5 words and you've cleared the table above.",[12,612,613,614,617,618,620,621,624,625,628],{},"Avoid tools that use ",[16,615,616],{},"Math.random()",". Our ",[606,619,609],{"href":608}," uses the ",[21,622,623],{},"Web Crypto API"," (",[16,626,627],{},"crypto.getRandomValues()","), ensuring your entropy source is as secure as your operating system's kernel. A passphrase generated from a predictable PRNG isn't random at all — it just looks that way until someone reverse-engineers the seed.",[12,630,631,632,636],{},"Want to sanity-check a passphrase you already use? Drop it into the ",[606,633,635],{"href":634},"\u002Fpassword-strength-checker","Password Strength Checker"," to see its entropy and estimated crack time before you commit it to a key that guards production.",[33,638],{},[36,640,642],{"id":641},"managing-keys-ssh-agent-permissions-and-fingerprints","Managing Keys: ssh-agent, Permissions, and Fingerprints",[12,644,645],{},[237,646],{"alt":647,"src":648},"generate ssh key — ssh-agent holding decrypted private key with file permissions and fingerprint verification","\u002Fimages\u002Fblog\u002Fhow-to-generate-ssh-keys-safely\u002Fkey-management.webp",[12,650,651,652,654],{},"A passphrase you type on every connection gets annoying fast, and annoyed engineers disable security. The fix is ",[21,653,81],{}," — it holds your decrypted key in memory for the session, so you authenticate once.",[67,656,658],{"className":254,"code":657,"language":256,"meta":75,"style":75},"# Start the agent and add your key (prompts for passphrase once)\neval \"$(ssh-agent -s)\"\nssh-add ~\u002F.ssh\u002Fid_ed25519\n\n# macOS: store the passphrase in Keychain\nssh-add --apple-use-keychain ~\u002F.ssh\u002Fid_ed25519\n",[16,659,660,665,682,690,697,703],{"__ignoreMap":75},[260,661,662],{"class":262,"line":263},[260,663,664],{"class":349},"# Start the agent and add your key (prompts for passphrase once)\n",[260,666,667,671,674,676,679],{"class":262,"line":353},[260,668,670],{"class":669},"s2Zo4","eval",[260,672,673],{"class":280}," \"$(",[260,675,81],{"class":266},[260,677,678],{"class":270}," -s",[260,680,681],{"class":280},")\"\n",[260,683,684,687],{"class":262,"line":413},[260,685,686],{"class":266},"ssh-add",[260,688,689],{"class":270}," ~\u002F.ssh\u002Fid_ed25519\n",[260,691,693],{"class":262,"line":692},4,[260,694,696],{"emptyLinePlaceholder":695},true,"\n",[260,698,700],{"class":262,"line":699},5,[260,701,702],{"class":349},"# macOS: store the passphrase in Keychain\n",[260,704,706,708,711],{"class":262,"line":705},6,[260,707,686],{"class":266},[260,709,710],{"class":270}," --apple-use-keychain",[260,712,689],{"class":270},[12,714,715,718],{},[21,716,717],{},"Lock down file permissions."," OpenSSH refuses to use keys with loose permissions, and for good reason:",[67,720,722],{"className":254,"code":721,"language":256,"meta":75,"style":75},"chmod 700 ~\u002F.ssh\nchmod 600 ~\u002F.ssh\u002Fid_ed25519        # private key: owner read\u002Fwrite only\nchmod 644 ~\u002F.ssh\u002Fid_ed25519.pub    # public key: world-readable is fine\nchmod 600 ~\u002F.ssh\u002Fauthorized_keys\n",[16,723,724,735,748,761],{"__ignoreMap":75},[260,725,726,729,732],{"class":262,"line":263},[260,727,728],{"class":266},"chmod",[260,730,731],{"class":365}," 700",[260,733,734],{"class":270}," ~\u002F.ssh\n",[260,736,737,739,742,745],{"class":262,"line":353},[260,738,728],{"class":266},[260,740,741],{"class":365}," 600",[260,743,744],{"class":270}," ~\u002F.ssh\u002Fid_ed25519",[260,746,747],{"class":349},"        # private key: owner read\u002Fwrite only\n",[260,749,750,752,755,758],{"class":262,"line":413},[260,751,728],{"class":266},[260,753,754],{"class":365}," 644",[260,756,757],{"class":270}," ~\u002F.ssh\u002Fid_ed25519.pub",[260,759,760],{"class":349},"    # public key: world-readable is fine\n",[260,762,763,765,767],{"class":262,"line":692},[260,764,728],{"class":266},[260,766,741],{"class":365},[260,768,769],{"class":270}," ~\u002F.ssh\u002Fauthorized_keys\n",[12,771,772,775],{},[21,773,774],{},"Verify key fingerprints."," Every public key has a SHA-256 fingerprint — your defense against accepting a man-in-the-middle host key:",[67,777,779],{"className":254,"code":778,"language":256,"meta":75,"style":75},"ssh-keygen -lf ~\u002F.ssh\u002Fid_ed25519.pub\n# 256 SHA256:abc123...xyz you@example.com (ED25519)\n",[16,780,781,791],{"__ignoreMap":75},[260,782,783,785,788],{"class":262,"line":263},[260,784,267],{"class":266},[260,786,787],{"class":270}," -lf",[260,789,790],{"class":270}," ~\u002F.ssh\u002Fid_ed25519.pub\n",[260,792,793],{"class":262,"line":353},[260,794,795],{"class":349},"# 256 SHA256:abc123...xyz you@example.com (ED25519)\n",[12,797,798,799,802,803,807],{},"That fingerprint is just a SHA-256 hash of the public key, base64-encoded. If you ever need to compute or compare SHA-256 \u002F SHA-512 digests by hand — verifying a host key out-of-band, checking a downloaded ",[16,800,801],{},"known_hosts"," entry — the ",[606,804,806],{"href":805},"\u002Fhash-generator","Hash Generator"," does it client-side via the Web Crypto API, no upload required.",[33,809],{},[36,811,813],{"id":812},"deploying-the-public-key-and-the-mistakes-to-avoid","Deploying the Public Key (and the Mistakes to Avoid)",[12,815,816],{},"Getting your public key onto a server is one command:",[67,818,820],{"className":254,"code":819,"language":256,"meta":75,"style":75},"ssh-copy-id -i ~\u002F.ssh\u002Fid_ed25519.pub user@server\n",[16,821,822],{"__ignoreMap":75},[260,823,824,827,830,832],{"class":262,"line":263},[260,825,826],{"class":266},"ssh-copy-id",[260,828,829],{"class":270}," -i",[260,831,757],{"class":270},[260,833,834],{"class":270}," user@server\n",[12,836,837,838,840],{},"This appends your public key to the server's ",[16,839,60],{},". From then on, key auth just works. Here are the mistakes that turn good keys into incidents:",[842,843,844,871,877,883],"ul",{},[845,846,847,850,851,854,855,857,858,861,862,865,866,870],"li",{},[21,848,849],{},"Committing the private key to git."," The ",[16,852,853],{},"id_ed25519"," file (no ",[16,856,422],{},") must never touch a repository. Add ",[16,859,860],{},"id_*"," and ",[16,863,864],{},"*.pem"," to your global gitignore. A leaked private key is a ",[606,867,869],{"href":868},"\u002Fblog\u002Fprotecting-identity-after-data-breach","data breach",", not a typo.",[845,872,873,876],{},[21,874,875],{},"Reusing one key everywhere."," Generate a separate key per context — one for GitHub, one for production, one for your personal VPS. If one leaks, you rotate one, not your entire identity.",[845,878,879,882],{},[21,880,881],{},"No rotation policy."," Keys don't expire on their own. Rotate them annually and immediately when someone leaves the team or a laptop goes missing.",[845,884,885,888],{},[21,886,887],{},"Skipping the passphrase \"just for CI.\""," Use dedicated deploy keys with restricted scope for automation instead. A passphrase-less key in a build runner is the most-stolen credential type in 2026.",[12,890,891,892,895],{},"For phishing-resistant, hardware-backed keys, OpenSSH 8.2+ supports FIDO2 security keys with ",[16,893,894],{},"ssh-keygen -t ed25519-sk",". The private key material lives on the hardware token and never touches disk — the gold standard for production access.",[33,897],{},[899,900,901,906,909],"blockquote",{},[12,902,903],{},[21,904,905],{},"🛡️ Security Checkpoint — Complete This Step",[12,907,908],{},"A passphrase-less or weak SSH key is a single stolen file away from full server compromise. Lock yours down before your next deploy.",[842,910,911,918,924],{},[845,912,913,914,917],{},"→ ",[606,915,916],{"href":608},"Generate a 4–5 word passphrase"," — true-random words via the Web Crypto API to encrypt your private key",[845,919,913,920,923],{},[606,921,922],{"href":634},"Check your passphrase entropy"," — confirm it clears 52+ bits against bcrypt-KDF cracking",[845,925,913,926,929],{},[606,927,928],{"href":805},"Verify a host key fingerprint"," — compute SHA-256 digests client-side to defeat man-in-the-middle attacks",[33,931],{},[36,933,935],{"id":934},"frequently-asked-questions","Frequently Asked Questions",[12,937,938],{},[21,939,940],{},"What is the best command to generate an SSH key in 2026?",[12,942,943,944,946],{},"Run ",[16,945,18],{},". Ed25519 gives you ~128-bit security in a 256-bit key that's faster and smaller than RSA. When it prompts for a passphrase, type a strong one (4+ random words) — that passphrase encrypts the private key on disk with the bcrypt KDF, so a stolen key file is useless without it. Skip RSA unless you're talking to a legacy host that genuinely can't handle ed25519.",[12,948,949],{},[21,950,951],{},"Should I use ed25519 or RSA for SSH keys?",[12,953,954],{},"Use ed25519 for anything new. It's immune to the weak-randomness and side-channel failures that have broken RSA and ECDSA implementations repeatedly, it's faster to verify, and its keys are a fraction of the size. RSA-4096 is your only fallback, reserved for legacy systems that lack ed25519 support. Never generate RSA-2048 in 2026 — it sits below the modern security minimum.",[12,956,957],{},[21,958,959],{},"Does an SSH key really need a passphrase?",[12,961,962,963,965],{},"Yes, with one exception. An unencrypted private key is plaintext access — copy the file, own the servers, no math required. A passphrase encrypts it with ",[16,964,338],{},", whose multiple key-expansion passes hold an attacker to under 100 guesses\u002Fsec even on a top-tier RTX 4090 — centuries of brute force for a 4-word passphrase. The exception is scoped CI deploy keys, where you use restricted, rotatable keys instead of passphrases. For any key tied to a human, always set a passphrase.",[12,967,968],{},[21,969,970],{},"How do I add a passphrase to an existing key without regenerating it?",[12,972,943,973,976],{},[16,974,975],{},"ssh-keygen -p -f ~\u002F.ssh\u002Fid_ed25519",". It prompts for the old passphrase (leave empty if there was none) and lets you set a new one. This re-encrypts the same key pair in place, so your public key stays valid and you don't have to redeploy it to any server. Use it the moment you realize you created a key with an empty passphrase.",[12,978,979],{},[21,980,981],{},"Where are SSH keys stored and how do I back them up?",[12,983,984,985,987,988,990,991,994],{},"Keys live in ",[16,986,30],{}," — ",[16,989,853],{}," (private) and ",[16,992,993],{},"id_ed25519.pub"," (public). Back up the private key only to encrypted storage: a password manager's secure file vault, an encrypted disk image, or a hardware token. Never put it in cloud sync folders, email, or git. The public key needs no protection — you can regenerate or redistribute it freely since it grants no access on its own.",[996,997,998],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":75,"searchDepth":353,"depth":353,"links":1000},[1001,1002,1003,1009,1010,1011,1012],{"id":38,"depth":353,"text":39},{"id":107,"depth":353,"text":108},{"id":245,"depth":353,"text":246,"children":1004},[1005,1006,1007,1008],{"id":250,"depth":413,"text":251},{"id":305,"depth":413,"text":306},{"id":322,"depth":413,"text":323},{"id":388,"depth":413,"text":389},{"id":428,"depth":353,"text":429},{"id":641,"depth":353,"text":642},{"id":812,"depth":353,"text":813},{"id":934,"depth":353,"text":935},"Security","Generate an SSH key the right way: ed25519 over RSA, a high-entropy passphrase, and proper key hygiene. The complete DevOps guide for 2026.","md",[1017,1019,1021],{"question":940,"answer":1018},"Run ssh-keygen -t ed25519 -C \"your_email@example.com\". Ed25519 is faster, smaller, and more secure than RSA. Always add a passphrase when prompted — it encrypts the private key on disk.",{"question":951,"answer":1020},"Use ed25519. It offers ~128-bit security with a 68-character key, is immune to the weak-randomness failures that plagued RSA, and is faster to verify. Only use RSA-4096 if you must support legacy systems that lack ed25519.",{"question":1022,"answer":1023},"Does an SSH key need a passphrase?","Yes. Without a passphrase, anyone who copies your private key file owns your servers instantly. A passphrase encrypts the key with the bcrypt KDF, so a stolen key is useless without it. Use 4+ random words for real protection.","\u002Fimages\u002Fblog\u002Fhow-to-generate-ssh-keys-safely\u002Fhero.webp",{},"\u002Fen\u002Fhow-to-generate-ssh-keys-safely","2026-05-23",{"title":5,"description":1014},"en\u002Fhow-to-generate-ssh-keys-safely",[1031,267,147,1032,1033],"generate ssh key","devops security","ssh passphrase","bS6atlh54KCPfQNfH2sIqRG8LemD2ZuOCeJYbGfd9Ik",1779865049124]