Skip to content

RunInstances

Launches one or more instances. This is the only action that creates a machine.

RunInstances is idempotent through ClientToken. Always send one.

ParameterTypeRequiredNotes
ImageIdstringYesami-…. See Machine images
MinCountintegerYes≥ 1
MaxCountintegerYes≥ 1, and ≥ MinCount
InstanceTypestringNoOur name or an AWS alias. Defaults to cd-standard-2-2
KeyNamestringNoA key pair you have imported. Without it you cannot log in
UserDatastringNoBase64. ≤ 16384 bytes decoded
SecurityGroupId.NlistNoDefaults to the default group of your VPC
SubnetIdstringNoDefaults to your own subnet
PrivateIpAddressstringNoMust be inside your subnet
TagSpecification.NlistNoResourceType must be instance or volume
BlockDeviceMapping.NlistNoRoot device size only
ClientTokenstringNo≤ 64 ASCII characters. Send one
Placement.AvailabilityZonestringNohel1-a
MetadataOptionsstructureNoAccepted only at the platform defaults — see below
DryRunbooleanNoEvaluate permissions and do nothing else

The call launches as many as it can between the two. If it cannot reach MinCount it launches nothing and returns InsufficientInstanceCapacity — it does not partially succeed.

For one machine, set both to 1.

Two calls with the same token return the same instances rather than launching a second set. A token is remembered long enough to cover a retry after a lost response, which is exactly the case it exists for.

Without one, a network timeout between your client and us can leave you paying for a machine you do not know about. Every SDK will generate one if you ask; the CLI takes --client-token.

Base64, at most 16384 bytes decoded — the Amazon limit, enforced here even though the platform underneath would accept more, so that a payload which works on AWS works here unchanged.

Never logged: the parameter is marked sensitive.

Delivery, the config drive, and why a running instance may still be unconfigured: Instance metadata and user data.

Accepted only when every member given equals the platform default:

HttpTokens=optional HttpEndpoint=enabled HttpPutResponseHopLimit=1
HttpProtocolIpv6=disabled InstanceMetadataTags=disabled

Anything else is UnsupportedOperation. In particular HttpTokens=required (IMDSv2) is refused rather than silently downgraded, because the metadata service has no session-token mode and pretending otherwise would leave you believing in a defence you do not have.

Terraform sends this block only when you configure metadata_options.

Supported for the root device only, and only to change its size. Additional volumes are created and attached separately — see Volume actions.

A root device size below the image’s minimum is InvalidParameterValue.

Refused with InvalidParameterValue or UnsupportedOperation, never ignored:

InstanceMarketOptions (spot), CapacityReservationSpecification, Placement.GroupName, Placement.HostId and Placement.Tenancy, LaunchTemplate, IamInstanceProfile, HibernationOptions, ElasticGpuSpecification, ElasticInferenceAccelerators, EnclaveOptions, CpuOptions, CreditSpecification, Ipv6AddressCount and Ipv6Addresses, NetworkInterface.N beyond a single default interface.

Refusing rather than ignoring is deliberate. A module that asks for an IAM instance profile fails at apply rather than launching a machine that silently has no credentials.

A reservation containing one entry per instance launched:

ElementNotes
reservationIdr-…
instancesSetOne instance per machine
instanceIdi-…
imageId, instanceType, keyNameAs launched. instanceType is our name, even if you passed an AWS alias
instanceStatepending at this point
privateIpAddressPresent once the address is assigned
placement.availabilityZonehel1-a
amiLaunchIndexPosition within the reservation
clientTokenIf you sent one
tagSetTags applied at launch
blockDeviceMappingThe root device
groupSetSecurity groups

[!primary]

The response names our instance type, not the alias you launched with. Launch m5.large and DescribeInstances reports cd-standard-2-8. Terraform records that in state, so a configuration written with the AWS name shows a diff on every plan. Write our name, or ignore_changes = [instance_type].

Tags applied through TagSpecification are applied as part of the launch, not afterwards, so a machine never exists untagged. That is what makes tag-based reconciliation safe after a failed retry.

CodeStatusCause
InvalidAMIID.NotFound400No such image, or not one we publish
InvalidParameterValue400Unknown instance type, MinCount/MaxCount < 1, user data not base64 or over the limit, client token over 64 characters, a bad tag
InvalidParameterCombination400MaxCount below MinCount
InvalidKeyPair.NotFound400No such key pair in your account
InvalidGroup.NotFound400No such security group
InstanceLimitExceeded400Your vCPU or memory quota
InsufficientInstanceCapacity500The hardware cannot fit it now. Not a quota problem
UnsupportedOperation400A parameter that exists in the model but not here
Unsupported400A combination that cannot be honoured

InsufficientInstanceCapacity is the one to handle differently: retrying the same shape or asking for more quota will not help. Take a smaller shape, or wait. GET /v1/catalog reports whether a shape is in stock right now — Catalog and availability.

Terminal window
aws --endpoint-url https://ec2.shelfcs.com --region hel1 \
ec2 run-instances \
--image-id ami-… \
--instance-type cd-standard-2-4 \
--key-name mykey \
--count 1 \
--client-token "$(uuidgen)" \
--user-data file://cloud-init.yaml \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web}]'

--count 1 sets both MinCount and MaxCount.

resource "aws_instance" "web" {
ami = data.aws_ami.debian.id
instance_type = "cd-standard-2-4" # our name, not m5.large
key_name = aws_key_pair.mine.key_name
user_data = file("cloud-init.yaml")
tags = { Name = "web" }
}

The provider generates a client token for you.

res = ec2.run_instances(
ImageId=image_id,
InstanceType="cd-standard-2-4",
KeyName="mykey",
MinCount=1, MaxCount=1,
ClientToken=str(uuid.uuid4()),
TagSpecifications=[{
"ResourceType": "instance",
"Tags": [{"Key": "Name", "Value": "web"}],
}],
)
iid = res["Instances"][0]["InstanceId"]
ec2.get_waiter("instance_running").wait(InstanceIds=[iid])

pending becomes running in well under a minute. That means the machine booted — not that your configuration applied. See Instance metadata and user data.

Then connect as the image’s login user, never root — Machine images.