When Amazon introduced EBS-backed AMIs in 2009, many developers assumed they could create completely custom images without relying on existing Fedora/Windows AMIs. The documentation initially suggested limitations, but the reality is more flexible than it appears.
There are several compelling reasons to create custom AMIs:
- Complete control over the base operating system
- Ability to pre-configure security settings
- Optimized performance for specific workloads
- Reduced instance launch times
Before starting, ensure you have:
# AWS CLI configured with proper permissions
aws configure
# Basic tools installed
sudo apt-get install -y git awscli grub2 kpartx
1. Launch a Base Instance
Start with a minimal Ubuntu instance:
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t2.micro \
--key-name MyKeyPair
2. Prepare the Root Volume
Once connected via SSH:
# Update package lists
sudo apt-get update
# Install essential packages
sudo apt-get install -y linux-image-virtual cloud-init
# Clean up temporary files
sudo apt-get clean
3. Create the AMI
Use the following AWS CLI command:
aws ec2 create-image \
--instance-id i-1234567890abcdef0 \
--name "MyCustomUbuntuAMI" \
--description "Custom Ubuntu 20.04 LTS AMI" \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":8,"DeleteOnTermination":true}}]'
Using Packer for Automated Builds
Example Packer template (JSON):
{
"builders": [{
"type": "amazon-ebs",
"region": "us-west-2",
"source_ami": "ami-0abcdef1234567890",
"instance_type": "t2.micro",
"ssh_username": "ubuntu",
"ami_name": "custom-ubuntu-{{timestamp}}",
"ami_description": "Custom Ubuntu AMI",
"ami_block_device_mappings": [{
"device_name": "/dev/sda1",
"volume_size": 8,
"delete_on_termination": true
}]
}],
"provisioners": [{
"type": "shell",
"scripts": [
"scripts/install-packages.sh",
"scripts/configure-security.sh"
]
}]
}
After creation, test your AMI thoroughly:
aws ec2 run-instances \
--image-id ami-your-new-ami-id \
--instance-type t2.micro \
--key-name MyKeyPair
- Boot failures: Check kernel and GRUB configuration
- SSH connection problems: Verify cloud-init installation
- Permission errors: Review IAM roles and policies
When working with AWS EC2, there comes a point where you need complete control over your machine images. While starting from existing AMIs works for basic scenarios, building from scratch offers several advantages:
- Eliminate unwanted pre-installed packages
- Implement custom security hardening
- Create minimal images for specific workloads
- Build images that match your organizational standards
Before we begin, ensure you have:
- AWS CLI configured with proper permissions
- An EC2 key pair created in your target region
- Basic understanding of Linux system administration
1. Launch a Base Instance
We'll start with a minimal Ubuntu instance:
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \ # Ubuntu 22.04 LTS
--instance-type t2.micro \
--key-name MyKeyPair \
--security-group-ids sg-0123456789abcdef0 \
--subnet-id subnet-0123456789abcdef0
2. Configure the Base System
SSH into the instance and perform these essential configurations:
# Update package lists
sudo apt update && sudo apt upgrade -y
# Install essential packages
sudo apt install -y cloud-init cloud-guest-utils \
linux-aws linux-headers-aws linux-image-aws
3. Prepare for AMI Creation
Clean up the system and prepare it for imaging:
# Clear package cache
sudo apt clean
# Remove machine-specific files
sudo rm -rf /etc/ssh/ssh_host_*
sudo truncate -s 0 /etc/machine-id
# Clean cloud-init data
sudo cloud-init clean --logs
4. Create the AMI
Now create your EBS-backed AMI:
aws ec2 create-image \
--instance-id i-0123456789abcdef0 \
--name "MyCustomUbuntuAMI" \
--description "Custom Ubuntu 22.04 LTS AMI" \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"DeleteOnTermination":true}}]'
Adding Custom Scripts
You can include initialization scripts in /etc/cloud/cloud.cfg.d/
:
#!/bin/bash
cat > /etc/cloud/cloud.cfg.d/99_custom.cfg << 'EOF'
#cloud-config
package_update: true
package_upgrade: true
packages:
- nginx
- postgresql-client
EOF
Security Hardening
Consider these additional security measures:
# Disable root login
sudo sed -i 's/^PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
# Enable automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
After creating your AMI, test it by launching a new instance:
aws ec2 run-instances \
--image-id ami-yournewamiid \
--instance-type t2.micro \
--key-name MyKeyPair
This process gives you complete control over your EC2 images while maintaining the benefits of EBS-backed AMIs.