EC2: launch a Linux server
EC2 (Elastic Compute Cloud) rents you virtual machines: a real Linux server with CPUs, memory and a disk, ready in about a minute. You pick an image, a size, a network and a key, and you can hand it a script to run on first boot. After that it's a Linux box like any other, and everything from the Linux paths works on it.
You will learn
- AMIs, instance types, key pairs and EBS disks
- Finding the latest Amazon Linux or Ubuntu image with SSM parameters
- User data: a script cloud-init runs as root on first boot
run-instances,wait,describe-instances, thencurlandssh -i- Stop, start and terminate, and what each one costs
The ingredients
| Ingredient | What it is | Example |
|---|---|---|
| AMI | Amazon Machine Image: the disk the server starts from (OS + anything pre-installed) | Amazon Linux 2023, Ubuntu 24.04 |
| Instance type | The size: vCPUs and memory. Family letter + generation + size | t3.micro: 2 vCPU, 1 GiB |
| Key pair | AWS puts the public key on the server. You keep the private key (a .pem file) and log in with it | cht-key.pem |
| Subnet + security group | Where it lives and which ports are open (the last lesson) | public-a, web-sg |
| EBS volume | The server's disk: a network drive that outlives a reboot or stop | 8 GiB gp3 |
| User data | A script that runs once, as root, on first boot | install Apache |
Which Linux? The families again
Everything in this site comes in two flavours, and EC2 is no different. On AWS, the RHEL family is usually Amazon Linux 2023. It's built from Fedora and CentOS Stream, so it uses dnf, httpd and firewalld-style tools, like Rocky. The Debian family is Ubuntu, straight from Canonical. (Rocky and RHEL images exist too, from their publishers.)
aws ssm get-parameter --name \ /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \ --query Parameter.Value --output text
Login user: ec2-user. Packages: dnf. Web server: httpd.
aws ssm get-parameter --name \ /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id \ --query Parameter.Value --output text
Login user: ubuntu. Packages: apt. Web server: apache2.
AMI IDs are different in every region and change with every update, so don't copy them from blog posts. These public SSM parameters always point at the newest image.
User data: set it up on first boot
#!/bin/bash dnf install -y httpd echo "<h1>Hello from $(hostname -s)</h1>" > /var/www/html/index.html systemctl enable --now httpd
#!/bin/bash apt-get update apt-get install -y apache2 echo "<h1>Hello from $(hostname -s)</h1>" > /var/www/html/index.html systemctl enable --now apache2
cloud-init runs this as root, once, on the very first boot. No sudo needed, and no one is watching, so use -y. Everything it prints goes to /var/log/cloud-init-output.log: the first place to look when a new server doesn't work.
Launch!
aws ec2 create-key-pair --key-name cht-key --query KeyMaterial --output text > cht-key.pem chmod 400 cht-key.pem # ssh refuses keys others could read ID=$(aws ec2 run-instances --image-id $AMI --instance-type t3.micro \ --key-name cht-key --subnet-id $SUB --security-group-ids $SG \ --user-data file://web.sh \ --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-1}]' \ --query 'Instances[0].InstanceId' --output text) aws ec2 wait instance-running --instance-ids $ID # blocks until it's up IP=$(aws ec2 describe-instances --instance-ids $ID \ --query 'Reservations[0].Instances[0].PublicIpAddress' --output text) curl http://$IP ssh -i cht-key.pem ec2-user@$IP # ubuntu@$IP on Ubuntu
AWS gives you the private key once. Lose it and you can't log in with that key pair ever again (you'd make a new one and a new server). describe-instances wraps instances in "Reservations" (one per launch request), hence the Reservations[].Instances[] in queries.
Stop, start, terminate
| Action | What happens | You pay for |
|---|---|---|
stop-instances | Shut down, like powering off. The disk stays. The public IP is released, and you get a new one on start | the disk only |
start-instances | Boots again (user data does not run again) | compute + disk |
reboot-instances | A normal reboot. Same IP | compute + disk |
terminate-instances | Gone for good, and so is its root disk | nothing more |
Need an address that never changes? Allocate an Elastic IP and associate it with the instance. Every public IPv4 address, elastic or not, costs about half a cent an hour, even when it isn't attached to anything.
If a server is broken, the cloud way is to throw it away and launch a fresh one from the same image and user data, not to nurse it back to health. That only works if everything it needs is in the user data or the image, never done by hand. The Auto Scaling lesson makes AWS do the replacing for you.
Practice: your first cloud server 🖥️
The network from the last lesson is ready: VPC cht-vpc, subnet public-a, security group web-sg (22 from your IP, 80 from everywhere). web.sh is in your home folder, written for your family's Linux.
Quick check
1. You stopped an instance overnight and started it again. Why can't you reach it at yesterday's address?
✓ Ask for the new one with describe-instances, or attach an Elastic IP. The DNS lesson gives it a name, which is even better.
2. Your new server doesn't answer on port 80, but SSH works. Where do you look first?
✓ And if the connection times out instead of being refused, check the security group.
3. What does chmod 400 cht-key.pem do, and why?
✓ Same rule as ~/.ssh/id_ed25519 in the SSH lesson.