Skip to content
AWS aws ec2 5 min read

Launching an EC2 Instance with the CLI

Clicking through the AWS Management Console (the web dashboard for AWS) is great for learning, but it does not scale. Once you need to launch the same kind of server again and again, you want a single repeatable command. The AWS Command Line Interface (CLI) lets you create and destroy EC2 instances (the virtual servers you rent from AWS) from your terminal in seconds. This page walks through aws ec2 run-instances end to end, plus the commands to list and delete instances, and the one mistake that trips up almost everyone: hardcoding an AMI ID.

When to use the CLI

The CLI is the right tool when you want speed, repeatability, and the ability to drop a command into a script or CI/CD pipeline (the automated system that builds and ships your code). It is also the foundation for shell scripts and for learning what infrastructure tools do under the hood.

Use the Console instead when you are exploring for the first time and want the guided wizard. Use Infrastructure as Code (IaC — describing your infrastructure in files, with tools like CloudFormation or Terraform) instead of one-off CLI commands when the server is permanent and must be tracked, reviewed, and recreated reliably. Think of the CLI as the middle ground: faster than the console, more ad hoc than IaC.

Before you start

You need the AWS CLI v2 installed and configured with credentials. Run a quick check:

aws --version
aws sts get-caller-identity

Output:

aws-cli/2.27.10 Python/3.13.2 Linux/6.8 exe/x86_64
{
    "UserId": "AIDAEXAMPLE1234567890",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/devcraftly"
}

If get-caller-identity fails, run aws configure to set your access key, secret key, and default Region (the geographic location, such as us-east-1, where your resources live).

Finding the right AMI ID

An AMI (Amazon Machine Image — the template that contains the operating system and software for your server) is identified by an ID like ami-0abcdef1234567890. The catch: an AMI ID is unique to a single Region. The Amazon Linux 2023 image in us-east-1 has a completely different ID than the same image in eu-west-1.

Gotcha: Never hardcode an AMI ID. A script with ami-0abcdef1234567890 baked in will silently launch the wrong image in another Region, or fail outright because that ID does not exist there. Resolve the latest AMI dynamically instead.

AWS publishes the latest AMI IDs as SSM public parameters (SSM is AWS Systems Manager, a service that, among other things, stores well-known values you can query). Because the parameter path is the same in every Region, the lookup is portable. Ask for the Amazon Linux 2023 image:

aws ssm get-parameters \
  --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameters[0].Value' \
  --output text

Output:

ami-0abcdef1234567890

Capture it into a variable so the rest of your commands stay Region-correct:

AMI_ID=$(aws ssm get-parameters \
  --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameters[0].Value' --output text)

A few other useful public paths: Ubuntu 24.04 lives under /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id, and the ARM/Graviton variant of Amazon Linux 2023 swaps x86_64 for arm64.

Launching the instance

Now run the launch command. The key parameters are below.

ParameterWhat it doesExample
--image-idThe AMI to boot fromami-0abcdef1234567890
--instance-typeThe hardware size (CPU + RAM)t3.micro
--key-nameThe SSH key pair for loginmy-keypair
--security-group-idsThe firewall rules to attachsg-0a1b2c3d
--subnet-idThe network slice to place it insubnet-0a1b2c3d
aws ec2 run-instances \
  --image-id "$AMI_ID" \
  --instance-type t3.micro \
  --key-name my-keypair \
  --security-group-ids sg-0a1b2c3d \
  --subnet-id subnet-0a1b2c3d \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-01}]' \
  --query 'Instances[0].InstanceId' \
  --output text

Output:

i-0a1b2c3d4e5f

The --tag-specifications flag names the instance web-01 at launch, which is far easier than tagging it afterward. The --query filter (which uses JMESPath, a JSON query language) trims the large default response down to just the new instance ID. Drop --query to see the full JSON if you want the private IP, AMI, and launch time.

Cost note: A t3.micro runs about $0.0104 per hour in us-east-1 (roughly $7.50/month if left on). The AWS Free Tier covers 750 hours/month of a t3.micro for the first 12 months. An instance keeps billing until you terminate it, so do not forget the cleanup step below.

Listing instances

To see what is running, use describe-instances. The raw output is enormous, so filter it to the fields you care about:

aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query 'Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,IP:PublicIpAddress,Name:Tags[?Key==`Name`]|[0].Value}' \
  --output table

Output:

-------------------------------------------------------------
|                     DescribeInstances                     |
+-----------------+-------------+----------------+----------+
|       ID        |     IP      |     Name       |   Type   |
+-----------------+-------------+----------------+----------+
|  i-0a1b2c3d4e5f |  54.12.34.56|  web-01        |  t3.micro|
+-----------------+-------------+----------------+----------+

The --filters option asks AWS to narrow results server-side (here, only running instances), while --query reshapes what is left. Filters are faster and cheaper than pulling everything and grepping locally.

Terminating instances

When you are done, terminate the instance to stop billing. Terminating is permanent: the instance and its root disk are deleted (unlike stopping, which keeps the disk and lets you start again later).

aws ec2 terminate-instances --instance-ids i-0a1b2c3d4e5f \
  --query 'TerminatingInstances[0].CurrentState.Name' \
  --output text

Output:

shutting-down

The state moves from shutting-down to terminated over a minute or two. You can confirm with describe-instances filtered to that ID.

Tip: To avoid accidental deletion of important servers, enable termination protection with aws ec2 modify-instance-attribute --instance-id i-0a1b2c3d4e5f --disable-api-termination. You then have to turn it off before you can terminate.

Console equivalent

If you prefer to verify the same actions in the web console:

  1. Sign in to the AWS Management Console and open the EC2 service.
  2. Choose Launch instances, pick an AMI, instance type, key pair, security group, and subnet, then choose Launch instance.
  3. Open Instances in the left menu to see the running list (the console equivalent of describe-instances).
  4. Select the instance, then choose Instance stateTerminate (delete) instance.

The console hides the Region/AMI problem because it always shows AMIs for your currently selected Region. The CLI does not, which is exactly why the SSM lookup matters.

Best practices

  • Resolve AMI IDs from SSM public parameters at runtime; never hardcode them, so your scripts work in any Region.
  • Always tag instances at launch with --tag-specifications so cost reports and searches are meaningful.
  • Use --query and --filters to keep output readable and to make scripts robust against AWS’s verbose default JSON.
  • Use --dry-run first (e.g. aws ec2 run-instances --dry-run ...) to confirm permissions without actually creating anything.
  • For repeated, identical launches, graduate to a launch template instead of long command lines.
  • Terminate or stop instances you are not using; an idle t3.micro still bills by the hour.
  • Enable termination protection on production instances to prevent a careless terminate-instances from deleting them.
Last updated June 15, 2026
Was this helpful?