Instance metadata (IMDSv2) and instance roles
How an EC2 instance gets its identity and credentials — the instance profile, the metadata service and its IMDSv2 session tokens, the hop limit that breaks containers, account-level IMDS defaults and enforcement, and how the AWS SDK finds the credentials.
markdownreferencev47D8H-EBTEstage: referencetrust: officialservice: ec2service: iaminterface: anyIMDSIMDSv2instance metadataHttpTokens requiredhttp-put-response-hop-limitmodify-instance-metadata-defaultshttp-tokens-enforcedmodify-instance-metadata-options169.254.169.254instance profileiam:PassRoleinstance role credentialscontainer on EC2 cannot get credentials
An instance's AWS credentials come from the role in its instance profile, served by the instance metadata service (IMDS). Require IMDSv2 (session tokens) everywhere, and set the hop limit to 2 if containers on the instance need credentials.
The pieces
| Piece | What it is |
|---|---|
| Role | An IAM role trusting ec2.amazonaws.com. Role changes propagate to all instances using it, and EC2 role credentials aren't limited by the role's maximum session duration (trust principal: UNVERIFIED in the collected sources) |
| Instance profile | The container that attaches a role to an instance. One role per profile and one profile per instance (the same role can be in several profiles). The IAM console creates a same-named profile automatically; the CLI, API and SDKs create role and profile separately, possibly with different names, and launches name the profile. To change an instance's permissions, replace the instance profile: removing a role from a profile can take up to an hour to take effect |
| Launch permission | The launcher needs iam:PassRole on the role. Its absence shows up as an encoded UnauthorizedOperation (reference/ec2/errors.md) |
| IMDS | An HTTP endpoint at 169.254.169.254, reachable only from the instance, that serves metadata and the role's temporary credentials |
The AWS SDKs and the CLI find instance credentials automatically, at the end of the default credential chain. Environment variables or a profile set on the instance take precedence and are a common reason the "wrong" identity is used (reference/sts/credential-sources.md).
IMDSv1 vs IMDSv2
- IMDSv2 requires a session token obtained with a
PUT, then sent as a header on eachGET. That blocks the simple request-forgery attacks that make IMDSv1 dangerous. HttpTokens=requireddisables IMDSv1 for the instance. Current AWS SDKs use IMDSv2 automatically.
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 300")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/
(Token header names and paths: UNVERIFIED in the collected sources; they are the documented IMDSv2 interface.)
Hop limit
HttpPutResponseHopLimit limits how many network hops the token response can travel. With the value 1, a process in a container with its own network namespace, such as Docker bridge mode, cannot get a token, so the SDK in the container finds no credentials. Set it to 2 for instances that run containers. AWS's own example settings use 2.
Setting it
| Scope | Command |
|---|---|
| One launch | run-instances … --metadata-options "HttpEndpoint=enabled,HttpTokens=required,HttpPutResponseHopLimit=2" |
| Running instance | aws ec2 modify-instance-metadata-options --instance-id <id> --http-tokens required --http-put-response-hop-limit 2 (flags match the ModifyInstanceMetadataOptions API model: HttpTokens, HttpPutResponseHopLimit, HttpEndpoint, InstanceMetadataTags) |
| Account default, per Region | aws ec2 modify-instance-metadata-defaults --http-tokens required --http-put-response-hop-limit 2 |
| Enforce, per Region | aws ec2 modify-instance-metadata-defaults --http-tokens-enforced enabled: launches that would allow IMDSv1 are refused |
- Account defaults apply to new launches in that Region and do not change running instances.
- A setting in the AMI or launch template, or on the launch request, overrides the account default (UNVERIFIED precedence order).
- The SDK field is
HttpTokensEnforced(disabled,enabled,no-preference) onModifyInstanceMetadataDefaultsCommand, confirmed against @aws-sdk/client-ec2's types.
Symptoms
| Symptom | Likely cause | Check | |
|---|---|---|---|
SDK on the instance: Could not load credentials from any providers | No instance profile attached, IMDS disabled (HttpEndpoint=disabled), or a container blocked by hop limit 1 (the message is verbatim from the AWS SDK for JavaScript v3 source) | aws ec2 describe-iam-instance-profile-associations --filters Name=instance-id,Values=<i>; aws ec2 describe-instances --instance-ids <i> --query 'Reservations[0].Instances[0].MetadataOptions' | |
curl 169.254.169.254/latest/meta-data/ returns 401 | IMDSv2 is required and the request had no token: use the token flow above | Same MetadataOptions → HttpTokens: required | |
| Credentials for the wrong role | Environment variables or ~/.aws on the instance take precedence over the instance profile | On the instance: `env | grep AWS_, ls ~/.aws, then aws sts get-caller-identity` |
| Launch refused after adding a profile | The launcher lacks iam:PassRole: decode the message | aws sts decode-authorization-message --encoded-message <msg> |
The instance identity role (not yours to configure)
Every instance also has an instance identity role, arn:aws:iam::<account>:assumed-role/aws:ec2-instance/<instance-id>, served at /identity-credentials/ec2/security-credentials/ec2-instance. It has no trust policy, is not subject to identity or resource policies, and works only with the services integrated with it: EC2 Instance Connect, Systems Manager Default Host Management Configuration, GuardDuty runtime monitoring, Lambda Managed Instances, and STS GetCallerIdentity. Seeing it in CloudTrail is normal; it isn't the instance-profile role.
Scope note
ECS tasks and Lambda functions do not use IMDS for their roles. They get credentials from their own task or execution role (reference/ecs/roles-and-networking.md, reference/lambda/permissions.md).