EC2 — Cheat Sheet (DVA-C02)
Verified against AWS documentation on 2026-10-12 · ⚠️ = changed vs older material: learn the value shown here (older practice material may say otherwise; full list in notes/current-facts.md) · explanations and examples: notes/ec2-mastery-guide.md.
IAM roles, instance profiles and the credential chain
| Topic | Current facts | Memory hook |
|---|---|---|
| Instance profile | Wraps exactly 1 role (hard limit); 1 profile per instance; IAM console auto-creates a same-name profile; CLI/API: create-instance-profile + add-role-to-instance-profile; launch with the profile name/ARN |
Profile = envelope, role = letter |
| Trust policy | "Principal": {"Service": "ec2.amazonaws.com"}, "Action": "sts:AssumeRole"; wrong trust → AssumeRoleUnauthorizedAccess in IMDS |
EC2 must be trusted to wear the role |
| Attach / swap live | associate-iam-instance-profile (running or stopped, none attached); replace-iam-instance-profile-association (running, needs iip-assoc-… ID); no reboot |
Replace the envelope, not the letter |
| Role swapped inside profile | Up to 1 h before the instance sees it → avoid on live instances | |
| Credentials | IMDS /latest/meta-data/iam/security-credentials/<role>; temporary ASIA… key + token; new set ≥ 5 min before expiry; SDKs auto-refresh; ignores role MaxSessionDuration |
Keys rotate themselves |
| Who am I? | aws sts get-caller-identity (no permission needed, even with explicit deny) → assumed-role/<role>/<instance-id> |
Always allowed, run it first |
| Decode errors | aws sts decode-authorization-message --encoded-message … needs sts:DecodeAuthorizationMessage |
|
| Launch with a role | ec2:RunInstances (+ Associate/ReplaceIamInstanceProfileAssociation) + iam:PassRole on the role ARN; console + iam:ListInstanceProfiles; no PassRole → UnauthorizedOperation (decode the message); empty profile → launch fails too (AccessDenied in the IAM docs) |
No pass, no launch |
| Consistency | IAM is eventually consistent; keep IAM changes out of hot paths | |
| Credentials outside AWS | ⚠️ Temporary credentials: aws login (AWS CLI 2.32.0+, 15-minute credentials refreshed for up to 12 h), IAM Identity Center (aws configure sso), IAM Roles Anywhere for servers; long-term keys only where roles are impossible |
Borrow credentials, don't own them |
| Tier | AWS CLI v2 (⚠️ 10 sources, first match wins) | SDK for Java 2.x ⚠️ | Memory hook |
|---|---|---|---|
| Explicit | 1 command-line options (--profile, --region) |
Java system properties aws.accessKeyId / aws.secretAccessKey / aws.sessionToken |
Closest to the code wins |
| Environment | 2 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
Same env vars | Env beats files |
| Federated profiles | 3 assume-role profile → 4 web identity → 5 IAM Identity Center | Web identity token (AWS_WEB_IDENTITY_TOKEN_FILE + AWS_ROLE_ARN) |
|
| Files | 6 ~/.aws/credentials → 7 credential_process → 8 ~/.aws/config |
Shared credentials/config files |
|
| Container | 9 ECS task role (169.254.170.2) |
ECS container credentials | Task before host (still block IMDS for tasks) |
| Instance | 10 EC2 instance profile via IMDS | EC2 instance profile | Instance role = last resort |
IMDSv2
| Item | Current value | Memory hook |
|---|---|---|
| Endpoint | 169.254.169.254; IPv6 [fd00:ec2::254] (Nitro + IPv6 subnet, opt-in); free |
Link-local = inside the instance only |
| Get token | PUT /latest/api/token + X-aws-ec2-metadata-token-ttl-seconds: 1–21600 (max 6 h) |
PUT to get… |
| Use token | GET + X-aws-ec2-metadata-token: <token>; token bound to the instance |
…GET to use |
HttpTokens |
optional (v1 + v2) / required (v2 only); ⚠️ required by default with AL2023 AMIs and console Quick Start launches; nothing set at launch, account or AMI level → optional (no instance-type default in current docs) |
Check MetadataOptions, don't assume |
| Hop limit | HttpPutResponseHopLimit 1–64; default 1; ⚠️ 2 with ImdsSupport v2.0 AMIs (AL2023); use 2 on container hosts |
1 = host only, 2 = containers |
| Status codes | 401 missing/expired token (v2 required) · 403 IMDS off / not allowed · 400 bad PUT · 404 no item · 503 retry | 401 = token, 403 = turned off |
| Refused | PUT with X-Forwarded-For; tokens from another instance |
Anti-SSRF / anti-proxy |
Metrics (AWS/EC2) |
MetadataNoToken = v1 calls while optional; MetadataNoTokenRejected = v1 calls after required |
NoToken before, Rejected after |
| Throttling | IMDS queries throttled per instance; link-local traffic capped at 1,024 packets/s per network interface; cache credentials, back off | |
| SDK minimums | CLI 1.16.289 · boto3 1.12.6 · Java 2.x 2.10.21 · JS v2 2.722.0; v1 fallback off with AWS_EC2_METADATA_V1_DISABLED=true |
| Where to require v2 | How | Note |
|---|---|---|
| Running instance | modify-instance-metadata-options --http-tokens required --http-endpoint enabled |
Immediate, no restart; check MetadataNoToken = 0 first |
| New instance / ASG | run-instances --metadata-options "HttpTokens=required,…" or launch template MetadataOptions |
Existing ASG instances unchanged → instance refresh |
| AMI | register-image / modify-image-attribute --imds-support v2.0 |
Irreversible; sets hop limit 2 |
| Account default, per Region | modify-instance-metadata-defaults --http-tokens required (⚠️ March 2024) |
New launches only; an explicit launch value still wins |
| Account enforcement, per Region | modify-instance-metadata-defaults --http-tokens-enforced enabled (⚠️ February 2026) |
Launch fails unless launch, account or AMI resolves required; --http-tokens optional on an instance → UnsupportedOperation; instances already allowing v1 untouched; restrict ec2:ModifyInstanceMetadataDefaults |
| Organization | Declarative policy for IMDS defaults | Accounts can't change it locally |
| IAM / SCP | ec2:MetadataHttpTokens, ec2:MetadataHttpPutResponseHopLimit, ec2:MetadataHttpEndpoint on RunInstances / ModifyInstanceMetadataOptions (scope denies to instance/*); deny ec2:RoleDelivery NumericLessThan 2.0 |
RoleDelivery = kill v1-fetched creds |
User data and bootstrapping
| Fact | Current value | Memory hook |
|---|---|---|
| When / as whom | First boot of each instance only, as root; cloud-init (Linux), EC2Launch v2 (Windows) | Once, as root |
| Formats | #!/bin/bash script, #cloud-config YAML, MIME multipart (both), #cloud-boothook (every boot, earliest) |
#! = script, #cloud-config = directives |
| Run every boot | Script in /var/lib/cloud/scripts/per-boot/ or scripts-user module frequency always (cloud_final_modules: [[scripts-user, always]]); Windows <persist>true</persist> / frequency: always |
Per-boot is opt-in |
| Size | 16 KB raw (before base64); encoded = ceil(n/3) × 4 | 16 raw |
| Who base64-encodes | You don't: console, CLI run-instances, boto3 run_instances. You do: raw API, modify-instance-attribute, launch template UserData, CloudFormation Fn::Base64 |
Double-encoding breaks it |
| Logs | /var/log/cloud-init-output.log (your script), /var/log/cloud-init.log; cloud-init status --long; aws ec2 get-console-output; Windows C:\ProgramData\Amazon\EC2Launch\log\agent.log |
output.log = your stdout |
| Modify | Stop (EBS root) → modify-instance-attribute (base64) → start; the new script does not run by itself |
Stop, swap, start, no rerun |
| Read | IMDS /latest/user-data (decoded), describe-instance-attribute (base64) |
Readable by anyone on the box → no secrets |
| Secrets | Secrets Manager GetSecretValue / Parameter Store SecureString via instance role (+ kms:Decrypt only when a customer managed key is used; the AWS managed key aws/secretsmanager already allows use through Secrets Manager in its key policy) |
Fetch, don't embed |
| AMI | User data not copied into AMIs; cloud-init copy in /var/lib/cloud/instances/<id>/ is → clean up first |
|
| AL2023 cloud-init | ⚠️ No launch-time security updates by default; an unreachable remote include fails cloud-init (fail closed); package manager DNF (dnf install -y) |
AL2023 fails closed |
| Approach | Best for | Trade-off |
|---|---|---|
| User data script | Light, per-launch config; fetching config/secrets | Adds boot time; versions drift from a changing AMI (latest-AMI SSM parameter), --releasever=latest or unpinned pip/npm/third-party sources (⚠️ AL2023 locks dnf repo installs to the AMI's release) |
| Golden AMI (EC2 Image Builder) | Fast Auto Scaling scale-out, identical builds | Rebuild pipeline for patches; Image Builder free (pay for resources used) |
| SSM Run Command / State Manager | Post-launch changes, drift correction, no SSH | SSM Agent + AmazonSSMManagedInstanceCore (or Default Host Management, IMDSv2 only) |
cfn-init + cfn-signal + CreationPolicy |
Stack waits until the app is ready; rollback on failure | Count 1 / Timeout PT5M by default, max 12 h |
Security groups and connectivity
| Aspect | Security group | Network ACL (awareness) |
|---|---|---|
| Attached to | ENI (instance, ALB, RDS, Lambda in VPC, endpoints) | Subnet (one NACL per subnet) |
| State | Stateful: replies allowed automatically | Stateless: allow return traffic on ephemeral ports 1024–65535 |
| Rules | Allow only; source = CIDR, prefix list or SG ID | Allow and deny; numbered 1–32766, lowest first, first match wins |
| Defaults | New SG: no inbound, all outbound; VPC default SG: inbound from itself | Default NACL: allow all; new custom NACL: deny all |
| Several attached | Rules unioned (5 SGs per ENI, max 16) | Only one per subnet |
| Change effect | Immediate, no restart (tracked connections drain naturally) | Immediate, cuts existing flows |
| Quotas | 60 inbound + 60 outbound rules per SG; 2,500 SGs per Region | 20 rules per direction (max 40) |
| Scope | Region; ⚠️ can be associated with other VPCs in the same Region and shared within an AWS Organization | VPC |
| Port | Service | Memory hook |
|---|---|---|
| 21 | FTP | Turned 21 and still talks in plain text |
| 22 | SSH, SFTP, EC2 Instance Connect | SSH has two S's → 22 |
| 80 / 443 | HTTP / HTTPS (443 also for SSM Agent and AWS APIs) | 80 = postcard, 443 = sealed envelope |
| 3389 | RDP (Windows) | 3389 = desktop; 3306 = data |
| 3306 | MySQL, MariaDB, Aurora MySQL | The "My" first database port |
| 5432 | PostgreSQL, Aurora PostgreSQL | Countdown 5-4-3-2 → Postgres |
| 1433 / 1521 | SQL Server / Oracle | Alphabetical: Microsoft (14) before Oracle (15) |
| 6379 / 11211 | Redis OSS–Valkey / Memcached (ElastiCache) | 6379 spells MERZ on a phone keypad; Memcached is all 1s and 2s |
| 2049 | NFS (Amazon EFS) | A file system from the year 2049 |
| You see / you need | It means / use | Check or configure | Memory hook |
|---|---|---|---|
| Timeout, request hangs | Network path dropped it | SG inbound rule, NACL, route to IGW/NAT, public IP | Timeout = Tunnel blocked |
| Connection refused (instant) | Host reached, nothing listening | Process up? Right port? Bound to 0.0.0.0? | Refused = Reached, nobody home |
Permission denied (publickey) |
SSH auth, not network | Username (ec2-user/ubuntu), key, chmod 400 |
Lock works, wrong key |
| HTTP 403 from site/ALB | HTTP layer refused (app, AWS WAF, S3 policy) | App auth, WAF rule, resource policy | Front desk said no |
AccessDenied / UnauthorizedOperation (403) |
IAM denied the API call | sts get-caller-identity, sts decode-authorization-message |
Denied = Doorman (IAM) |
Unable to locate credentials |
Empty credential chain | Instance profile? IMDS enabled? Container hop limit | No badge at all |
Connect timeout on endpoint URL (hangs) |
Packets to the AWS API dropped | NAT gateway or VPC endpoint? SG/NACL egress 443? Endpoint SG inbound 443? Private DNS on the endpoint? | Road to AWS missing |
Could not connect to the endpoint URL (fast) |
Name resolution or connection setup failed | Region or --endpoint-url spelling, VPC DNS settings, local endpoint (DynamoDB Local) running? |
Wrong address on the envelope |
| Shell, no inbound ports, audited | Session Manager | SSM Agent + AmazonSSMManagedInstanceCore (or Default Host Management Configuration) + outbound 443 to ssm/ssmmessages/ec2messages (NAT or VPC endpoints); CloudTrail + S3/CW Logs |
Agent dials out |
| Browser SSH with IAM control | EC2 Instance Connect | 22 from the EIC prefix list (com.amazonaws.<region>.ec2-instance-connect; IPv6: com.amazonaws.<region>.ipv6.ec2-instance-connect); key lives 60 s; public IPv4 or IPv6 (⚠️ since Sep 2024) address |
Still SSH, still 22 |
| SSH/RDP to private instance, no bastion | EIC Endpoint ⚠️ | 22/3389 from endpoint SG; IAM OpenTunnel; ≤ 1 h per connection, 20 concurrent, 1 per VPC and per subnet, 5 per Region |
Private tunnel door |
| IP must survive stop/start | Elastic IP (5 per Region) | Public IPv4 changes on stop/start, not on reboot; ⚠️ every public IPv4 incl. EIPs $0.005/h since 1 Feb 2024 | Pay for every public IPv4 |
| DB reachable only from app tier | SG reference | DB SG inbound 3306/5432 with source = app SG ID (same VPC or same-Region peer; inter-Region peer needs its CIDR) | Trust the badge, not the address |
Purchasing options and Spot
| Option | Commitment | Max discount vs On-Demand | Capacity guaranteed? | Pick it when | Memory hook |
|---|---|---|---|---|---|
| On-Demand | None; ⚠️ per second (60 s min) for Linux, Windows, RHEL, Ubuntu Pro; SLES hourly | 0% | No | Short, spiky, unpredictable, must not be interrupted | Walk-in price |
| Standard RI | 1 or 3 yr, fixed configuration | 72% | Only if zonal | Steady, known instance type; resellable on RI Marketplace | Lease one specific car |
| Convertible RI | 1 or 3 yr, exchangeable (same Region only) | 66% | Only if zonal | Steady, but family/OS may change; cannot be sold | Lease with trade-in |
| Compute Savings Plan | 1 or 3 yr, $/hour; no cancel (⚠️ any SP: return only if ≤ $100/h, within 7 days, same month) | 66% | No | Steady spend across families, Regions, Fargate, Lambda | Commit dollars, not machines |
| EC2 Instance Savings Plan | 1 or 3 yr, $/hour, one family + Region | 72% | No | Steady usage in one family in one Region | Dollars, but one family |
| SageMaker AI / Database Savings Plans ⚠️ | 1 or 3 yr, $/hour | 64% / 35% | No | Steady SageMaker AI usage / steady spend across RDS, Aurora, DynamoDB, ElastiCache | Four Savings Plans types now |
| Spot | None; reclaimable with 2-min notice | 90% | No | Fault-tolerant batch, CI, rendering, containers; LOWEST cost | Standby ticket |
| Dedicated Host | On-Demand or reservation (≤ 70% off) | — | Whole host | BYOL per socket/core/VM, host affinity, compliance | Rent the whole building |
| Dedicated Instance | Per instance + $2/h per Region | — | No | Single-tenant hardware without host control | Own room, landlord picks the building |
| On-Demand Capacity Reservation | None (⚠️ future-dated: requested 5–120 days ahead, commitment ≥ 14 days, ≥ 32 vCPUs) | 0% alone; add SP or regional RI | Yes, one AZ | Event or DR capacity in a specific AZ | Paid table booking, even if you don't show |
| Capacity Blocks for ML | Future window ≤ 8 weeks ahead; 1–14 d or up to 182 d; prepaid, no cancel | — | Yes (GPU/Trainium, ≤ 64 instances) | Short ML training runs | Book the GPU studio |
| Spot item | Where / value | Remember |
|---|---|---|
| Interruption notice | IMDS latest/meta-data/spot/instance-action (404 until set); EventBridge EC2 Spot Instance Interruption Warning |
2 minutes before stop/terminate; poll every 5 s; hibernate starts immediately |
| Rebalance recommendation | IMDS latest/meta-data/events/recommendations/rebalance; EventBridge EC2 Instance Rebalance Recommendation |
Earlier, best effort; may coincide with the notice |
| Interruption behaviour | terminate (default), stop, hibernate |
stop/hibernate need persistent request or maintain fleet |
| Max price | Default = On-Demand price | Leave unset; you always pay the current Spot price; ⚠️ no bidding |
| Request type | One-time (default) or persistent | Persistent: cancel request first, then terminate instances |
| Allocation strategy | ⚠️ price-capacity-optimized recommended |
lowest-price = API default (CLI/API only in ASGs) but most interruptions |
| Launch API | ASG or EC2 Fleet (instant/request/maintain, default maintain) |
⚠️ RequestSpotInstances / Spot Fleet = legacy; Spot blocks deprecated |
| Billing | EC2 interrupts in first hour = free | You stop/terminate = pay per second |
| Protection | Termination/stop protection not allowed on Spot | Design for interruption |
| ASG mixed instances setting | Default | Meaning |
|---|---|---|
OnDemandBaseCapacity |
0 | First N units always On-Demand |
OnDemandPercentageAboveBaseCapacity |
100 | On-Demand % above the base; 100 = no Spot at all |
SpotAllocationStrategy |
lowest-price (API); console preselects price-capacity-optimized |
Set price-capacity-optimized |
OnDemandAllocationStrategy |
prioritized (manual types) / lowest-price (attribute-based) |
Prioritized = override order (e.g. to hit RIs) |
SpotInstancePools |
2 (range 1–20) | Only with lowest-price |
SpotMaxPrice |
Unset (= On-Demand price) | Setting it increases interruptions |
CapacityRebalance |
Must be enabled (--capacity-rebalance) |
Replace at-risk Spot first; may exceed max by ≤ 10% of desired |
EBS volumes, snapshots, encryption and AMIs
| Type | Size | Max IOPS (ratio) | Max throughput | Boot | Multi-Attach | Pick when | Memory hook |
|---|---|---|---|---|---|---|---|
| gp3 | 1 GiB–64 TiB ⚠️ | 80,000 ⚠️ (≤ 500 per GiB); 3,000 included | 2,000 MiB/s ⚠️ (≤ 0.25 per IOPS); 125 included | ✓ | ✗ | Default for boot, apps and most DBs | "3k + 125 free at any size; 80k / 2k / 64T max" |
| gp2 | 1 GiB–16 TiB | 16,000 (3 per GiB, min 100; < 1 TiB bursts to 3,000) | 250 MiB/s | ✓ | ✗ | Legacy; migrate to gp3 (~20% cheaper per GiB) | "3 per GiB, capped at 5,334 GiB" |
| io2 Block Express | 4 GiB–64 TiB | 256,000 (1,000 per GiB) | 4,000 MiB/s | ✓ | ✓ | > 80k IOPS, > 2,000 MiB/s, sub-ms latency, 99.999%, shared block | "1000:1, five nines" |
| io1 | 4 GiB–16 TiB | 64,000 Nitro / 32,000 other (50 per GiB) | 1,000 MiB/s | ✓ | ✓ (Linux) | Legacy Provisioned IOPS; prefer io2 | "50:1" |
| st1 | 125 GiB–16 TiB | 500 | 500 MiB/s | ✗ | ✗ | Big sequential: logs, ETL | "HDD can't boot" |
| sc1 | 125 GiB–16 TiB | 250 | 250 MiB/s | ✗ | ✗ | Coldest, cheapest | "sc = sleepy & cheap" |
| Feature | High-yield facts | CLI | Memory hook |
|---|---|---|---|
| Scope | Volume = 1 AZ; snapshot and AMI = 1 Region | create-volume --availability-zone |
"Volume lives in a zone, images live in a Region" |
| Multi-Attach | io1/io2, ≤ 16 Nitro instances, same AZ, cluster-aware FS, not boot | create-volume --multi-attach-enabled |
"16 in one zone" |
DeleteOnTermination |
Root at launch = true; attached later = false; launch-mapping data volumes: console No, CLI Yes | modify-instance-attribute --block-device-mappings |
"Root dies with the instance" |
| Elastic Volumes | Size up only; modifying → optimizing → completed; ⚠️ up to 4 changes per rolling 24 h (2026) |
modify-volume, describe-volumes-modifications |
"Grow the disk, then grow the FS" |
| Snapshots | Incremental, async, stored in S3; copy in destination Region; first cross-Region copy full; ≤ 20 concurrent copies per destination | create-snapshot, copy-snapshot --source-region |
"Copy from the place you're going" |
| Snapshots Archive | Up to 75% cheaper; full snapshot; 90-day minimum; restore ≤ 72 h (temporary 1–180 days) | modify-snapshot-tier, restore-snapshot-tier |
"75 off, 90 in, 72 out" |
| Recycle Bin | Snapshots and AMIs 1–365 days; ⚠️ volumes 1–7 days (2025); rule must exist before deletion | aws rbin create-rule |
"Bin before you delete" |
| Fast Snapshot Restore | Fully initialized volumes; per snapshot per AZ; ≤ 5 per Region; billed per AZ-minute (1 h minimum) | enable-fast-snapshot-restores |
"Pre-warmed, paid per AZ" |
| Volume initialization rate | ⚠️ 100–300 MiB/s, predictable warm-up (2025) | create-volume --volume-initialization-rate |
|
| Time-based copy | 15 min–48 h target, 15-min steps, extra charge; for an AMI the target applies to each of its snapshots | copy-snapshot --completion-duration-minutes; copy-image --snapshot-copy-completion-duration-minutes |
|
| Default volume type | Console: gp3; CreateVolume and CopyVolumes API/CLI: gp2 | --volume-type gp3 |
"The API says gp2 unless told" |
| Volume copy | ⚠️ Same AZ, instant, crash-consistent, can encrypt/re-key (2025); background initialization at the lowest of 3,000 IOPS / 125 MiB/s and both volumes' provisioned levels; 1 copy per source at a time, ≤ 5 in progress per Region | copy-volumes --volume-type gp3 |
"Copy in place, name the type" |
| Volume sharing | ⚠️ AWS RAM share; the other account can only view and copy (same AZ), never attach; unencrypted or customer managed key only | aws ram create-resource-share |
"Share to copy, not to mount" |
| Root volume replacement | Running instance, automatic reboot, same ID and IPs; from launch state, snapshot, AMI or ⚠️ an existing detached same-AZ volume (2026); that swap doesn't inherit DeleteOnTermination; no encrypted → unencrypted |
create-replace-root-volume-task --volume-id |
"Swap the root, keep the ID" |
| Data Lifecycle Manager | Tag-based schedules, retention, cross-Region/account copy, FSR, archive, AMI deprecate/deregister; no extra cost | aws dlm create-lifecycle-policy |
"Set-and-forget backups" |
| Scenario | Do this | Memory hook |
|---|---|---|
| Encrypt an existing unencrypted volume | Snapshot → create-volume --encrypted --volume-type gp3 (or copy-snapshot --encrypted) → swap; ⚠️ same-AZ shortcut copy-volumes --encrypted --volume-type gp3 (otherwise gp2) |
"No in-place encryption" |
| Change the KMS key | Copy the snapshot/AMI with a new --kms-key-id (full copy) |
"New key = new copy" |
| Share an encrypted snapshot/AMI cross-account | Customer managed key + key policy (and grants) + createVolumePermission / --launch-permission; impossible with aws/ebs |
"aws/ebs never leaves the account" |
| Copy an encrypted AMI/snapshot to another Region | Run in the destination Region; key must exist there | "Keys are Regional" |
| ASG launch with a customer managed key | Key policy for AWSServiceRoleForAutoScaling; cross-account: unconditioned kms:CreateGrant for the ASG account, which runs aws kms create-grant for its role |
"Client.InternalError on launch = KMS" |
| Encrypt every new volume | enable-ebs-encryption-by-default per Region (+ modify-ebs-default-kms-key-id) |
"New only, never retroactive" |
| Latest Amazon Linux AMI | resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64; CFN AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>; ⚠️ AL2 ended 2026-06-30 |
"Resolve, don't hard-code" |
| Retire an AMI | Deprecate (still launchable by ID) → disable → deregister-image --delete-associated-snapshots ⚠️ (default keeps snapshots) |
"Hide, block, delete" |
| Faster scale-out | Golden AMI (built by EC2 Image Builder) instead of long user data | "Bake, don't fry" |
Storage choice: instance store vs EBS vs EFS
| Instance store | EBS | EFS | |
|---|---|---|---|
| What | Local NVMe/SSD/HDD on the host | Network block volume | Managed NFS v4.0/4.1 file system |
| Sharing | One instance | One instance (io1/io2 Multi-Attach ≤ 16 Nitro, same AZ) | Thousands of clients: EC2, ECS, EKS, Fargate, Lambda, on-prem |
| Scope | That instance | One AZ | Regional (≥ 3 AZs) or One Zone |
| Attach | Launch only; NVMe types attached automatically, old non-NVMe types (C3, M3, R3, D2, H1, I2, X1…) need ephemeralN mappings |
Any time, same AZ | Mount through the mount target in each AZ |
| Persistence | Survives reboot; lost on stop (also an OS shutdown), hibernate, terminate, type change, retirement, recovery, disk failure | Independent of the instance | Independent of the instance |
| Performance | Highest (e.g. i3.16xlarge ≈ 3.3 M random-read IOPS) |
Up to 256,000 IOPS per volume (io2 Block Express) | Elastic: 20–60 GiB/s read, 1–5 GiB/s write per FS ⚠️ |
| Backup | Do it yourself (copy to S3/EBS/EFS); no snapshots, not in AMIs | Snapshots, DLM, AWS Backup | AWS Backup, replication |
| OS | Any | Any | Linux only (Windows → FSx for Windows) |
| Price | Included in the instance | Provisioned GiB (+ IOPS/throughput) | Stored GB by class (+ throughput mode) |
| Memory hook | "Reboot-safe, stop-sorry" | "One AZ, one owner" | "NFS 2049, one mount target per AZ" |
| EFS setting | Options | Current guidance | Memory hook |
|---|---|---|---|
| File system type | Regional / One Zone | Regional for production (⚠️ older material calls it "Standard"); One Zone is cheaper, backups on by default, no Max I/O | "One Zone = one basket" |
| Performance mode (fixed at creation) | General Purpose / Max I/O | General Purpose for everything ⚠️ Max I/O is previous-gen, not with Elastic or One Zone | "GP forever" |
| Throughput mode (switchable) | Elastic / Provisioned / Bursting | Elastic for spiky or unknown; Provisioned for steady known (24 h lock after any change: no switching away, no decrease); Bursting = 50 MiB/s per TiB, burst 100 MiB/s per TiB | "Elastic = pay per byte moved" |
| Defaults trap | Console: Elastic + GP + encrypted | API/CLI: bursting, unencrypted unless flags are set ⚠️ |
"Console is kind, CLI is literal" |
| Storage classes | Standard / Infrequent Access / Archive | Lifecycle (console default): IA after 30 days, Archive after 90; Archive needs Elastic, 90-day minimum; 128 KiB minimum billed per file | "S → IA → A" |
| Encryption | At rest (creation only) / in transit (-o tls) |
Enforce with elasticfilesystem:Encrypted and a aws:SecureTransport deny |
"At birth or never" |
| Access control | SG TCP 2049, IAM (-o tls,iam), access points |
ClientMount / ClientWrite / ClientRootAccess; no policy = open to anyone who reaches a mount target |
|
| Mount errors | Hang + Connection timed out / Failed to resolve server / access denied by server |
Timeout = SG (or NACL) blocks 2049, or wrong IP; resolve failure = no mount target in the AZ, other VPC, VPC DNS off, or target < 90 s old; denied = IAM or file system policy | "Hang = port, name = target, denied = policy" |
| Lambda | Access point + VPC + /mnt/... path |
Execution role needs ClientMount (+ ClientWrite) and VPC permissions |
"Lambda enters through the access point" |
| Per-client throughput | 1,500 MiB/s with efs-utils ≥ 2.0 (Elastic), else 500 MiB/s | Keep amazon-efs-utils current |
| If you need | Choose | Memory hook |
|---|---|---|
| Shared POSIX files across AZs (CMS, uploads folder used as files, home dirs) | EFS Regional + mount target per AZ + SG 2049 | "Many readers, many AZs = EFS" |
| Highest-IOPS scratch or cache that can be lost | Instance store | "Fast and forgetful" |
| Persistent single-instance DB or boot volume | EBS gp3 (io2 Block Express above 80k IOPS) | "One server, one volume" |
| Shared block device, cluster-aware app, one AZ | io2 Multi-Attach | "16 in one zone" |
| Windows SMB / Active Directory share | FSx for Windows File Server | "Windows talks SMB" |
| HPC/ML parallel FS over S3 data | FSx for Lustre | "Lustre for lots of cores" |
| Objects (incl. user uploads via the SDK or presigned URLs), static assets, backups, data lake | S3 | "Keys, not paths" |
Elastic Load Balancing: ALB, NLB, GWLB
| Type | Layer and protocols | Targets | Signature features | Memory hook |
|---|---|---|---|---|
| ALB | L7: HTTP, HTTPS, HTTP/2, WebSockets, gRPC | instance, ip (private), lambda (1 fn/TG, 1 MB req/resp) |
Rules on host, path, header, method, query string, source IP; ⚠️ regex (RegexValues) on host-header, path-pattern, http-header; redirect, fixed response, Cognito/OIDC auth, JWT validation; weighted TGs (≤ 5, weight 0–999); X-Forwarded-For/-Proto/-Port; no Elastic IPs (⚠️ public IPv4 can come from an IPAM pool: allow-list a CIDR, not fixed per-AZ IPs) |
Reads the letter |
| NLB | ⚠️ L4: TCP, UDP, TLS, TCP_UDP, QUIC, TCP_QUIC | instance, ip, alb (no Lambda) |
1 static IP per AZ + optional Elastic IP; millions of req/s; client IP preserved (instance targets); ⚠️ SGs only if set at creation, and QUIC/TCP_QUIC need an ipv4 NLB with no SGs (QUIC: draining fixed 300 s, no Proxy Protocol v2); ⚠️ TCP idle 350 s (60–6000) |
Reads only the envelope |
| GWLB | L3: all IP packets, all ports | instance, ip (appliances) |
Transparent bump-in-the-wire via GWLB endpoint + route tables; GENEVE port 6081; 5-tuple flow stickiness; MTU 8,500 | X-rays the parcel |
| CLB | L4/L7: TCP, SSL, HTTP, HTTPS | Instances (no TGs) | Legacy; 1 cert per listener (no SNI); connection draining 1–3600 s | Retired grandpa |
| Code or metric | Generated by | Usual cause | Memory hook |
|---|---|---|---|
| 403 | ALB | AWS WAF blocked the request | Bouncer |
| 460 | ALB | Client closed the connection before the LB answered | Client gave up |
| 463 | ALB | X-Forwarded-For with more than 30 IPs |
Too many hops |
| 500 | ALB | AWS WAF execution error; IdP token/user-info or JWKS endpoint unreachable or slower than 5 s; IdP claims over 11 KB | Helper broke |
| 502 | ALB | Target reset/closed the connection (keep-alive < idle timeout), malformed response, target TLS error, Lambda error/timeout/invalid or > 1 MB response | Bad answer |
| 503 | ALB | No registered (or usable) targets in the target group | Nobody home |
| 504 | ALB | No connection within 10 s (SG/NACL) or no response before the idle timeout | No answer in time |
HTTPCode_ELB_5XX_Count vs HTTPCode_Target_5XX_Count |
LB vs your app | Separate the load balancer's errors from your code's errors | ELB = infra, Target = code |
TargetResponseTime / RequestCountPerTarget |
ALB | Target latency (Avg/pNN) / requests per target (statistic Sum) | |
| Access logs | ALB → S3 | Off by default; every 5 min; same-Region bucket, SSE-S3; ⚠️ bucket-policy principal logdelivery.elasticloadbalancing.amazonaws.com (not a Regional ELB account ID); also CloudWatch vended logs |
Opt-in diary |
| Setting | Default | Range / note |
|---|---|---|
| ALB health check | HTTP /, 30 s interval, 5 s timeout, 5 healthy / 2 unhealthy, code 200 |
Interval 5–300 s, timeout 2–120 s, codes 200–499; healthy threshold = unhealthy → healthy, a new target needs only 1 passing check; all unhealthy → fail open |
| NLB health check | TCP, 30 s, 10 s (HTTP 6 s), 5 / 2, HTTP codes 200-399 |
HTTP/HTTPS optional |
| ALB idle timeout | 60 s | 1–4000 s |
| Deregistration delay | 300 s | 0–3600 s (0 = off); state draining for the full delay, then unused; ALB: upper bound for in-flight requests (none in flight, no active connections → deregistered at once, still shown draining); lower it for faster deployments; NLB QUIC fixed 300 s |
| Slow start | 0 (off) | 30–900 s; not with least outstanding requests / weighted random |
| Cross-zone | ALB on (free), NLB/GWLB off (charged when on), ⚠️ CLB off via API/CLI, on via console (free) | ALB can turn it off per target group |
| ALB to target connect timeout | 10 s | Exceeded → 504 |
Sticky sessions, TLS and SNI
| Stickiness | Mechanism | Cookie / key | Duration | Memory hook |
|---|---|---|---|---|
| ALB duration-based | Load balancer cookie (lb_cookie) |
AWSALB + AWSALBCORS (SameSite=None; Secure), both on every response; AWSALBCORS wins if both arrive |
1 s – 7 days, default 1 day | LB bakes the cookie |
| ALB application-based | Your cookie (name set per target group) + LB cookie (app_cookie); client must return both |
custom name + AWSALBAPP-0 (-1…-3 when over 4 KB, max 16 KB) |
1 s – 7 days, default 1 day | App bakes, LB stamps |
| ALB weighted target groups | Target-group stickiness on the forward action | AWSALBTG + AWSALBTGCORS |
1 s – 7 days | Sticks to blue or green |
| CLB | LB-generated or application-controlled (follows your cookie's lifetime) | AWSELB + AWSELBCORS |
optional expiry (none = browser session) | ELB = old name |
| NLB | Source IP hash (source_ip) |
none | n/a; not on TLS or QUIC listeners | No cookies at layer 4 |
| Reserved / limits | Prefixes AWSALB, AWSALBAPP, AWSALBTG are reserved; ALB stickiness needs cross-zone on; uneven load and lost sessions on failure |
Stateless + ElastiCache/DynamoDB beats sticky |
| TLS / ACM fact | Current value | Memory hook |
|---|---|---|
| ACM public certificate cost | Free with integrated services (ELB, CloudFront, API Gateway); ⚠️ exportable public certificates (since June 2025, key exported too) cost $7 per FQDN / $79 per wildcard at issuance and each renewal | Free if it stays in AWS |
| ACM validity / renewal | ⚠️ 198 days (was 395); DNS-validated certs auto-renew from 45 days before expiry; email needs approval; HTTP validation only via CloudFront distribution tenants; imported never. CA/B Forum max: 100 days from 15 Mar 2027, 47 days from 15 Mar 2029 | 198, renew at 45 |
| Region | Same Region as the ALB/NLB; CloudFront viewer certs in us-east-1 | CloudFront certs live in Virginia |
| Listener certificates | 1 default + certificate list for SNI; 25 per ALB excluding default; CLI/CloudFormation/CDK listeners start with an empty list (console adds the default); match but client-incompatible → handshake fails | Default = fallback only when no SNI or no match |
| Default security policy | ⚠️ Console: ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09; CLI/CloudFormation/CDK: ELBSecurityPolicy-2016-08 |
API default is old: set it explicitly |
Wildcard *.example.com |
One level only; not the apex | Add the apex as a SAN |
| Capability | ALB | NLB | CLB | CloudFront |
|---|---|---|---|---|
| SNI (many certificates on one listener) | Yes | Yes (TLS listener) | No | Yes |
| TLS passthrough to targets | No (always terminates, can re-encrypt) | Yes (TCP listener on 443) | Yes (TCP listener) | see CloudFront module |
| Validates target certificates | No (self-signed OK) | No (TLS target groups) | Optional back-end server authentication | Yes: origin cert from a trusted CA with a matching name, else 502 |
| Mutual TLS at the load balancer | Yes: passthrough (X-Amzn-Mtls-Clientcert) or verify (trust store from S3) |
No (use TCP passthrough) | No (use TCP passthrough) | see CloudFront module |
Auto Scaling
| Policy | Use when | Key facts | Memory hook |
|---|---|---|---|
| Target tracking | Keep a per-instance metric at a value (CPU 50%, 500 req/target) | ASG creates and owns the alarms; ASGAverageCPUUtilization, ASGAverageNetworkIn/Out, ALBRequestCountPerTarget (+ ResourceLabel); custom + metric math; high-res down to 10 s; out if any policy, in only if all |
Thermostat |
| Step | React harder to bigger breaches | You own the alarm; ChangeInCapacity / ExactCapacity / PercentChangeInCapacity; uses warmup, not cooldown |
Staircase |
| Simple | Legacy only | One adjustment, then waits for the cooldown (default 300 s) | Sleepy |
| Scheduled | Known calendar peaks | Cron + time zone (UTC default); sets min/max/desired; ≤ 125 per ASG | Calendar |
| Predictive | Recurring daily/weekly pattern, slow boot | ≥ 24 h data, up to 14 days analysed, 48 h hourly forecast, refreshed every 6 h; ForecastOnly first; scales out only |
Weather forecast |
| SQS workers | Queue-driven processing | Target tracking on backlog per instance = visible messages ÷ GroupInServiceInstances (⚠️ metric math, no custom metric needed; enable group metrics first or the alarms sit in INSUFFICIENT_DATA); target = acceptable latency ÷ processing time |
Messages per worker |
| Setting | Default | Notes |
|---|---|---|
| Health check type | EC2 (always on) | Add ELB, ⚠️ VPC_LATTICE, EBS explicitly; custom: set-instance-health |
| ⚠️ EC2 application status checks | Off (create in EC2) | HTTP/HTTPS port + path every 60 s (fixed), thresholds 2/2, timeout 6 s, matcher 200, init grace 300 s; associate by tag aws:autoscaling:groupName → impaired instances replaced even with type EC2; no redirects followed; $0.01 per managed ENI-hour per AZ |
| Health check grace period | 300 s console / 0 s CLI-SDK | Delays health replacement after InService |
| Default instance warmup | Not set | Recommended; used by target tracking, step, instance refresh |
| Default cooldown | 300 s | ⚠️ Gates simple scaling; may delay scale-in; never delays unhealthy-instance replacement |
| Lifecycle hook heartbeat | 3600 s (30–7200) | Global max 48 h or 100 × heartbeat; DefaultResult = ABANDON |
| Lifecycle notification targets | EventBridge (console) | SNS / SQS (role ARN, no FIFO) or ⚠️ Lambda (resource policy for AWSServiceRoleForAutoScaling); CLI, CloudFormation or SDK |
| ⚠️ Instance lifecycle policy | Off | Retain on TerminateHookAbandon → Terminating:Retained (billed, outside desired capacity); terminate with terminate-instance-in-auto-scaling-group |
| Instance refresh healthy % | Min 90% (0–100), Max 100% (100–200) | ⚠️ Min 100% = launch before terminate; max − min ≤ 100 |
| Refresh extras | Checkpoint delay 3600 s; bake time 0; ≤ 10 alarms; 14-day limit | Event per checkpoint except 100%; skip matching: CLI off / console on; auto rollback needs a desired configuration and a numbered LT version; listed alarms must be OK at start (INSUFFICIENT_DATA or ALARM → error) |
| Launch configurations | ⚠️ None for accounts created since 1 Oct 2024 | No new instance types since 1 Jan 2023; use launch templates |
| Group metrics | Off | Free, 1-minute; enable-metrics-collection |
| Symptom or need | Do this | Memory hook |
|---|---|---|
| ALB says unhealthy, ASG keeps the instance | --health-check-type ELB |
ASG trusts only EC2 by default |
| App hangs on a running instance, no load balancer in front | ⚠️ EC2 application status check on an unauthenticated 200 path, associated by tag aws:autoscaling:groupName |
EC2 check, now app-aware |
| New instances replaced in a loop | Grace period ≥ boot time or launch lifecycle hook; check /health and SGs; suspend ReplaceUnhealthy to debug |
Grace before judging |
| Roll out a new AMI / template version | Instance refresh (+ checkpoints, skip matching, auto rollback with alarms) | Refresh, don't rebuild |
| Run code before traffic / before termination | Pending:Wait / Terminating:Wait hook + complete-lifecycle-action |
Set up, clean up |
| Inspect a sick instance undisturbed | enter-standby (deregistered, no health checks, still billed) |
Bench the player |
| Fast scale-out for slow-booting apps | Warm pool (Stopped = pay only EBS volumes and Elastic IPs) |
Pre-heated oven |
| Spot instance at risk | Capacity Rebalancing + price-capacity-optimized |
Move before the eviction |
| Keep a busy worker on scale-in | Scale-in protection (not against health or Spot) | Shield only for scale-in |
CloudWatch for EC2
| Data | Source / namespace | Frequency | Memory hook |
|---|---|---|---|
CPUUtilization, NetworkIn/Out, EBS* (Nitro) |
EC2, AWS/EC2 |
5 min basic (free) / 1 min detailed (paid) | Seen from outside the guest |
DiskReadOps/WriteOps/ReadBytes/WriteBytes |
EC2, AWS/EC2 |
Same | Disk* = instance store only |
CPUCredit* (T instances), NetworkPackets* |
EC2, AWS/EC2 |
5 min only | |
StatusCheckFailed, _Instance, _System, _AttachedEBS |
EC2, AWS/EC2 |
1 min, free | 0 = pass, 1 = fail |
Memory, disk space, swap, processes (procstat), logs |
Unified CloudWatch agent, CWAgent + CloudWatch Logs |
60 s default; < 60 s = high-resolution | Inside the guest = agent + role |
| App metrics | PutMetricData (or agent StatsD :8125 / collectd) |
StorageResolution 60 (default) or 1 |
Your namespace, never AWS/ |
| Status check | Means | Fix | Memory hook |
|---|---|---|---|
| System | AWS host, power or network fault | Recover (alarm or simplified automatic recovery) or stop/start (new host, new public IP) | AWS's box → move it |
| Instance | Your OS/config: bad network/boot config, memory exhausted, corrupted FS, kernel | Reboot or fix the OS | Your box → reboot/fix |
| Attached EBS ⚠️ | EBS volumes unreachable or I/O stalled (Nitro) | Wait, replace volume, stop/start; ASG replaces | Storage link broken |
| Application (opt-in) ⚠️ | HTTP/HTTPS probe of path/port every 60 s fails | Fix app; ASG replaces impaired instances | Health page says no |
| Item | Value |
|---|---|
| Agent IAM | Instance role with CloudWatchAgentServerPolicy; Parameter Store config named AmazonCloudWatch-*; CloudWatchAgentAdminPolicy only to write configs |
| Logs agent | ⚠️ The old CloudWatch Logs agent (awslogs) is deprecated and fails on IMDSv2-only instances: use the unified CloudWatch agent |
| PutMetricData limits | ⚠️ ≤ 1,000 metrics and 1 MB per call; 150 values per array; 30 dimensions per metric; timestamps 2 weeks back to 2 hours ahead; 500 TPS |
| Retention | < 60 s: 3 h; 1 min: 15 days; 5 min: 63 days; 1 h: 455 days (15 months) |
| High-resolution read periods | 1, 5, 10, 30 s or multiples of 60 s |
| Alarm periods | ⚠️ 10, 20, 30 s (high-resolution, extra cost; only useful on StorageResolution 1 data) or multiples of 60 s |
| Monitoring change in launch template | New instances only; running ones keep their type until instance refresh or monitor-instances |
| Alarm logic | States OK / ALARM / INSUFFICIENT_DATA; M of N = DatapointsToAlarm of EvaluationPeriods; missing data default missing; ≤ 5 actions per state |
| Evaluation window | Period × EvaluationPeriods ≤ 1 day (periods < 1 h) or ≤ 7 days |
| EC2 actions | arn:aws:automate:<region>:ec2:<action> (stop, terminate, reboot, recover); recover only on StatusCheckFailed_System; recover 2 × 1 min, reboot 3 × 1 min |
| Recovery keeps | Instance ID, private/public/Elastic IPs, metadata, placement group, EBS volumes, AZ (loses RAM) |
| Simplified automatic recovery | ⚠️ On by default for supported types; not for metal, instance store, Dedicated Hosts, EFA, ASG members; modify-instance-maintenance-options --auto-recovery |
| Testing | set-alarm-state runs the new state's actions; composite alarms cannot do EC2/Auto Scaling actions |
| Free tier | 10 metrics (custom + detailed monitoring combined), 10 alarm metrics, 1 million API requests, 5 GB logs |
Numbers to know
| Item | Value | Note |
|---|---|---|
| Roles per instance profile | 1 (hard limit, not adjustable) | Also 1 role (1 profile) per instance at a time; one role can sit in many profiles |
| Delay after swapping the role inside an instance profile | Up to 1 hour | Replace the instance-profile association instead (or disassociate/associate, stop/start) |
| Instance role credential refresh | New set at least 5 min before expiry | SDKs/CLI refresh automatically; if you cache them yourself, refresh hourly or ≥ 15 min before Expiration |
| AWS CLI v2 credential sources | 10 ⚠️ | options → env → assume role → web identity → Identity Center → credentials file → credential_process → config file → container → instance profile |
aws login console credentials |
15-minute credentials, auto-refreshed up to 12 h ⚠️ | Needs AWS CLI 2.32.0+; the session also works in current SDKs |
| AWS SDK for Java 1.x end of support | 2025-12-31 | Use SDK for Java 2.x (system property aws.secretAccessKey) |
| IMDS endpoints | 169.254.169.254 / [fd00:ec2::254] |
IPv6 only on Nitro in IPv6-capable subnets, off by default |
| IMDSv2 token TTL | 1–21,600 s (max 6 h) | Header X-aws-ec2-metadata-token-ttl-seconds on PUT /latest/api/token |
HttpPutResponseHopLimit |
1–64; default 1 (2 with ImdsSupport v2.0 AMIs such as AL2023 ⚠️) |
Set 2 on container hosts when IMDSv2 is required |
| IMDS HTTP errors | 401 / 403 / 400 / 404 / 503 | 401 = missing or expired token with v2 required; 403 = IMDS off or not allowed; 400 bad PUT; 404 no such item; 503 retry |
| Link-local packet limit | 1,024 packets/s per network interface | Shared by IMDS, Route 53 Resolver DNS, NTP and Windows licensing; drops show in ENA linklocal_allowance_exceeded |
| AWS CLI IMDS timeout / attempts | 1 s / 1 attempt | AWS_METADATA_SERVICE_TIMEOUT, AWS_METADATA_SERVICE_NUM_ATTEMPTS |
| Minimum versions using IMDSv2 automatically | CLI 1.16.289; boto3 1.12.6; Java 2.x 2.10.21; JS v2 2.722.0 | All current SDKs support IMDSv2 |
| IMDSv2 released | 2019-11-19 | Session-oriented IMDS |
| Account-level IMDS defaults | Since 2024-03-25 ⚠️ | modify-instance-metadata-defaults, per Region, new launches only |
| Account-level IMDSv2 enforcement | Since 2026-02-24 ⚠️ | --http-tokens-enforced enabled: launches that allow IMDSv1 fail |
| User data size | 16 KB raw (before base64) | Encoded size = ceil(n/3) × 4 |
CloudFormation CreationPolicy ResourceSignal |
Count 1, Timeout PT5M (defaults); max 12 h |
cfn-signal -e $? reports success or failure |
| Public IPv4 address charge | $0.005 per IP-hour ⚠️ | Since 1 Feb 2024; in-use and idle addresses alike, including Elastic IPs on running instances; BYOIP addresses are not charged |
| Security groups per network interface | 5 (adjustable to 16) | Rules per SG × SGs per ENI must stay ≤ 1,000 |
| Rules per security group | 60 inbound + 60 outbound | Counted separately for IPv4 and IPv6; a rule that references an SG counts as one rule |
| VPC security groups per Region | 2,500 | Default quota, adjustable |
| Elastic IP addresses per Region | 5 | Default quota, adjustable |
| Network ACL rules | 20 per direction (max 40); rule numbers 1–32766 | Lowest number evaluated first, first match wins; default NACL allows all, new custom NACL denies all |
| Ephemeral port range to allow in NACLs | 1024–65535 | Stateless NACLs need explicit return-traffic rules; SGs do not |
| EC2 Instance Connect pushed key lifetime | 60 seconds | SendSSHPublicKey; still SSH on TCP 22 |
| EC2 Instance Connect Endpoint limits | 1 per VPC and per subnet, 5 per Region, 20 concurrent connections, 1 hour max per connection ⚠️ | No extra charge; cross-AZ data transfer applies |
| Classic ports | 22 SSH/SFTP, 21 FTP, 80 HTTP, 443 HTTPS, 3389 RDP | 443 is also the outbound port for SSM Agent and SDK calls to AWS APIs |
| Database, cache and NFS ports | 3306 MySQL/MariaDB/Aurora MySQL, 5432 PostgreSQL, 1433 SQL Server, 1521 Oracle, 6379 Redis OSS/Valkey, 11211 Memcached, 2049 NFS (EFS) | Allow on the DB, cache or EFS mount-target SG with source = app SG ID |
| Session Manager connectivity | Outbound TCP 443 to ssm, ssmmessages, ec2messages |
No inbound rules; via NAT/IGW or interface VPC endpoints whose SG allows inbound 443 from the instances |
| On-Demand billing granularity | Per second, 60-second minimum ⚠️ | Linux, Windows (incl. SQL Server editions), RHEL, Ubuntu Pro; SLES is billed per full hour |
| Reserved Instance max discount | Standard 72%, Convertible 66% | 1 or 3 years; All/Partial/No Upfront; only Standard can be sold on the RI Marketplace |
| Savings Plans max discount | Compute 66%, EC2 Instance 72%, SageMaker AI 64%, Database 35% ⚠️ | 1 or 3 years, $/hour commitment; no capacity reservation; never applies to Spot |
| Savings Plan return window | Within 7 days of purchase, same calendar month, hourly commitment ≤ $100 ⚠️ | Up to 10 returns per calendar year per management account; upfront refunded in full (ReturnSavingsPlan) |
| Spot max discount | Up to 90% vs On-Demand | No bidding ⚠️; default max price = On-Demand price; AWS recommends not setting one |
| Spot interruption notice | 2 minutes | IMDS spot/instance-action + EventBridge warning; poll every 5 s; no 2-minute grace with the hibernate behaviour |
| Dedicated Host Reservation discount | Up to 70% | Versus On-Demand Dedicated Host pricing |
| Dedicated Instances regional fee | $2 per hour per Region | Charged in each Region where at least one Dedicated Instance runs; not discounted by Savings Plans |
| Future-dated Capacity Reservations | ≥ 32 vCPUs per request; requested 5–120 days ahead; commitment ≥ 14 days (12 weeks for U and X) ⚠️ | Cancellation charge may apply; immediate-use ODCRs have no commitment |
| Capacity Blocks for ML | Up to 8 weeks ahead; 1–14 days (1-day steps) or up to 182 days (7-day steps); ≤ 64 instances per block, 256 across blocks | Prepaid, cannot be cancelled |
ASG OnDemandBaseCapacity default |
0 | First N capacity units are always On-Demand |
ASG OnDemandPercentageAboveBaseCapacity default |
100 | 100 means no Spot at all until lowered |
ASG SpotInstancePools default |
2 (range 1–20) | Only used with the lowest-price strategy |
| ASG Capacity Rebalancing overshoot | Up to 10% of desired capacity above max size | Temporary, while replacements launch before at-risk Spot Instances terminate |
| Spot diversification best practice | ≥ 10 instance types | Plus all AZs; Spot placement score 1–10 |
| gp3 included baseline | 3,000 IOPS + 125 MiB/s at any size | No burst credits; ~20% cheaper per GiB than gp2 |
| gp3 maximums | 80,000 IOPS / 2,000 MiB/s / 64 TiB ⚠️ | Since 2025-09-26; older material: 16,000 IOPS / 1,000 MiB/s / 16 TiB |
| gp3 provisioning ratios | 500 IOPS per GiB; 0.25 MiB/s per provisioned IOPS | Full 80,000 IOPS from 160 GiB; 2,000 MiB/s needs ≥ 8,000 IOPS |
| gp2 IOPS | 3 IOPS/GiB, min 100, max 16,000 (at 5,334 GiB) | Volumes < 1 TiB burst to 3,000 IOPS (5.4 M credit bucket, BurstBalance) |
| gp2 throughput | 128–250 MiB/s | 250 MiB/s from 334 GiB |
| io2 Block Express | 256,000 IOPS, 4,000 MiB/s, 4 GiB–64 TiB, 1,000 IOPS/GiB, 99.999% ⚠️ | All io2 volumes are Block Express since 2025-04-30; non-Nitro instances reach 32,000 IOPS |
| io1 | 64,000 IOPS (Nitro) / 32,000 (other), 50 IOPS/GiB, 1,000 MiB/s, 4 GiB–16 TiB | AWS recommends io2 instead |
| st1 / sc1 | 500 / 250 MiB/s; 500 / 250 IOPS; 125 GiB–16 TiB | HDD types cannot be boot volumes |
| EBS Multi-Attach | Up to 16 Nitro instances, same AZ | io1/io2 only; cluster-aware file system; not boot; Windows: io2 only |
| Elastic Volumes frequency | Up to 4 modifications per volume per rolling 24 h ⚠️ | Since 2026-01-15; each one after the previous completes (older material: wait ≥ 6 h) |
copy-volumes concurrency |
1 copy per source volume at a time; ≤ 5 in progress per Region ⚠️ | Same AZ only; VolumeType defaults to gp2; CopyVolumesLimitExceeded beyond that |
| Concurrent snapshot copies | 20 per destination | ResourceLimitExceeded beyond that |
| Time-based snapshot/AMI copy | 15 min–48 h completion target, 15-minute increments | Extra per-GiB charge |
| EBS Snapshots Archive | Up to 75% cheaper; 90-day minimum; restore up to 72 h; temporary restore 1–180 days | An archived snapshot is a full snapshot; restore it before use, copy or share |
| Recycle Bin retention | Snapshots and AMIs 1–365 days; EBS volumes 1–7 days ⚠️ | Volumes supported since 2025-11-20; the rule must exist before the deletion |
| Recycle Bin rule lock unlock delay | 7–30 days | Region-level rules without exclusion tags |
| Fast Snapshot Restore | ≤ 5 snapshots per Region; billed per snapshot per AZ per minute (1-hour minimum) | Enabled per snapshot per AZ |
| Provisioned Rate for Volume Initialization | 100–300 MiB/s per volume ⚠️ | Since 2025-05-06; 5,000 MiB/s cumulative per Region |
| AMI deprecation | Public AMIs: 2 years after creation by default; max 10 years (private) | Deprecated AMIs remain launchable by ID |
| AMI deregistration protection cooldown | 24 h (optional) | Protection blocks deregistration regardless of IAM |
| AL2023 AMI deprecation | 90 days after each release | Resolve the SSM public parameter instead of hard-coding IDs |
| Amazon Linux 2 end of support | 2026-06-30 ⚠️ | Use the al2023-ami-kernel-default-x86_64 / -arm64 parameters |
| EFS mount targets | 1 per AZ (one subnet per AZ); One Zone file system: 1 in total | Mount targets in one VPC at a time; mount-target SG allows TCP 2049 from the client SG |
| EFS mount target DNS record | Up to 90 s after the mount target is created | Scripted mounts should wait before mounting |
| EFS Elastic throughput | 20–60 GiB/s read, 1–5 GiB/s write per file system ⚠️ | Region-dependent; older material: 3 GiB/s read / 1 GiB/s write |
| EFS per-client throughput | 1,500 MiB/s (Elastic + amazon-efs-utils ≥ 2.0), else 500 MiB/s | Keep amazon-efs-utils current |
| EFS Bursting throughput | Baseline 50 KiB/s per GiB (50 MiB/s per TiB); burst 100 MiB/s per TiB (min 100 MiB/s) | BurstCreditBalance metric |
| EFS Provisioned throughput change lock | 24 h | After switching to Provisioned or changing the amount: no switch away and no decrease for 24 h |
| EFS lifecycle console defaults | IA after 30 days, Archive after 90 days, no move back | TransitionToPrimaryStorageClass AFTER_1_ACCESS is optional |
| EFS IA/Archive billing minimums | 128 KiB per file; Archive 90-day minimum storage | Archive requires Elastic throughput (Regional file systems) |
| Instance store example | i3.16xlarge: 8 × 1,900 GB NVMe, ~3.3 M random-read / 1.44 M write IOPS |
Included in the instance price; survives reboot only |
| Lambda EFS mount path | /mnt/<name> (pattern /mnt/[a-zA-Z0-9-_.]+) |
Access point + VPC configuration required |
| ALB health check defaults | HTTP /, 30 s interval, 5 s timeout, 5 healthy / 2 unhealthy, success code 200 |
Ranges: interval 5–300 s, timeout 2–120 s, thresholds 2–10, codes 200–499; Lambda target groups: off by default (35 s / 30 s when on) |
| NLB health check defaults | TCP, 30 s interval, 10 s timeout (6 s for HTTP), 5 / 2, HTTP codes 200–399 | HTTP/HTTPS health checks optional; matcher range 200–599 |
| ALB connection idle timeout | 60 s (1–4000 s) | Target slower than this → 504; app keep-alive shorter than this → 502 |
| ALB connect timeout to a target | 10 s | No TCP connection in time (SG, NACL, overload) → 504 |
| Deregistration delay (connection draining) | 300 s default, 0–3600 s | 0 disables; target state draining; CLB connection draining 1–3600 s |
| Slow start | 30–900 s (default 0 = off) | Not with least_outstanding_requests or weighted_random |
| Weighted target groups | Up to 5 per forward action, weight 0–999 | Blue/green or canary at the ALB; no automatic failover between groups |
| Lambda target payload | 1 MB request body, 1 MB response JSON | Request too big → 413, response too big → 502; one function per target group, same account and Region |
X-Forwarded-For limit |
30 IP addresses | More → HTTP 463 |
| ALB HTTP header limits | 16 KB request line, 16 KB single header, 64 KB all request headers | Hard limits (400 or 413 when exceeded) |
| ALB access logs | Off by default; delivered every 5 minutes | Same-Region S3 bucket, SSE-S3 only, best effort, pay S3 storage only |
| ELB CloudWatch metric granularity | 60 s | Reported only while traffic flows; RequestCountPerTarget statistic Sum = per-target average |
| GWLB | GENEVE on port 6081, MTU 8,500 bytes | Targets instance/ip; 5-tuple flow stickiness by default |
| NLB TCP idle timeout | 350 s default (60–6000 s) ⚠️ | Configurable since September 2024; TLS listeners stay at a fixed 350 s |
| ALB quotas | 100 rules, 50 listeners, 1,000 targets, 25 certificates (excluding the default) | Per ALB; subnets in at least 2 AZs required |
| ALB stickiness duration | 1 s – 7 days (default 1 day = 86,400 s for lb_cookie and app_cookie) |
lb_cookie (AWSALB), app_cookie (AWSALBAPP), target-group stickiness (AWSALBTG; no default, set the duration when enabling it) |
| ACM public certificate validity | 198 days ⚠️ | CA/B Forum maximum: 100 days from 15 March 2027, 47 days from 15 March 2029 |
| ACM managed renewal start (DNS validated) | 45 days before expiry ⚠️ | Older 395-day certificates renew 60 days before expiry; imported certificates never auto-renew |
| ACM exportable public certificate price | $7 per FQDN, $79 per wildcard name ⚠️ | Charged at issuance and at each renewal; standard ACM certificates are free with integrated services |
| ASG health check grace period | 300 s (console) / 0 s (CLI, SDK) | Delays health-check replacement after InService |
| Default scaling cooldown | 300 s | Applies only to simple scaling policies ⚠️ |
| Default instance warmup | Not set by default; start around 300 s | Used by target tracking, step scaling, instance refresh; instance refresh falls back to the grace period |
| Instance refresh healthy percentages | Min 90% (0–100), Max 100% (100–200) | Max − min ≤ 100; Min 100% = launch before terminate ⚠️ |
| Instance refresh extras | CheckpointDelay 3600 s, BakeTime 0, up to 10 alarms, 14-day limit |
Retries a blocking problem for 1 hour before failing; skip matching: CLI off / console on |
| Lifecycle hook heartbeat timeout | 3600 s default (30–7200 s) | Global max 48 h or 100 × heartbeat; DefaultResult ABANDON |
| Predictive scaling | ≥ 24 h data, up to 14 days analysed, 48 h hourly forecast, refreshed every 6 h | Starts in ForecastOnly; ForecastAndScale only scales out |
| Target tracking high-resolution period | 10 s minimum | The metric must be published at that resolution or finer |
| Scheduled actions | 125 per ASG | Cron in UTC unless a time zone is set; may start up to 2 minutes late |
| Unhealthy instance replacement batch | Up to 10% of desired capacity at a time | Instances that are no longer running are replaced immediately |
| Launch configuration cut-offs | No new instance types since 1 Jan 2023; accounts created on or after 1 Oct 2024 cannot create any ⚠️ | Accounts created on or after 1 Jun 2023 cannot create them in the console |
| EC2 monitoring periods | Basic 5 min (free); detailed 1 min (charged per metric) | Status check metrics are 1 min and free even with basic monitoring |
CPU credit and NetworkPackets* metrics |
5-minute frequency only | CPU credit metrics exist only for burstable T instances |
| CloudWatch metric retention | < 60 s: 3 h; 1 min: 15 days; 5 min: 63 days; 1 h: 455 days (15 months) | High-resolution data points are kept 3 hours at full resolution |
PutMetricData request limits |
1,000 metrics, 1 MB (gzip allowed), 150 values per Values array ⚠️ |
Default 500 requests per second |
| Dimensions per metric | 30 | Each distinct dimension combination is a separate metric |
PutMetricData timestamp window |
2 weeks in the past to 2 hours in the future | Keep instance clocks synced |
StorageResolution |
60 (default, standard) or 1 (high-resolution) | High-resolution data readable at 1, 5, 10, 30 s or multiples of 60 s |
| Alarm period values | 10, 20, 30 s or any multiple of 60 s ⚠️ | Sub-minute periods = high-resolution alarms (extra cost) and need StorageResolution 1 metrics |
| Alarm evaluation window max | 1 day for periods < 1 h; 7 days otherwise | Period × EvaluationPeriods |
| Alarm actions per state | 5 | AlarmActions / OKActions / InsufficientDataActions |
| Recover vs reboot alarm guidance | Recover 2 × 1-minute periods; reboot 3 × 1-minute periods | Avoids a race between the two actions |
| CloudWatch alarm history | 30 days | aws cloudwatch describe-alarm-history |
| CloudWatch free tier | 10 metrics (custom + detailed monitoring combined), 10 alarm metrics, 1 million API requests, 5 GB logs | Basic monitoring metrics are free |
| CloudWatch agent StatsD listener | :8125 (default service_address) |
StatsD on Linux and Windows; collectd on Linux only |
| EC2 application status checks (opt-in) | Probe every 60 s; failure and success thresholds 2; 6 s timeout; 300 s initialization grace ⚠️ | Metric StatusCheckFailed_Application; Auto Scaling replaces impaired instances; $0.01 per managed ENI-hour per AZ |
Decision rules
IAM roles, IMDS and user data (§1–§3)
- If you see an application on EC2 that needs AWS access in the MOST secure way, think IAM role delivered through an instance profile (no access keys on the instance).
- If you see an instance role being ignored (calls run as an IAM user, broader access than the role, or
AccessDeniednaming the wrong principal), think credential-chain precedence: environment variables or~/.aws/credentialswin; confirm withaws sts get-caller-identity. - If you see "change the role of a running instance without downtime", think
replace-iam-instance-profile-association(associate-iam-instance-profileif none is attached). - If you see
UnauthorizedOperationorAccessDeniedwhen launching an instance with a role, think missingiam:PassRoleon the role ARN (or an empty instance profile). - If you see an encoded authorization failure message, think
aws sts decode-authorization-message(needssts:DecodeAuthorizationMessage). - If you see two roles needed on one instance, think one role per instance profile: merge the least-privilege permissions into one role, or use ECS task roles.
- If you see an SSRF or open reverse proxy risk to instance credentials, think IMDSv2 (
HttpTokens=required). - If you see HTTP 401 from
169.254.169.254, think missing or expired IMDSv2 token. - If you see containers on EC2 failing to get credentials after IMDSv2 was required, think PUT response hop limit 1: set it to 2 (or use ECS task roles).
- If you see "prove nothing still uses IMDSv1 before enforcing", think
MetadataNoToken= 0 (then watchMetadataNoTokenRejected). - If you see "all new launches in the account must use IMDSv2", think
modify-instance-metadata-defaultsper Region (+--http-tokens-enforced enabled) or an SCP onec2:MetadataHttpTokens. - If you see "make credentials obtained through IMDSv1 unusable", think a deny on
ec2:RoleDeliveryNumericLessThan2.0. - If you see a bootstrap script that did not rerun after a reboot or after editing user data, think user data runs once per instance by default.
- If you see "debug a failed user data script", think
/var/log/cloud-init-output.log(oraws ec2 get-console-outputwithout SSH). - If you see a password or API key in user data, think Secrets Manager or a Parameter Store
SecureStringfetched at boot with the instance role. - If you see slow Auto Scaling scale-out caused by installs at boot or long user-data scripts, think golden AMI built and distributed by EC2 Image Builder, plus minimal user data.
- If you see "CloudFormation must wait until the software is installed", think
CreationPolicy+cfn-signal. - If you see user data that must change on an existing instance, think stop →
modify-instance-attributewith base64 → start, and remember it will not rerun automatically. - If you see user data not running when supplied through the raw API or a launch template, think missing (or double) base64 encoding.
Connectivity and access (§4)
- If you see a connection that times out or a request that hangs, think security group inbound rule, NACL, route table or missing public IP: the packet never reached a listener.
- If you see an instant
Connection refused, think the host was reached but nothing listens on that port (process down, wrong port, bound to 127.0.0.1). - If you see
AccessDeniedorUnauthorizedOperationfrom an SDK or CLI call, think IAM (role policy, explicit deny, wrong identity), not security groups; usests get-caller-identityandsts decode-authorization-message. - If you see
Unable to locate credentialson EC2, think no instance profile, IMDS disabled, or a container behind hop limit 1. - If you see
Permission denied (publickey), think wrong username, wrong key or key file permissions (chmod 400), not the network. - If you see HTTP 403 from your site or ALB, think application authorization, AWS WAF or a resource policy: the network path works.
- If you see
Connect timeout on endpoint URLfrom a private subnet, think NAT gateway or VPC endpoint (a fastCould not connect to the endpoint URLpoints at the Region, endpoint URL or DNS instead). - If you see "allow the database only from the application tier", think DB security group inbound 3306/5432 with the app security group ID as the source.
- If you see shell access with no inbound ports, auditing and the least operational overhead, think Session Manager (SSM Agent +
AmazonSSMManagedInstanceCore+ outbound 443 or VPC endpoints). - If you see SSH or RDP to a private-subnet instance without a bastion or public IP, think EC2 Instance Connect Endpoint (or Session Manager).
- If you see browser-based SSH controlled by IAM, think EC2 Instance Connect with TCP 22 allowed from the
ec2-instance-connectprefix list. - If you see a public IP that must survive stop/start, think Elastic IP, and remember every public IPv4 costs $0.005 per hour.
Purchasing options and Spot (§5)
- If you see the lowest cost for fault-tolerant, interruptible batch or CI jobs, think Spot through an ASG or EC2 Fleet with
price-capacity-optimizedand many instance types. - If you see "save work before the Spot instance is reclaimed", think IMDS
spot/instance-actionor the EventBridge interruption warning, plus rebalance recommendations and checkpointing to S3 or DynamoDB. - If you see steady usage that will change instance family or Region or move to Fargate or Lambda, think Compute Savings Plans.
- If you see steady usage locked to one instance family in one Region with the biggest discount, think EC2 Instance Savings Plan or Standard RI.
- If you see per-socket or per-core BYOL licensing or host-level visibility, think Dedicated Hosts.
- If you see guaranteed capacity in one AZ for an event with no long-term commitment, think On-Demand Capacity Reservation (add Savings Plans or regional RIs for a discount).
- If you see a persistent Spot request that keeps launching new instances, think cancel the request first, then terminate the instances.
- If you see an ASG that needs a fixed On-Demand baseline with Spot above it, think mixed instances policy with
OnDemandBaseCapacityandOnDemandPercentageAboveBaseCapacity. - If you see a mixed instances ASG that launches no Spot, think
OnDemandPercentageAboveBaseCapacityis still at its default of 100.
EBS, AMIs and storage choice (§6–§7)
- If you see "encrypt an existing unencrypted EBS volume", think snapshot →
create-volume --encrypted(or an encrypted snapshot copy) → swap; encryption is never added in place. - If you see "share an encrypted snapshot or AMI with another account", think customer managed KMS key + key policy/IAM (+ grant) +
createVolumePermissionor launch permission; impossible withaws/ebs. - If you see one volume that needs up to 80,000 IOPS or 2,000 MiB/s at the lowest cost, think gp3 with provisioned IOPS and throughput, not io1/io2.
- If you see more than 80,000 IOPS, sub-millisecond latency, 99.999% durability or Multi-Attach, think io2 Block Express on a Nitro instance.
- If you see the same AMI needed in another Region for deployment or DR, think
copy-imagerun in the destination Region with a destination-Region KMS key, then the new AMI ID in launch templates. - If you see root volume data that disappeared after termination, think
DeleteOnTermination=trueon the root by default; set it to false at launch or withmodify-instance-attribute. - If you see "move EBS data to another AZ or Region", think snapshot (
copy-snapshotfor another Region), thencreate-volumein the target AZ. - If you see a volume that was enlarged while
dfstill shows the old size, thinkgrowpart+xfs_growfsorresize2fs. - If you see "recover an accidentally deleted snapshot, AMI or EBS volume", think a Recycle Bin retention rule that existed before the deletion.
- If you see the cheapest long-term retention for rarely used snapshots, think EBS Snapshots Archive (≥ 90 days, restore up to 72 h).
- If you see volumes restored from snapshots that are slow on first access, think Fast Snapshot Restore (per AZ) or a Provisioned Rate for Volume Initialization.
- If you see "automate snapshot/AMI creation, retention and cross-Region copy by tags", think Amazon Data Lifecycle Manager.
- If you see "always launch the latest Amazon Linux AMI from the CLI or CloudFormation", think the SSM public parameter
/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64viaresolve:ssm:orAWS::SSM::Parameter::Value<AWS::EC2::Image::Id>. - If you see instances going
pending→terminatedwithClient.InternalErrorand encrypted volumes, think missing KMS key permissions for the caller orAWSServiceRoleForAutoScaling. - If you see a deprecated AMI still being used by an Auto Scaling group, think deprecation only hides it: disable or deregister it to block launches.
- If you see a shared POSIX file system for instances in several AZs, think EFS Regional with a mount target per AZ and security groups allowing TCP 2049.
- If you see the highest IOPS for scratch or cache data that can be lost, think instance store (survives reboot, lost on stop, hibernate or terminate).
- If you see persistent storage for a single-instance database, think EBS gp3 (io2 Block Express at extreme IOPS).
- If you see "choose an EFS performance mode", think General Purpose; Max I/O is previous generation.
- If you see spiky or unpredictable EFS throughput, think Elastic throughput; Provisioned only for steady, known demand.
- If you see an EFS mount that hangs and ends in
Connection timed out, think TCP 2049 blocked between the security groups (Failed to resolve server= no mount target in that AZ). - If you see Lambda needing a shared persistent file system, think EFS access point + VPC configuration +
/mnt/mount path +ClientMount/ClientWrite. - If you see "encrypt EFS data in transit", think
mount -t efs -o tlsplus a file system policy denyingaws:SecureTransport= false. - If you see an existing EFS file system that must become encrypted at rest, think a new encrypted file system and replicating or copying the data.
- If you see Windows instances that need a shared file system, think FSx for Windows File Server (SMB).
- If you see HPC/ML needing a parallel file system over S3 data, think FSx for Lustre.
Load balancing, TLS and Auto Scaling (§8–§10)
- If you see routing by URL path, host name, HTTP header, method or query string (or a Lambda function as a target), think ALB listener rules forwarding to target groups.
- If you see fixed or allow-listed IP addresses, Elastic IPs, TCP/UDP traffic or millions of requests per second with the client IP preserved, think Network Load Balancer.
- If you see third-party firewall, IDS/IPS or deep packet inspection appliances inserted transparently into traffic, think Gateway Load Balancer (GENEVE port 6081).
- If you see ALB HTTP 502, think the target closed or reset the connection or returned a malformed response (keep-alive shorter than the idle timeout, Lambda error or timeout).
- If you see ALB HTTP 503, think the target group has no registered (or usable) targets.
- If you see ALB HTTP 504, think the target did not answer before the idle timeout or could not be reached within 10 s (security group or NACL).
- If you see HTTP 460, think the client gave up before the load balancer answered.
- If you see HTTP 463, think more than 30 IP addresses in
X-Forwarded-For. - If you see
HTTPCode_ELB_5XX_Countrising whileHTTPCode_Target_5XX_Countstays flat, think a load balancer or connectivity problem, not application code. - If you see the application logging the load balancer's IP instead of the user's, think
X-Forwarded-For(andX-Forwarded-Protofor the original scheme). - If you see in-flight requests dropped during deployments or scale-in, think deregistration delay (connection draining).
- If you see deployments slowed because targets stay
drainingfor 5 minutes, think a lowerderegistration_delay.timeout_seconds. - If you see traffic shifted gradually to a new version at the load balancer, think weighted target groups on the ALB forward action.
- If you see users losing session data and the MOST scalable or resilient fix, think sessions externalized to ElastiCache or DynamoDB rather than sticky sessions.
- If you see one instance overloaded after enabling stickiness, think stickiness imbalance: shorten the duration or make the tier stateless.
- If you see several HTTPS domains on one load balancer, think an ALB or NLB listener certificate list with SNI (a CLB cannot).
- If you see an ACM certificate for a CloudFront distribution, think request or import it in us-east-1.
- If you see TLS that the load balancer must not decrypt, think NLB TCP listener passthrough.
- If you see client certificate authentication at the load balancer, think ALB mutual TLS in verify mode with a trust store.
- If you see an HTTPS listener created with the CLI or CloudFormation that must enforce TLS 1.2+, think an explicit security policy (the API default is
ELBSecurityPolicy-2016-08). - If you see keeping average CPU or requests per target at a value, think target tracking scaling.
- If you see a known calendar peak, think a scheduled action with cron and a time zone.
- If you see recurring daily or weekly load patterns with slow-booting instances, think predictive scaling paired with a dynamic policy.
- If you see queue workers scaling on SQS, think target tracking on backlog per instance via metric math.
- If you see a new AMI or launch template version rolled through an Auto Scaling group, think instance refresh (min/max healthy %, checkpoints, skip matching, auto rollback with alarms).
- If you see instances terminated and replaced in a loop right after launch, think health check grace period or a launch lifecycle hook (and check the health check path and security groups).
- If you see an Auto Scaling group not replacing instances that fail ALB health checks, think health check type
ELB. - If you see code that must run before an instance takes traffic or before it terminates, think lifecycle hooks with
complete-lifecycle-action. - If you see debugging an instance without Auto Scaling replacing it, think suspend
ReplaceUnhealthyorenter-standby. - If you see slow-booting apps that must scale out fast at low cost, think a warm pool of stopped instances.
- If you see Spot Instances in an Auto Scaling group at elevated interruption risk, think Capacity Rebalancing.
CloudWatch for EC2 (§11)
- If you see memory or disk-space utilisation metrics for EC2, think CloudWatch agent with
CloudWatchAgentServerPolicyon the instance role (CWAgentnamespace). - If you see faster Auto Scaling reaction to EC2 metrics, think detailed (1-minute) monitoring.
- If you see sub-minute custom metrics or alarms, think
PutMetricDatawithStorageResolution1 and a 10, 20 or 30-second alarm period. - If you see
StatusCheckFailed_System, think AWS hardware: recover action or stop/start, not reboot. - If you see
StatusCheckFailed_Instance, think your OS or configuration: reboot or fix it; recover is not allowed on this metric. - If you see a need to test an alarm notification, think
aws cloudwatch set-alarm-state, which really runs the actions for the new state.
Memory hooks
IAM roles, IMDS and user data (§1–§3)
- Profile = envelope, role = letter: one letter per envelope.
- Closest to the code wins; the instance role is the last resort.
- Who am I?
sts get-caller-identityis always allowed. - No pass, no launch:
iam:PassRoleon the role ARN. - PUT to get, GET to use (IMDSv2 token flow).
- Hop limit 1 = host only; 2 = containers.
- 401 = token problem; 403 = IMDS turned off.
- NoToken before, NoTokenRejected after.
- User data: once, as root, logged in
cloud-init-output.log. - 16 KB raw: measured before base64.
- Bake the heavy, boot the light: golden AMI over long user data (bake, don't fry).
- Fetch secrets, don't embed them.
Connectivity and access (§4)
- Timeout = Tunnel blocked; Refused = Reached, nobody home; Denied = Doorman (IAM) said no.
- A security group is a bouncer with a guest list who remembers you on the way out (stateful, allow-only); a NACL is a turnstile that checks every pass in both directions (stateless, allow and deny).
- Trust the badge, not the address: reference security group IDs instead of CIDRs.
- SSH has two S's, so port 22.
- 3389 = desktop (RDP), 3306 = data (MySQL).
- Countdown 5-4-3-2 leads to Postgres.
- Alphabetical ports: Microsoft SQL Server 1433 comes before Oracle 1521.
- 6379 spells MERZ on a phone keypad, so Redis; Memcached 11211 is all 1s and 2s.
- NFS on 2049: a file system from the future (EFS).
- Session Manager: the agent dials out, so nothing has to dial in.
- EC2 Instance Connect is still SSH, still port 22.
Purchasing options and Spot (§5)
- Spot has a two-minute drill, unless it hibernates (then it sleeps at once).
- Compute Savings Plan = commit dollars, not machines.
- Dedicated Host = rent the whole building; Dedicated Instance = your own room, the landlord picks the building.
- Capacity Reservation = paid table booking, even if you don't show up.
EBS, AMIs and storage choice (§6–§7)
- gp3: 3k + 125 free at any size; 80k / 2k / 64T max ⚠️.
- gp2: 3 per GiB, capped at 5,334 GiB; small volumes burst to 3k.
- io2 Block Express: 1000:1, 256k IOPS, 4,000 MiB/s, five nines ⚠️.
- io1 = 50:1, 64k.
- Multi-Attach: io only, 16 in one zone, cluster file system.
- HDD (st1/sc1) can't boot.
- Volume lives in a zone; snapshots and AMIs live in a Region.
- Encryption is one-way: unencrypted → encrypted by copying, never back.
aws/ebsnever leaves the account.- Copy from the Region you're going to (
copy-image/copy-snapshotrun in the destination). - Archive: 75 off, 90 in, 72 out.
- Bin before you delete (Recycle Bin rules must pre-exist).
- Grow the disk, then grow the FS.
- Root dies with the instance (
DeleteOnTermination=true). - Hide, block, delete: deprecate → disable → deregister.
Client.InternalErroron launch = KMS permissions.- Instance store: reboot-safe, stop-sorry.
- EFS: NFS 2049, one mount target per AZ, Linux only.
- EFS today: General Purpose + Elastic; Max I/O is retro ⚠️.
- EFS encryption at rest: at birth or never.
- Console is kind, CLI is literal (EFS API defaults to bursting and unencrypted;
CreateVolumedefaults to gp2).
Load balancing, TLS and Auto Scaling (§8–§10)
- ALB reads the letter, NLB reads only the envelope, GWLB X-rays the parcel, CLB is the retired grandpa.
- 502 = bad answer, 503 = nobody home, 504 = no answer in time.
- ELB_5XX = the load balancer's fault; Target_5XX = your code's fault.
- X-Forwarded-For says who it is For, -Proto how they came, -Port which door they knocked on.
- Cross-zone: ALB always on and free; NLB and GWLB off and paid.
- Drain the bar for 300 s before closing (deregistration delay).
- Cookie jar:
AWSALB(duration),AWSALBAPP(app),AWSALBTG(target group),AWSELB(classic); NLB sticks by source IP with no cookies. - SNI: the client says the name, the load balancer shows the matching badge; no name means the default badge.
- CloudFront certificates live in Virginia (us-east-1).
- ACM: 198 days, renew at 45 ⚠️.
- Scaling policies: thermostat (target tracking), staircase (step), sleepy (simple + cooldown), calendar (scheduled), weather forecast (predictive).
- Grace before judging health; warmup before counting metrics; cooldown only for simple.
- Instance refresh dials: Min keeps the lights on, Max lets the new arrive before the old leave.
- Lifecycle hooks:
Pending:Waitto set up,Terminating:Waitto clean up, and silence meansABANDON.
CloudWatch for EC2 (§11)
- Inside the guest OS = agent plus role; outside the guest = hypervisor metrics.
Disk*metrics = instance store only.- System check = AWS's box, so move it (recover or stop/start); instance check = your box, so reboot or fix it.
- Recovery keeps the ID card and phone numbers (instance ID and IPs) and forgets what was in RAM.