Docs / EC2 Docker Instances

EC2: API reference

Run EC2 instances as Docker containers locally. SSH in, run user data scripts, enforce security groups. Real containers, real networking, no AWS account needed.

Want a runnable walkthrough? See EC2: demo & walkthrough.

EC2 backend selector

Terminal
# EC2_VM_MANAGER=docker is the default; this is just for clarity:
$ EC2_VM_MANAGER=docker localemu start

# Opt out of real containers for metadata-only mode:
$ EC2_VM_MANAGER=none localemu start

EC2_VM_MANAGER=docker is the default; every run-instances call already launches a real Docker container. Set EC2_VM_MANAGER=none to switch to metadata-only mode (the AWS API surface still responds, but no container is created).

AMI IDs

LocalEmu ships a built-in AMI ID to Docker image map (services/ec2/docker/ami_mapping.py). Use any of these IDs with run-instances --image-id. Unknown AMI IDs fall back to localemu/ec2-base:v3; you can register a custom image by tagging it as localemu-ec2/<ami-id>.

AMI ID Docker image Notes
ami-ubuntu-22.04 localemu/ec2-base:v3 LocalEmu-managed Ubuntu base. sshd, iptables, curl, awscli, postgresql-client, mysql-client baked in.
ami-localemu-ubuntu localemu/ec2-base:v3 Alias of ami-ubuntu-22.04.
ami-ubuntu-24.04 ubuntu:24.04 Bare upstream image: no sshd, no LocalEmu tooling.
ami-ubuntu-20.04 ubuntu:20.04 Bare upstream image.
ami-amazon-linux-2023 (alias: ami-al2023) amazonlinux:2023 Bare upstream image.
ami-amazon-linux-2 amazonlinux:2 Bare upstream image.
ami-debian-12 debian:12 Bare upstream image.
ami-debian-11 debian:11 Bare upstream image.
ami-alpine-3.20 alpine:3.20 Bare upstream image.
ami-alpine-3.18 alpine:3.18 Bare upstream image.
ami-centos-9 quay.io/centos/centos:stream9 Bare upstream image.

For SSH-driven workflows (the default for localemu ssh, the user-data demo, the transit-gateway tutorial) stick to ami-ubuntu-22.04: it boots with sshd already running. The bare upstream images do not include an SSH server and will not respond on port 22 without a custom user-data script.

Instance Types and Resource Limits

Each instance type maps to Docker container memory limits.

Instance Type Memory Limit
t2.nano 512 MB
t2.micro 1 GB
t2.small 2 GB
t2.medium 4 GB
t2.large 8 GB

SSH Access

Create a key pair, launch an instance with it, then SSH in using the mapped port.

Create a key pair

Terminal
$ awsemu ec2 create-key-pair --key-name my-key --query 'KeyMaterial' --output text > my-key.pem
$ chmod 400 my-key.pem

Launch an instance

Terminal
$ awsemu ec2 run-instances \
    --image-id ami-ubuntu-22.04 \
    --instance-type t2.micro \
    --key-name my-key

InstanceId: i-abc123def456
State: pending

Get the SSH port from the localemu:ssh-port tag

Terminal
$ awsemu ec2 describe-tags \
    --filters "Name=resource-id,Values=i-abc123def456" \
              "Name=key,Values=localemu:ssh-port"

Tags:
  - Key: localemu:ssh-port
    Value: 22022

Connect via SSH

Terminal
$ ssh -i my-key.pem -p 22022 root@localhost

localemu ssh Command

The localemu ssh command provides a shortcut to connect to EC2 instances without manually looking up ports.

Terminal
$ localemu ssh i-abc123def456

$ localemu ssh --list
i-abc123def456  running  ami-ubuntu-22.04  t2.micro  port:22022

SSM Session Manager

Start an interactive session using SSM. No SSH keys required.

Terminal
$ awsemu ssm start-session --target i-abc123def456

User Data

Pass a startup script with --user-data. The script runs automatically when the container boots.

Terminal
$ awsemu ec2 run-instances \
    --image-id ami-ubuntu-22.04 \
    --instance-type t2.small \
    --key-name my-key \
    --user-data file://setup.sh

Instance Metadata Service (IMDS)

A per-VPC IMDS sidecar serves the full /latest/meta-data/ tree (instance-id, ami-id, instance-type, local hostname + IP, IAM credentials, ...) and is reachable from inside the container two ways: an iptables OUTPUT-chain DNAT rule rewrites every packet to the link-local 169.254.169.254:80 address that boto3 / awscli / the AWS SDKs hardcode, and the AWS_EC2_METADATA_SERVICE_ENDPOINT env var is also injected for the small set of clients that honour it. Both paths land on the same sidecar.

Security Groups

Security group rules are enforced via a TCP proxy. Rules are evaluated at connection time. Changes from AuthorizeSecurityGroupIngress take effect immediately.

Terminal
$ awsemu ec2 create-security-group \
    --group-name web-sg \
    --description "Allow HTTP"

GroupId: sg-abc123

$ awsemu ec2 authorize-security-group-ingress \
    --group-id sg-abc123 \
    --protocol tcp \
    --port 80 \
    --cidr 0.0.0.0/0

Container Naming

Each EC2 instance runs in a Docker container named localemu-ec2-<instance-id>.

Terminal
$ docker ps --filter "label=localemu.service=ec2"

CONTAINER ID   IMAGE                  STATUS       NAMES
a1b2c3d4e5f6   localemu/ec2-base:v3   Up 2 min     localemu-ec2-i-abc123def456

Instance Lifecycle

EC2 instances follow the standard lifecycle. Each state maps to a Docker container state.

pending running stopping stopped terminated