# Run Aether in an AWS Lambda `MicroVM`
This crate provides the lifecycle hook server needed to run Aether as a one-shot coding agent in a Lambda `MicroVM`.
## Build a `MicroVM` image
### 1. Create a `Dockerfile` containing Aether, the hook server, Git, and the GitHub CLI:
```dockerfile
FROM rust:1.97-bookworm AS build
RUN cargo install --locked aether-agent-cli
RUN cargo install --locked lambda-microvm-hook-server
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates git gh \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /usr/local/cargo/bin/aether /usr/local/bin/
COPY --from=build /usr/local/cargo/bin/lambda-microvm-hook-server /usr/local/bin/
CMD ["lambda-microvm-hook-server"]
```
### 2. Upload it to S3:
```sh
zip aether-microvm.zip Dockerfile
aws s3 cp aether-microvm.zip s3://YOUR_BUCKET/aether-microvm.zip
```
### 3. Create the microVM image
Replace the bucket, Region, account ID, and role name:
```sh
aws lambda-microvms create-microvm-image \
--name aether \
--code-artifact uri=s3://YOUR_BUCKET/aether-microvm.zip \
--base-image-arn arn:aws:lambda:YOUR_REGION:aws:microvm-image:al2023-1 \
--build-role-arn arn:aws:iam::YOUR_ACCOUNT_ID:role/YOUR_BUILD_ROLE \
--egress-network-connectors arn:aws:lambda:YOUR_REGION:aws:network-connector:aws-network-connector:INTERNET_EGRESS \
--hooks '{
"port": 9000,
"microvmImageHooks": {
"ready": "/aws/lambda-microvms/runtime/v1/ready",
"readyTimeoutInSeconds": 300
},
"microvmHooks": {
"run": "/aws/lambda-microvms/runtime/v1/run",
"runTimeoutInSeconds": 60,
"terminate": "/aws/lambda-microvms/runtime/v1/terminate",
"terminateTimeoutInSeconds": 30
}
}'
```
### 4. Wait for AWS to build your microVM image
```sh
aws lambda-microvms get-microvm-image --image-identifier aether
```
Wait until `state` is `CREATED`:
## Run your agent
Create a short-lived fine-grained GitHub token with **Contents: read/write** and **Pull requests: read/write**, then export it with your model provider's key:
```sh
export GH_TOKEN=...
export ZAI_API_KEY=...
```
The setup script is supplied dynamically for each run. This example clones the requested repository and passes the user's prompt directly to Aether:
```sh
cat > setup.sh <<'EOF'
set -eu
repository=$1
prompt=$2
workspace=/workspace/repository
gh auth setup-git
gh repo clone "$repository" "$workspace"
cd "$workspace"
exec aether headless --output text "$prompt"
EOF
```
Build the run payload. `OWNER/REPOSITORY` may be public or private as long as the token can access it.
```sh
jq -n \
--rawfile setup setup.sh \
--arg repository "OWNER/REPOSITORY" \
--arg prompt "Fix the failing login test, add a regression test, and open a pull request" \
--arg github_token "$GH_TOKEN" \
--arg zai_key "$ZAI_API_KEY" \
'{
command: "/bin/sh",
args: ["-c", $setup, "setup.sh", $repository, $prompt],
environment: {
GH_TOKEN: $github_token,
ZAI_API_KEY: $zai_key
}
}' > payload.json
```
Run the agent:
```sh
aws lambda-microvms run-microvm \
--image-identifier aether \
--egress-network-connectors arn:aws:lambda:YOUR_REGION:aws:network-connector:aws-network-connector:INTERNET_EGRESS \
--run-hook-payload file://payload.json \
--maximum-duration-in-seconds 3600
```
Aether clones the repository, completes the task, and opens the PR. Watch the image's `CloudWatch` logs or run `gh pr list --repo OWNER/REPOSITORY` to see when it appears.
When Aether finishes, the hook server exits and Lambda terminates the `MicroVM`. The maximum duration is only a guardrail for a stuck agent. The run payload contains credentials, so use narrowly scoped, short-lived tokens.