Instance metadata service
The instance metadata service answers HTTP/1.1 requests from inside an instance about that instance. For how it works, see How instance metadata works.
Address
| Family | Address | Port |
|---|---|---|
| IPv4 | 169.254.169.254 | TCP 80 |
| IPv6 | fd00:ec2::254 | TCP 80 |
The IPv6 address answers only on an instance whose NIC has an IPv6 address. Plain HTTP only. The source MAC and source IP of the request must be the pair assigned to the instance's NIC. Any other packet addressed to a metadata address, including UDP, ICMP, and TCP to another port, is dropped without a reply.
Authentication
| Header | Sent on | Value |
|---|---|---|
X-aws-ec2-metadata-token-ttl-seconds | PUT /latest/api/token | Token lifetime in seconds. Required. An integer; values below 1 become 1, values above 21600 become 21600. |
X-aws-ec2-metadata-token | Every other request | The token from PUT /latest/api/token. |
A token is an HS256 JWT signed with a key held by the compute node. It is bound
to the instance and to the network port the PUT arrived on, and expires at
the requested lifetime, with 5 seconds of clock leeway. A token does not verify
on another instance or on another compute node.
Routes
| Method | Path | Token | Returns |
|---|---|---|---|
PUT | /latest/api/token | No | A new token, as the body. |
GET | /latest/meta-data | Yes | The names under meta-data/, one per line. |
GET | /latest/meta-data/<KEY> | Yes | The value of meta-data/<KEY>, or a listing when <KEY> has children. |
GET | /latest/user-data | Yes | The value of user-data. |
GET | /latest/dynamic | Yes | The names under dynamic/. No key exists there, so this returns 404. |
GET | /latest/dynamic/<KEY> | Yes | The value of dynamic/<KEY>. Any path under iam returns 404. |
GET | /triton/dynamic/realized | Yes | Every key the guest can read, as one JSON object. See The realized view. |
GET | /triton/system | Yes | The names under triton/system/. |
GET | /triton/system/<KEY> | Yes | The value of triton/system/<KEY>, or a listing. |
GET | /triton/<TREE> | Yes | The names under <TREE>/, for example /triton/config. |
GET | /triton/<TREE>/<KEY> | Yes | The value of <TREE>/<KEY>, or a listing. |
PUT | /triton/guest/<KEY> | Yes | Updates the existing key guest/<KEY>. See Guest writeback. |
DELETE | /triton/guest/<KEY> | Yes | 501. Not implemented. |
<KEY> can contain /. A path with no matching route returns 404 with an
empty body.
Response format
| Value | Body | Content-Type |
|---|---|---|
| JSON string | The string itself, with no quotes and no trailing newline | text/plain; charset=utf-8 |
| Any other JSON value | The JSON encoding | application/json |
| Listing | One child name per line, sorted, each line ending in a newline. A name with children of its own ends in /. | text/plain; charset=utf-8 |
A path that names no key and has visible keys below it returns the listing.
Computed keys
The platform computes these from the instance's records. They are read-only.
| Key | Path | Value |
|---|---|---|
meta-data/instance-id | /latest/meta-data/instance-id | Instance UUID. |
meta-data/ami-id | /latest/meta-data/ami-id | Image UUID. |
meta-data/instance-type | /latest/meta-data/instance-type | <CPU>vcpu-<MIB>m, for example 2vcpu-4096m. |
meta-data/hostname | /latest/meta-data/hostname | Instance name, or tritond-<INSTANCE_UUID> when the name is empty. |
meta-data/local-hostname | /latest/meta-data/local-hostname | Same as hostname. |
meta-data/mac | /latest/meta-data/mac | Primary NIC MAC address. |
meta-data/local-ipv4 | /latest/meta-data/local-ipv4 | Primary NIC IPv4 address. Absent without one. |
meta-data/local-ipv6 | /latest/meta-data/local-ipv6 | Primary NIC IPv6 address. Absent without one. |
meta-data/public-keys/<N>/openssh-key | /latest/meta-data/public-keys/<N>/openssh-key | The SSH public keys attached at create, numbered from 0. |
triton/system/vpc-id | /triton/system/vpc-id | VPC UUID of the primary NIC. |
triton/system/subnet-id | /triton/system/subnet-id | Subnet UUID of the primary NIC. |
triton/system/vni | /triton/system/vni | The VPC's virtual network identifier, as a JSON number. |
triton/system/image-id | /triton/system/image-id | Image UUID. |
triton/system/image-name | /triton/system/image-name | Image name. |
triton/system/image-os | /triton/system/image-os | Image operating system. |
triton/system/image-version | /triton/system/image-version | Image version. |
triton/system/brand | /triton/system/brand | Instance brand, for example bhyve. |
triton/system/cpu | /triton/system/cpu | Virtual CPUs, as a JSON number. |
triton/system/memory-mib | /triton/system/memory-mib | Memory in MiB, as a JSON number. |
triton/system/owner/tenant | /triton/system/owner/tenant | Tenant UUID. |
triton/system/owner/project | /triton/system/owner/project | Project UUID. |
triton/system/created-at | /triton/system/created-at | Creation time, RFC 3339. |
triton/system/fstab | /triton/system/fstab | Shared filesystem mounts, one <SERVER>:<EXPORT> <PATH> <OPTIONS> line each. |
triton/system/filesystems | /triton/system/filesystems | The same mounts, as a JSON object. |
The primary NIC is the NIC on the instance's primary subnet. When no NIC is on that subnet, it is the first NIC attached to the instance. A key whose source record is missing, such as a NIC that does not exist yet, is absent. The two filesystem keys are absent when the mounts cannot be read.
Stored keys
Stored keys are set with tritonctl meta set at tenant, project, or instance
scope, or by the operator at silo scope. For one instance, the narrowest scope
that sets a key wins: instance, then project, then tenant, then silo.
| Namespace | Path | Scopes | Guest-visible by default |
|---|---|---|---|
config/<KEY> | /triton/config/<KEY> | Any | Yes |
state/<KEY> | /triton/state/<KEY> | Any | Yes |
instance/<KEY> | /triton/instance/<KEY> | Any | At project and instance scope |
guest/<KEY> | /triton/guest/<KEY> | Any | At project and instance scope |
user-data | /latest/user-data | Instance only | Yes |
A key the guest cannot see behaves as absent. --guest-visible on
tritonctl meta set overrides the default. The platform's generated passwords,
instance/root_pw and instance/administrator_pw, are stored hidden from the
guest.
Key rules, enforced when a key is set:
| Rule | Limit |
|---|---|
| Length | 1 to 256 bytes |
| Characters | a to z, 0 to 9, ., _, -, /. The first character is a letter or digit. |
| Segments | At most 8, separated by /, none empty |
| Namespaces | config/, state/, instance/, and guest/ need at least one segment after the namespace. user-data takes none. |
| Value size | 65536 bytes, as encoded JSON |
user-data value | A JSON string |
Keys under meta-data/, system/, and dynamic/ are reserved and cannot be
set.
Service options
Two stored keys control the service for the instances under the scope that sets them.
| Key | Type | Default | Effect |
|---|---|---|---|
config/imds/enabled | Boolean | true, or the operator's cluster default | false turns the service off for the instance. |
config/imds/hop-limit | Integer, 1 to 64 | 1 | The IP time-to-live, or IPv6 hop limit, of every reply. |
When config/imds/enabled is false on an instance that had the service,
PUT /latest/api/token and the GET routes answer 404, imds disabled.
Paths under /latest/dynamic/iam answer 404, not found, as they always do,
and DELETE /triton/guest/<KEY> answers 501. If the service was off for an instance
when its network configuration was last compiled, the instance has no route to
the metadata address, and its requests time out.
A changed hop limit applies to connections the guest opens after the agent re-reads it, which it does every 30 seconds through its 30-second cache. A connection already open keeps the hop limit it was accepted with.
The realized view
GET /triton/dynamic/realized returns one JSON object. Each member is a key
the guest can read:
{
"meta-data/instance-id": { "value": "<INSTANCE_UUID>", "from": "system" },
"config/app/mode": { "value": "production", "from": "project" }
}
from is system for a computed key, or silo, tenant, project, or
instance for the scope a stored key came from.
Guest writeback
PUT /triton/guest/<KEY> replaces the value of guest/<KEY>. The body is
stored as JSON when it parses as JSON, and as a JSON string otherwise. The key
must already exist and carry guest_writable. The write lands at the scope the
key already lives in, and keeps its guest_visible and guest_writable flags.
tritonctl meta set --guest-writable sets the flag, on guest/ keys at
tenant, project, or instance scope.
A successful write returns 200 with the stored entry as JSON, and the next
read from the same instance sees it.
| Status | Cause |
|---|---|
400, body must be UTF-8 | The body is not JSON and not UTF-8. |
400 | The value breaks a key rule, such as the size limit. |
403 | The key is not guest_writable, or lives at silo scope. |
404 | No such key for this instance. |
502 | The agent could not complete the write with the control plane. |
Limits
| Limit | Value |
|---|---|
| Request rate | 100 per second for each instance, with a burst of 200. PUT /latest/api/token does not count. |
| Open connections | 8 for each NIC at once |
| Idle connection | Closed after 30 seconds without progress |
| Freshness | Values are cached for 30 seconds in the agent. A guest write clears the cache for that instance. |
| Token lifetime | 1 to 21600 seconds |
Errors
| Status | Body | Returned by | Cause |
|---|---|---|---|
400 | missing X-aws-ec2-metadata-token-ttl-seconds header | PUT /latest/api/token | No TTL header. |
400 | non-ascii X-aws-ec2-metadata-token-ttl-seconds header | PUT /latest/api/token | The TTL header is not ASCII. |
400 | X-aws-ec2-metadata-token-ttl-seconds must be an integer | PUT /latest/api/token | The TTL header is not an integer. |
401 | missing IMDS token | Every route except the token route | No token header. |
401 | invalid IMDS token | Every route except the token route | The token is malformed, expired, or issued to another instance or on another compute node. |
404 | imds disabled | PUT /latest/api/token and the GET routes, except paths under /latest/dynamic/iam | config/imds/enabled is false for the instance. |
404 | not found | GET routes | No visible key at the path, and none below it. |
429 | rate limit exceeded | Every route except the token route | The instance exceeded the request rate. |
500 | mint failed | PUT /latest/api/token | The agent could not sign the token. |
501 | Starts with IMDS handler not yet implemented | DELETE /triton/guest/<KEY> | Not implemented. |
503 | realized view unavailable | GET routes, except paths under /latest/dynamic/iam | The agent's 30-second copy of the instance's metadata expired, and it could not fetch a new one from the control plane. |
Each body ends with a newline. The token header is checked before the rate
limit, and both before the imds disabled check, so a request without a token
gets 401 even when the service is off.
See also
- Read instance metadata from inside an instance
- Pass user-data when you create an instance
tritonctl instance createfor--user-data.- tritonctl command index for
tritonctl meta.