Creating Custom EBS-Backed EC2 AMIs from Scratch: A Step-by-Step Guide for Ubuntu and Other Linux Distributions


2 views

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.