# Amazon Linux 2 Terminal Cheatsheet

- URL: https://karandeepsingh.ca/cheatsheets/terminal/al2-terminal/
- Author: Karandeep Singh
- Published: 2025-07-23
- Tags: linux, aws, ec2
- Summary: Working notes for Amazon Linux 2 on EC2. Covers yum and amazon-linux-extras, SSM access, instance metadata, cloud-init, and security updates.

A lot of my EC2 fleet still runs Amazon Linux 2, and it has just enough quirks (amazon-linux-extras, IMDSv2, SSM instead of SSH) that muscle memory from Ubuntu doesn't fully transfer. This is my working reference for AL2 boxes. If you're coming from Debian-land, my [Ubuntu Terminal Cheatsheet](https://karandeepsingh.ca/cheatsheets/terminal/ubuntu-terminal/) is the apt-side mirror of this page.

## yum - Package Management

> **Docs:** [Amazon Linux basics in the EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-ami-basics.html)

```bash
sudo yum update -y                   # update everything
sudo yum check-update                # what would update (exit 100 = updates exist)
sudo yum install -y htop             # install
sudo yum install -y https://example.com/pkg.rpm   # install from URL
sudo yum remove htop                 # remove
yum search nginx                     # search names/summaries
yum info docker                      # version, repo, description
yum list installed | grep kernel     # installed packages
sudo yum history                     # transaction history
sudo yum history undo 12             # roll back a transaction
sudo yum clean all                   # clear caches
repoquery -l htop                    # files a package installs (yum-utils)
```

Repo management with `yum-config-manager` (from `yum-utils`):

```bash
sudo yum install -y yum-utils
yum repolist                                 # enabled repos
sudo yum-config-manager --enable epel
sudo yum-config-manager --disable epel
sudo yum-config-manager --add-repo https://repo.example.com/example.repo
```

Security-only patching:

```bash
sudo yum update --security -y        # only security-flagged updates
yum updateinfo list security         # list pending security advisories
yum updateinfo info ALAS2-2026-1234  # details on one advisory
```

## amazon-linux-extras

> **Docs:** [Amazon Linux basics in the EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-ami-basics.html)

AL2's mechanism for newer software than core repos carry:

```bash
amazon-linux-extras list                     # topics and enabled state
sudo amazon-linux-extras enable docker       # enable a topic
sudo amazon-linux-extras install -y docker   # enable + install in one step
sudo amazon-linux-extras install -y epel     # EPEL, the sanctioned way on AL2
sudo amazon-linux-extras install -y nginx1 postgresql14
```

Common topics: `docker`, `epel`, `nginx1`, `postgresql14`, `redis6`, `java-openjdk11`, `python3.8`, `kernel-5.10`.

```bash
# the usual docker-on-AL2 sequence
sudo amazon-linux-extras install -y docker
sudo systemctl enable --now docker
sudo usermod -aG docker ec2-user        # then re-login
```

## systemd - Services

> **Docs:** [systemctl(1) on man7.org](https://man7.org/linux/man-pages/man1/systemctl.1.html)

AL2 is systemd-based, so this is the same as any modern distro:

```bash
systemctl status amazon-ssm-agent
sudo systemctl restart docker
sudo systemctl enable --now nginx        # start now and on boot
systemctl list-units --type=service --state=failed
journalctl -u docker -f                  # follow a unit's logs
journalctl -u cloud-init --no-pager
sudo systemctl daemon-reload             # after editing unit files
```

Note: on AL2 much of syslog still lands in `/var/log/messages` (not `/var/log/syslog` as on Ubuntu).

## SSM Session Manager vs SSH

> **Docs:** [Session Manager in the AWS Systems Manager User Guide](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html)

I default to SSM: no open port 22, no key distribution, everything audited in CloudTrail.

```bash
# from your workstation (needs session-manager-plugin + instance profile with SSM policy)
aws ssm start-session --target i-0abc123def456

# port-forward through SSM (e.g. reach an RDS via the instance)
aws ssm start-session --target i-0abc123def456 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"host":["db.internal"],"portNumber":["5432"],"localPortNumber":["15432"]}'

# on the instance: check the agent
sudo systemctl status amazon-ssm-agent
```

Requirements: the `amazon-ssm-agent` (preinstalled on AL2 AMIs), an instance profile with `AmazonSSMManagedInstanceCore`, and outbound HTTPS to SSM endpoints. Classic SSH still works when you need it:

```bash
ssh -i key.pem ec2-user@<public-ip>
```

## ec2-user & sudo

> **Docs:** [Amazon Linux basics in the EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-ami-basics.html)

The default user is `ec2-user`, with passwordless sudo baked in via `/etc/sudoers.d/90-cloud-init-users`:

```bash
sudo -i                              # root shell
sudo visudo -f /etc/sudoers.d/deploy # add rules safely, syntax-checked
sudo adduser deploy                  # note: adduser is a useradd alias here,
sudo passwd deploy                   #   not Ubuntu's interactive wrapper
sudo usermod -aG wheel deploy        # wheel = the sudo group on AL2 (not "sudo")
```

## Instance Metadata (IMDSv2)

```diff
# userdata.sh
@@ -1,6 +1,9 @@
-# v1: one unauthenticated GET. Also reachable by anything that can make the
-# instance fetch a URL for it, which is how SSRF turns into credentials.
-REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/region)
+# v2: get a token first, then send it. A blind SSRF cannot set a header.
+TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token \
+          -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
+REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
+          http://169.254.169.254/latest/meta-data/placement/region)
```

**warning:**
If a metadata call returns `401 Unauthorized`, the instance has
`HttpTokens=required` and the script is still speaking v1. That is the fix
above, not a networking problem.

> **Docs:** [Instance metadata and user data in the EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)

Always use IMDSv2. It's session-token based, and many hardened AMIs disable IMDSv1 entirely:

```bash
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

curl -sH "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id
curl -sH "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/availability-zone
curl -sH "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/local-ipv4
curl -sH "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl -sH "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/tags/instance/Name  # if tag access enabled
```

For anything beyond a quick lookup, script against AWS from the instance with the SDK; the instance role's credentials are picked up automatically. See my [Python Boto3 Cheatsheet](https://karandeepsingh.ca/cheatsheets/python/python-boto3/) for the patterns.

## cloud-init - Debugging User Data

> **Docs:** [cloud-init documentation](https://cloudinit.readthedocs.io/)

When an instance doesn't come up configured, this is where to look:

```bash
sudo cat /var/log/cloud-init-output.log   # stdout/stderr of your user-data script
sudo less /var/log/cloud-init.log         # cloud-init's own detailed log
cloud-init status --long                  # done / running / error
sudo cat /var/lib/cloud/instance/user-data.txt   # what user data actually ran
sudo cloud-init clean --logs && sudo reboot      # force user data to re-run (testing)
```

## CloudWatch Agent Basics

> **Docs:** [Install the CloudWatch agent in the CloudWatch User Guide](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html)

```bash
sudo yum install -y amazon-cloudwatch-agent
# interactive config wizard → writes JSON config
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard

# start with a local config file
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
  -a fetch-config -m ec2 -s \
  -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json

# or pull config from SSM Parameter Store (the fleet-friendly way)
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
  -a fetch-config -m ec2 -s -c ssm:AmazonCloudWatch-linux

/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a status
sudo tail -f /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log
```

## Timezone & Locale

> **Docs:** [timedatectl(1) on man7.org](https://man7.org/linux/man-pages/man1/timedatectl.1.html)

```bash
timedatectl                                   # current time, TZ, NTP sync (chrony)
sudo timedatectl set-timezone America/Edmonton
timedatectl list-timezones | grep -i edmonton
localectl status
sudo localectl set-locale LANG=en_US.UTF-8
chronyc tracking                              # NTP sync details (Amazon Time Sync)
```

## AL2 vs AL2023

> **Docs:** [Comparing AL2 and AL2023 in the Amazon Linux 2023 User Guide](https://docs.aws.amazon.com/linux/al2023/ug/compare-with-al2.html)

**info:**
Amazon Linux 2023 replaces `yum` with `dnf` (mostly flag-compatible) and **drops `amazon-linux-extras` entirely**; packages live in the main repos. It also uses deterministic upgrades (versioned repositories) instead of continuous updates, and there is no EPEL for AL2023. AL2 hits end-of-life June 2026, so new builds should target AL2023; on it, read `yum` as `dnf` throughout this sheet.

## Common Paths

| Path | What lives there |
|------|------------------|
| `/etc/yum.repos.d/` | yum repo definitions |
| `/etc/system-release` | AL2 version string |
| `/var/log/cloud-init-output.log` | User-data script output |
| `/var/log/cloud-init.log` | cloud-init detailed log |
| `/var/lib/cloud/instance/` | Current instance's cloud-init state |
| `/var/log/messages` | General syslog (Ubuntu's `/var/log/syslog` equivalent) |
| `/var/log/secure` | Auth log (Ubuntu's `/var/log/auth.log` equivalent) |
| `/var/log/amazon/ssm/` | SSM agent logs |
| `/opt/aws/amazon-cloudwatch-agent/` | CloudWatch agent binaries, config, logs |
| `/etc/sudoers.d/90-cloud-init-users` | ec2-user's passwordless sudo grant |
| `/home/ec2-user/.ssh/authorized_keys` | Key pair injected at launch |

## Quick Reference

| Task | Command |
|------|---------|
| Patch security only | `sudo yum update --security -y` |
| Enable Docker | `sudo amazon-linux-extras install -y docker` |
| Shell in without SSH | `aws ssm start-session --target i-...` |
| Instance ID | IMDSv2 token curl (see above) |
| Debug user data | `sudo cat /var/log/cloud-init-output.log` |
| AL2 version | `cat /etc/system-release` |

**Question:** Does any script on your instances still read metadata without a token? On an account with IMDSv2 enforced they fail with 401 — would you recognise that error for what it is?

## Related Cheatsheets

- [Ubuntu Terminal](https://karandeepsingh.ca/cheatsheets/terminal/ubuntu-terminal/): the apt/Debian side, with netplan, ufw, unattended upgrades, and `do-release-upgrade`
- [Ansible](https://karandeepsingh.ca/cheatsheets/devops/ansible/): inventories, modules, and playbooks, so you stop configuring instances by hand
- [tmux](https://karandeepsingh.ca/cheatsheets/terminal/tmux/): persistent sessions for long yum updates and SSM/SSH work that has to survive a disconnect

There's more in the [Terminal group](https://karandeepsingh.ca/cheatsheets/terminal/), and the whole [cheatsheet library](https://karandeepsingh.ca/cheatsheets/) if you want to browse.
