Symptom
You launched an EC2 instance with a user data script, but the package isn't installed, the service isn't running or the file is missing. The instance itself looks healthy and passes its status checks.
User data doesn't report failures to the console, so you have to look on the instance.
Step 1: check what AWS actually received
From your own machine:
aws ec2 describe-instance-attribute \
--instance-id i-0123456789abcdef0 \
--attribute userData \
--query "UserData.Value" --output text | base64 --decode
No output means the instance has no user data at all, which is common when a launch template version didn't include it.
On the instance, with an IMDSv2 token:
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 300")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/user-data
Step 2: read the logs on Linux
Connect with SSH or Session Manager, then:
cloud-init status --long
sudo tail -n 50 /var/log/cloud-init-output.log
sudo grep -iE "error|fail|warn" /var/log/cloud-init.log | tail -20
| File | Contains |
|---|---|
/var/log/cloud-init-output.log | Everything your script printed. Start here |
/var/log/cloud-init.log | What cloud-init itself did |
/var/lib/cloud/instance/user-data.txt | The user data as received |
/var/lib/cloud/instance/scripts/ | The script as saved for running |
If cloud-init status says running, it hasn't finished yet. Wait with:
cloud-init status --wait
Step 3: match the error to the fix
| What you see | Cause | Fix |
|---|---|---|
| Script is ignored; no output in the log | First line isn't #!/bin/bash | Add the shebang as the very first line |
/bin/bash^M: bad interpreter | Windows line endings | Save with LF endings or run dos2unix |
command not found | A different distro: yum vs apt | Use the package tool for that AMI |
Could not resolve host or a timeout | No route to the internet | Check the NAT gateway, route table and security group |
AccessDenied from aws s3 cp | The instance role lacks access or is missing | Attach a role with the needed permission |
Unable to locate package on Ubuntu | Package lists are empty | Run apt-get update first |
| Script stops halfway, with no clear error | One command failed and later steps depended on it | Add set -euxo pipefail for visible logging |
| Nothing happens after a reboot | User data runs only on first boot by default | See "Test it again" below |
A template that fails visibly
#!/bin/bash
set -euxo pipefail
dnf update -y
dnf install -y nginx
systemctl enable --now nginx
echo "bootstrap finished $(date -Is)" > /var/tmp/bootstrap-done
Keep in mind:
- The script runs as
root, sosudoisn't needed. HOMEmay not be set, and the working directory isn't your user's home. Use full paths.- User data has a size limit (16 KB before base64 encoding). For anything bigger, download a script from S3.
Step 4: Windows instances
The script must be wrapped in tags:
<powershell>
Install-WindowsFeature -Name Web-Server -IncludeManagementTools
New-Item -Path C:\bootstrap-done.txt -ItemType File -Force
</powershell>
Add <persist>true</persist> on a line after the closing tag if you need it to run at every boot.
Logs for EC2Launch v2:
Get-Content C:\ProgramData\Amazon\EC2Launch\log\agent.log -Tail 50
Get-ChildItem C:\ProgramData\Amazon\EC2Launch\log\
Test it again without a new instance
Run the saved script directly and watch the output:
sudo bash -x /var/lib/cloud/instance/scripts/part-001
The file name can vary. List the folder first.
To make cloud-init run everything again on the next boot:
sudo cloud-init clean --logs
sudo reboot
Warning
cloud-init cleanmakes the instance behave as if it's booting for the first time, so all your cloud-init steps run again. Your script may install or overwrite things twice. Use this on test instances, not production.
The most reliable test is often the simplest one: terminate the test instance and launch a new one with the corrected script.
Change the user data on an existing instance
The instance must be stopped first. Changing it doesn't re-run it by itself.
aws ec2 stop-instances --instance-ids i-0123456789abcdef0
aws ec2 wait instance-stopped --instance-ids i-0123456789abcdef0
aws ec2 modify-instance-attribute --instance-id i-0123456789abcdef0 \
--attribute userData --value file://user-data.txt
aws ec2 start-instances --instance-ids i-0123456789abcdef0
Depending on your CLI version, a file may need to be base64-encoded first. Check the result with the command from Step 1.
Caution
Never put passwords or keys in user data. Anyone or anything on the instance that can reach the metadata service can read it. Fetch secrets at runtime from Secrets Manager or Parameter Store instead.
Prevent it next time
- Start every script with
set -euxo pipefail, so it stops and logs the failing line. - Write a marker file at the end, and have your health check look for it.
- Test scripts on a throwaway instance before you put them in a launch template.
- For more on building instances, see the standalone EC2 build. For access problems, see SSH connection timeouts.