Home / AWS

AWSTroubleshootingBeginner

EC2 user data script did not run: read cloud-init logs and fix the usual mistakes

Your EC2 instance launched but the bootstrap script didn't install anything. Check what AWS received, read the cloud-init logs, fix the common errors and test the script again safely.

Deepak Bhardwaj5 min read

Applies toAmazon EC2, Amazon Linux 2023, Ubuntu, Windows Server with EC2Launch v2

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, so sudo isn't needed.
  • HOME may 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 clean makes 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.