A Complete Guide to Automating Instance Configuration at Launch
User Data is a feature provided by Amazon EC2 that allows you to run scripts automatically when an instance launches. These scripts can be used to configure the instance, install software, or perform any other setup tasks.
The most common type, starting with #!/bin/bash for Linux instances.
#!/bin/bash yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd
YAML-formatted cloud-init configuration for more complex setups.
#cloud-config packages: - httpd - php runcmd: - systemctl start httpd - systemctl enable httpd
For Windows instances, using PowerShell scripts.
Install-WindowsFeature -Name Web-Server New-Item -Path C:\inetpub\wwwroot\index.html -Value "Hello World"
# Launch with user data from file aws ec2 run-instances \ --image-id ami-12345678 \ --instance-type t2.micro \ --key-name MyKeyPair \ --user-data file://my-script.sh # Launch with inline user data aws ec2 run-instances \ --image-id ami-12345678 \ --instance-type t2.micro \ --key-name MyKeyPair \ --user-data "#!/bin/bash yum update -y yum install -y httpd systemctl start httpd"
User data can be modified for stopped instances:
# Modify user data for stopped instance aws ec2 modify-instance-attribute \ --instance-id i-1234567890abcdef0 \ --attribute userData \ --value file://new-script.sh
From the EC2 console:
From the instance (Linux):
# View user data curl http://169.254.169.254/latest/user-data # View base64-encoded user data curl -s http://169.254.169.254/latest/user-data | base64 --decode
For Linux instances:
# View cloud-init output log cat /var/log/cloud-init-output.log # View cloud-init log cat /var/log/cloud-init.log
For Windows instances:
# View EC2Launch logs Get-Content C:\ProgramData\Amazon\EC2-Windows\Launch\Log\EC2Launch.log # View EC2Launch v2 logs Get-Content C:\ProgramData\Amazon\EC2Launch\log\agent.log
Cloud-init is a multi-distribution package that handles early initialization of a cloud instance. It provides advanced configuration options through YAML directives.
#cloud-config
# Update package lists
package_update: true
# Install packages
packages:
- nginx
- python3
- git
# Create users
users:
- name: devuser
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2E...
# Write files
write_files:
- path: /var/www/html/index.html
content: |
Hello World!
# Run commands
runcmd:
- systemctl start nginx
- systemctl enable nginx
MIME multi-part archives allow you to include multiple scripts or configuration files in a single user data payload.
Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config package_update: true packages: - httpd - php --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.txt" #!/bin/bash echo "Hello World" > /var/www/html/index.html systemctl start httpd systemctl enable httpd --//--
#!/bin/bash # Download bootstrap script from S3 aws s3 cp s3://my-bucket/bootstrap.sh /tmp/ chmod +x /tmp/bootstrap.sh /tmp/bootstrap.sh # Or using curl/wget curl -s https://my-website.com/bootstrap.sh | bash
#!/bin/bash
# Install AWS CFN tools
yum install -y aws-cfn-bootstrap
# Signal success or failure to CloudFormation
/opt/aws/bin/cfn-signal -e $? \
--stack ${AWS::StackName} \
--resource WebServerInstance \
--region ${AWS::Region}
#!/bin/bash
# Exit on error
set -e
# Error handling function
handle_error() {
local exit_code=$?
echo "Error occurred on line $1, exit code: $exit_code"
# Send notification or log to external service
aws sns publish \
--topic-arn arn:aws:sns:region:account:topic \
--message "Instance bootstrap failed"
exit $exit_code
}
# Set trap for errors
trap 'handle_error $LINENO' ERR
# Your commands here
yum update -y
yum install -y httpd
systemctl start httpd
Netflix uses User Data scripts to bootstrap their EC2 instances with their custom Spinnaker deployment platform. This allows them to quickly deploy and configure thousands of instances with consistent configurations.
OpenAI leverages User Data scripts to set up GPU-optimized instances for AI model training and inference. Their scripts configure CUDA drivers, ML frameworks, and monitoring tools automatically.
Prime Video uses User Data to configure streaming servers, setting up content delivery networks and caching layers automatically when scaling up during high-demand periods.
#!/bin/bash
# Install and configure NGINX
yum update -y
yum install -y nginx
systemctl start nginx
systemctl enable nginx
# Create custom index page
cat > /usr/share/nginx/html/index.html << 'EOF'
Welcome to my website
Hello from EC2!
This server was configured automatically.
EOF
#!/bin/bash # Install and configure MySQL yum update -y yum install -y mysql-server systemctl start mysqld systemctl enable mysqld # Secure MySQL installation mysql_secure_installation << EOF y MySecurePassword MySecurePassword y y y y EOF # Create database and user mysql -u root -pMySecurePassword << EOF CREATE DATABASE myapp; CREATE USER 'appuser'@'%' IDENTIFIED BY 'AppPassword123'; GRANT ALL PRIVILEGES ON myapp.* TO 'appuser'@'%'; FLUSH PRIVILEGES; EOF
#!/bin/bash # Get instance metadata INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/region) # Register with load balancer aws elbv2 register-targets \ --target-group-arn arn:aws:elasticloadbalancing:$REGION:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 \ --targets Id=$INSTANCE_ID \ --region $REGION # Get configuration from Parameter Store CONFIG=$(aws ssm get-parameter \ I'll provide the rest of the code for the user_data.html file, continuing from where it left off: ```html --name /myapp/config \ --with-decryption \ --region $REGION \ --query Parameter.Value \ --output text) # Apply configuration echo "$CONFIG" > /etc/myapp/config.json
#!/bin/bash
# Install Node.js
curl -sL https://rpm.nodesource.com/setup_14.x | bash -
yum install -y nodejs
# Create app directory
mkdir -p /var/www/myapp
cd /var/www/myapp
# Clone application from Git
git clone https://github.com/myuser/myapp.git .
# Install dependencies
npm install
# Setup process manager
npm install -g pm2
pm2 start app.js
pm2 startup
pm2 save
# Setup nginx as reverse proxy
cat > /etc/nginx/conf.d/myapp.conf << 'EOF'
server {
listen 80;
server_name _;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
EOF
# Restart nginx
systemctl restart nginx
| Feature | User Data | Custom AMIs | Systems Manager | CloudFormation |
|---|---|---|---|---|
| Execution Timing | First boot only | Pre-configured | Any time | Stack creation/update |
| Size Limitation | 16 KB | None | None | 51,200 bytes |
| Reusability | Medium | High | High | High |
| Maintenance | Simple | Complex | Medium | Medium |
| Ongoing Management | No | No | Yes | Yes |