{"id":194,"date":"2026-10-12T17:51:43","date_gmt":"2026-10-12T17:51:43","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2010\/03\/09\/amazon-ec2-key-pairs-and-other-stumbling-blocks\/"},"modified":"2026-10-12T17:51:43","modified_gmt":"2026-10-12T17:51:43","slug":"amazon-ec2-key-pairs-and-other-stumbling-blocks","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/amazon-ec2-key-pairs-and-other-stumbling-blocks\/","title":{"rendered":"EC2 Key Pairs vs AWS Credentials: Practical Fixes for SSH and Security Group Setup"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This guide explains the two setup issues that most often block first-time EC2 deployments: mixing up AWS API credentials with EC2 SSH key pairs, and forgetting to allow SSH in security groups. If you are onboarding to Amazon EC2 or troubleshooting SSH-based automation, you will learn what each credential is for, how to configure access correctly, and how to verify it with practical commands.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Originally published on 2010-03-09; fully revised and updated.<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2010\/03\/amazon-ec2-key-pairs.png\" alt=\"Amazon EC2 key pairs and security group stumbling blocks\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Untangling EC2 key pairs and AWS API credentials<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In the original context, this confusion frequently affected users of <a href=\"http:\/\/code.google.com\/p\/cloudtools\">Cloud Tools<\/a> and <a href=\"http:\/\/www.cloudfoundry.com\/\">Cloud Foundry<\/a>. The core issue is still common: <strong>the key used to sign AWS API calls is not the same key used to SSH into an EC2 instance<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Credential or key<\/th><th>Primary purpose<\/th><th>Where it is used<\/th><th>Common confusion<\/th><\/tr><\/thead><tbody><tr><td>EC2 key pair (key name plus private .pem)<\/td><td>OS login to EC2 over SSH<\/td><td>Key pair name in launch request; private key file in ssh -i<\/td><td>Mistaken for AWS API authentication credentials<\/td><\/tr><tr><td>Access key ID plus secret access key<\/td><td>Programmatic AWS API authentication<\/td><td>AWS CLI, SDKs, automation tools<\/td><td>Cannot be used directly for SSH login<\/td><\/tr><tr><td>X.509 certificate plus private key<\/td><td>Legacy AWS request-signing path<\/td><td>Older tooling and historical workflows<\/td><td>Looks like an SSH key pair but serves a different purpose<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In most modern environments, teams prefer short-lived credentials through IAM roles for API access. But for direct SSH, EC2 key pairs are still a standard pattern unless your organization uses an alternative such as Session Manager.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EC2 access workflow at a glance<\/h2>\n\n\n\n<pre class=\"mermaid\">\nflowchart TD\n    A[Create EC2 key pair] --> B[Download private key file]\n    B --> C[Set secure file permissions]\n    C --> D[Allow SSH in security group]\n    D --> E[Launch instance with key pair name]\n    E --> F[Connect with ssh using private key]\n    F --> G{Connected}\n    G -- no --> H[Check key name security group username and network path]\n    G -- yes --> I[Continue with provisioning]\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create an EC2 key pair and protect the private key<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can create key pairs from the AWS console or CLI. The critical part is that the private key must be stored securely, and access to that file must be restricted.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript language-bash\">aws ec2 create-key-pair --key-name demo-key --query <span class=\"hljs-string\">'KeyMaterial'<\/span> --output text &gt; demo-key.pem\nchmod <span class=\"hljs-number\">400<\/span> demo-key.pem\naws ec2 describe-key-pairs --key-names demo-key<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<ul class=\"wp-block-list\"><li>Open the EC2 console and go to <strong>Key Pairs<\/strong>.<\/li><li>Choose <strong>Create key pair<\/strong>.<\/li><li>Enter a key pair name, create it, and download the private key file immediately.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Historical AWS console screenshots from the original article: <a href=\"http:\/\/files.blog-city.com\/files\/M04\/58308\/p\/f\/keypaircreating.png\">Creating a key pair<\/a> and <a href=\"http:\/\/files.blog-city.com\/files\/M04\/58308\/p\/f\/keypaircreated.png\">Saving a key pair<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Allow SSH in the correct security group<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If port 22 is blocked, SSH-based tooling will fail even when the key pair is correct. Apply ingress rules to the security group attached to your instance, and prefer a narrow source CIDR instead of broad internet access.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs language-bash\">aws ec2 authorize-security-group-ingress \\\n  --group-id sg-0123456789abcdef0 \\\n  --protocol tcp \\\n  --port 22 \\\n  --cidr 203.0.113.10\/32\n\naws ec2 describe-security-groups --group-ids sg-0123456789abcdef0<\/code><\/span><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Launch and connect to an instance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">At launch time, specify the <strong>key pair name<\/strong>. At connection time, provide the matching private key file to SSH.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css language-bash\"><span class=\"hljs-selector-tag\">aws<\/span> <span class=\"hljs-selector-tag\">ec2<\/span> <span class=\"hljs-selector-tag\">run-instances<\/span> \\\n  <span class=\"hljs-selector-tag\">--image-id<\/span> <span class=\"hljs-selector-tag\">ami-0123456789abcdef0<\/span> \\\n  <span class=\"hljs-selector-tag\">--instance-type<\/span> <span class=\"hljs-selector-tag\">t3<\/span><span class=\"hljs-selector-class\">.micro<\/span> \\\n  <span class=\"hljs-selector-tag\">--key-name<\/span> <span class=\"hljs-selector-tag\">demo-key<\/span> \\\n  <span class=\"hljs-selector-tag\">--security-group-ids<\/span> <span class=\"hljs-selector-tag\">sg-0123456789abcdef0<\/span> \\\n  <span class=\"hljs-selector-tag\">--subnet-id<\/span> <span class=\"hljs-selector-tag\">subnet-0123456789abcdef0<\/span>\n\n<span class=\"hljs-selector-tag\">ssh<\/span> <span class=\"hljs-selector-tag\">-i<\/span> <span class=\"hljs-selector-tag\">demo-key<\/span><span class=\"hljs-selector-class\">.pem<\/span> <span class=\"hljs-selector-tag\">ec2-user<\/span><span class=\"hljs-keyword\">@EC2_PUBLIC_DNS<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If login fails, verify all four elements together: instance key pair name, private key file, security group rules, and OS username (for example, many images use <strong>ec2-user<\/strong> or <strong>ubuntu<\/strong>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Choose an access pattern<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Access method<\/th><th>Best for<\/th><th>Inbound port 22 needed<\/th><th>EC2 key pair needed<\/th><\/tr><\/thead><tbody><tr><td>Direct SSH to instance<\/td><td>Simple admin access and legacy tooling<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>AWS Systems Manager Session Manager<\/td><td>Managed shell access without open SSH port<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>SSH through bastion host<\/td><td>Controlled access in private networks<\/td><td>Yes<\/td><td>Usually yes<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls and best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Do not confuse AWS API credentials with EC2 SSH key pairs; they solve different problems.<\/li><li>Download private key material once, then store it securely and set restrictive file permissions.<\/li><li>Avoid opening SSH to 0.0.0.0\/0 unless there is a temporary, controlled need.<\/li><li>Confirm the instance is attached to the security group you edited.<\/li><li>Use reproducible commands or Infrastructure as Code instead of one-off manual fixes.<\/li><li>Where possible, prefer short-lived API credentials and centrally managed access patterns.<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>New EC2 users often confuse AWS API credentials with EC2 SSH key pairs, then get blocked by security group rules. This guide separates each concept and shows repeatable CLI and console steps to get instance access working safely.<\/p>\n","protected":false},"author":1,"featured_media":4124,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[5633],"tags":[3354,3411,162,3409,3408,3362,1868,3406,3412,1122,3407,1806,3404,3410,3405,213],"class_list":["post-194","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","tag-amazon-ec2","tag-amazon-ec2-key-pairs-tutorial","tag-aws","tag-aws-tools","tag-cloud-foundry","tag-cloud-tools","tag-ec2","tag-ec2-key-pairs","tag-ec2-key-pairs-and-security-group-configuration","tag-guide","tag-how-to-create-a-key-pair","tag-instruction","tag-key-pairs","tag-security-groups","tag-stumbling-blocks","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/194","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=194"}],"version-history":[{"count":3,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/194\/revisions"}],"predecessor-version":[{"id":79024,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/194\/revisions\/79024"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media\/4124"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=194"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=194"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}