6
stars
16
commits
HCL
primary language
May 20, 2026
updated
A production-grade deployment of a distributed SLM (Small Language Model) inference system across multiple AWS EC2 instances. The system runs a Qwen3-0.6B model behind a worker mesh orchestrated by the iii framework, exposed as a JSON HTTP API through an Nginx reverse proxy.


| Component | VM | Subnet | Language | Function |
|---|---|---|---|---|
| iii Engine | VM1 | Public | Rust binary | Orchestrates workers, serves HTTP API |
| Nginx | VM1 | Public | — | Reverse proxy with rate limiting, security headers |
| Caller Worker | VM2 | Private | TypeScript | Routes HTTP requests → inference RPC calls |
| Inference Worker | VM3 | Private | Python | Loads Qwen3-0.6B model, runs inference |
ws://10.0.1.x:49134)curl http://<PUBLIC_IP>/health
{ "status": "ok" }
curl -X POST http://<PUBLIC_IP>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "Explain quantum entanglement in simple terms."}
]
}'
Response:
{
"result": {
"response": "Quantum entanglement is a phenomenon where two particles become linked...",
"success": "You've connected two workers and they're interoperating seamlessly..."
}
}
| Field | Type | Required | Description |
|---|---|---|---|
messages | Array | Yes | Chat messages in OpenAI-compatible format |
messages[].role | String | Yes | "user", "assistant", or "system" |
messages[].content | String | Yes | Message content |
aws configure)ap-south-1# 1. Clone the repo
git clone https://github.com/faizanfirdousi/alchemyst-assign.git
cd alchemyst-assign
# 2. Configure Terraform
cd terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars:
# key_name = "your-ec2-keypair"
# my_ip = "YOUR_PUBLIC_IP/32" ← run: curl -s ifconfig.me
# 3. Deploy
terraform init
terraform plan # Review what will be created
terraform apply # Create everything (~3-5 minutes)
# 4. Wait ~2-3 minutes for user-data scripts to complete, then test:
curl http://$(terraform output -raw api_gateway_public_ip)/health
curl -X POST http://$(terraform output -raw api_gateway_public_ip)/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello!"}]}'
# 5. Tear down when done
terraform destroy
| Resource | Count | Purpose |
|---|---|---|
| VPC | 1 | Isolated network (10.0.0.0/16) |
| Public Subnet | 1 | Hosts VM1 (10.0.1.0/24) |
| Private Subnet | 1 | Hosts VM2, VM3 (10.0.2.0/24) |
| Internet Gateway | 1 | Public subnet → internet |
| NAT Gateway | 1 | Private subnet → internet (outbound only) |
| Security Groups | 2 | API gateway (public) + Workers (private) |
| EC2 Instances | 3 | VM1 (t3.small), VM2 (t3.small), VM3 (c7i-flex.large) |
All services are containerized and pre-built on Docker Hub:
| Image | Size | Contents |
|---|---|---|
faizanfirdousi/iii-engine | 258 MB | iii engine binary + config |
faizanfirdousi/iii-caller-worker | 305 MB | Node.js 20 + TypeScript worker |
faizanfirdousi/iii-inference-worker | 2.1 GB | Python 3.11 + PyTorch (CPU) + Qwen3-0.6B model |
Images are automatically rebuilt and pushed by GitHub Actions when source code changes on main (see CI/CD below).
# From the repo root
docker build -f docker/engine/Dockerfile -t faizanfirdousi/iii-engine:latest .
docker build -f docker/caller-worker/Dockerfile -t faizanfirdousi/iii-caller-worker:latest .
docker build -f docker/inference-worker/Dockerfile -t faizanfirdousi/iii-inference-worker:latest .
docker push faizanfirdousi/iii-engine:latest
docker push faizanfirdousi/iii-caller-worker:latest
docker push faizanfirdousi/iii-inference-worker:latest
A GitHub Actions workflow handles the CI pipeline — automatically rebuilding and pushing Docker images whenever source code changes are pushed to main. The key design choice is selective rebuilding — instead of rebuilding all three images on every push, the pipeline detects which files changed and only rebuilds the image(s) that are actually affected.
Push to main
│
▼
Detect changed files (dorny/paths-filter)
│
├── workers/caller-worker/** changed? → rebuild iii-caller-worker
├── workers/inference-worker/** changed? → rebuild iii-inference-worker
└── config.yaml / iii.lock changed? → rebuild iii-engine
│
▼
Build with Docker Buildx + GitHub Actions layer cache
│
▼
Push updated image(s) to Docker Hub
Trigger — The workflow runs only when a push to main modifies files under workers/, docker/, config.yaml, iii.lock, or .iii/. Changes to Terraform, docs, or the README do not trigger a build.
Change detection — Uses dorny/paths-filter to compare the diff against path patterns for each image. This avoids wasting ~3 minutes rebuilding the 2.1 GB inference worker image when only the caller worker code changed.
Build & push — Each image is built conditionally using docker/build-push-action with GitHub Actions cache (type=gha), so unchanged Docker layers are reused across runs.
Add two repository secrets under Settings → Secrets and variables → Actions:
| Secret | Description |
|---|---|
DOCKERHUB_USERNAME | Docker Hub username |
DOCKERHUB_TOKEN | Docker Hub Personal Access Token (Read & Write) |
.
├── .github/workflows/
│ └── docker-publish.yml # CI/CD: auto-build images on push
├── config.yaml # iii engine configuration
├── iii.lock # Worker version lock
├── workers/
│ ├── caller-worker/ # TypeScript — routes HTTP → RPC
│ │ ├── src/worker.ts
│ │ └── package.json
│ └── inference-worker/ # Python — runs Qwen3-0.6B model
│ ├── inference_worker.py
│ └── requirements.txt
├── docker/
│ ├── engine/
│ │ ├── Dockerfile # iii engine container
│ │ └── nginx.conf # Nginx: rate limiting, security headers, health check
│ ├── caller-worker/
│ │ └── Dockerfile # Node.js 20 container
│ └── inference-worker/
│ └── Dockerfile # Python 3.11 + model pre-downloaded
├── docker-compose.vm1.yml # VM1: engine + nginx (public subnet)
├── docker-compose.vm2.yml # VM2: caller worker (private subnet)
├── docker-compose.vm3.yml # VM3: inference worker (private subnet)
├── terraform/
│ ├── main.tf # Provider + AMI data source
│ ├── variables.tf # Configurable inputs
│ ├── vpc.tf # VPC, subnets, IGW, NAT, route tables
│ ├── security_groups.tf # Firewall rules
│ ├── ec2.tf # 3 EC2 instances + user-data
│ ├── outputs.tf # Public IP + curl commands
│ ├── terraform.tfvars.example
│ └── user-data/
│ ├── vm1.sh.tpl # VM1 bootstrap (15 lines)
│ └── worker.sh.tpl # VM2/VM3 bootstrap (ENGINE_IP injected)
└── README.md
state_store.db to an EBS volume.For a 60B-parameter model (~120 GB in FP16):
p4d.24xlarge (8× A100 80 GB) with tensor parallelism; serve via vLLM or TGI for batched inference.16 commits
HCL
59.0%
Python
14.9%
Dockerfile
13.0%
TypeScript
6.8%
Shell
6.3%
6
stars
16
commits
HCL
primary language
May 20, 2026
updated
A production-grade deployment of a distributed SLM (Small Language Model) inference system across multiple AWS EC2 instances. The system runs a Qwen3-0.6B model behind a worker mesh orchestrated by the iii framework, exposed as a JSON HTTP API through an Nginx reverse proxy.


| Component | VM | Subnet | Language | Function |
|---|---|---|---|---|
| iii Engine | VM1 | Public | Rust binary | Orchestrates workers, serves HTTP API |
| Nginx | VM1 | Public | — | Reverse proxy with rate limiting, security headers |
| Caller Worker | VM2 | Private | TypeScript | Routes HTTP requests → inference RPC calls |
| Inference Worker | VM3 | Private | Python | Loads Qwen3-0.6B model, runs inference |
ws://10.0.1.x:49134)curl http://<PUBLIC_IP>/health
{ "status": "ok" }
curl -X POST http://<PUBLIC_IP>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "Explain quantum entanglement in simple terms."}
]
}'
Response:
{
"result": {
"response": "Quantum entanglement is a phenomenon where two particles become linked...",
"success": "You've connected two workers and they're interoperating seamlessly..."
}
}
| Field | Type | Required | Description |
|---|---|---|---|
messages | Array | Yes | Chat messages in OpenAI-compatible format |
messages[].role | String | Yes | "user", "assistant", or "system" |
messages[].content | String | Yes | Message content |
aws configure)ap-south-1# 1. Clone the repo
git clone https://github.com/faizanfirdousi/alchemyst-assign.git
cd alchemyst-assign
# 2. Configure Terraform
cd terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars:
# key_name = "your-ec2-keypair"
# my_ip = "YOUR_PUBLIC_IP/32" ← run: curl -s ifconfig.me
# 3. Deploy
terraform init
terraform plan # Review what will be created
terraform apply # Create everything (~3-5 minutes)
# 4. Wait ~2-3 minutes for user-data scripts to complete, then test:
curl http://$(terraform output -raw api_gateway_public_ip)/health
curl -X POST http://$(terraform output -raw api_gateway_public_ip)/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello!"}]}'
# 5. Tear down when done
terraform destroy
| Resource | Count | Purpose |
|---|---|---|
| VPC | 1 | Isolated network (10.0.0.0/16) |
| Public Subnet | 1 | Hosts VM1 (10.0.1.0/24) |
| Private Subnet | 1 | Hosts VM2, VM3 (10.0.2.0/24) |
| Internet Gateway | 1 | Public subnet → internet |
| NAT Gateway | 1 | Private subnet → internet (outbound only) |
| Security Groups | 2 | API gateway (public) + Workers (private) |
| EC2 Instances | 3 | VM1 (t3.small), VM2 (t3.small), VM3 (c7i-flex.large) |
All services are containerized and pre-built on Docker Hub:
| Image | Size | Contents |
|---|---|---|
faizanfirdousi/iii-engine | 258 MB | iii engine binary + config |
faizanfirdousi/iii-caller-worker | 305 MB | Node.js 20 + TypeScript worker |
faizanfirdousi/iii-inference-worker | 2.1 GB | Python 3.11 + PyTorch (CPU) + Qwen3-0.6B model |
Images are automatically rebuilt and pushed by GitHub Actions when source code changes on main (see CI/CD below).
# From the repo root
docker build -f docker/engine/Dockerfile -t faizanfirdousi/iii-engine:latest .
docker build -f docker/caller-worker/Dockerfile -t faizanfirdousi/iii-caller-worker:latest .
docker build -f docker/inference-worker/Dockerfile -t faizanfirdousi/iii-inference-worker:latest .
docker push faizanfirdousi/iii-engine:latest
docker push faizanfirdousi/iii-caller-worker:latest
docker push faizanfirdousi/iii-inference-worker:latest
A GitHub Actions workflow handles the CI pipeline — automatically rebuilding and pushing Docker images whenever source code changes are pushed to main. The key design choice is selective rebuilding — instead of rebuilding all three images on every push, the pipeline detects which files changed and only rebuilds the image(s) that are actually affected.
Push to main
│
▼
Detect changed files (dorny/paths-filter)
│
├── workers/caller-worker/** changed? → rebuild iii-caller-worker
├── workers/inference-worker/** changed? → rebuild iii-inference-worker
└── config.yaml / iii.lock changed? → rebuild iii-engine
│
▼
Build with Docker Buildx + GitHub Actions layer cache
│
▼
Push updated image(s) to Docker Hub
Trigger — The workflow runs only when a push to main modifies files under workers/, docker/, config.yaml, iii.lock, or .iii/. Changes to Terraform, docs, or the README do not trigger a build.
Change detection — Uses dorny/paths-filter to compare the diff against path patterns for each image. This avoids wasting ~3 minutes rebuilding the 2.1 GB inference worker image when only the caller worker code changed.
Build & push — Each image is built conditionally using docker/build-push-action with GitHub Actions cache (type=gha), so unchanged Docker layers are reused across runs.
Add two repository secrets under Settings → Secrets and variables → Actions:
| Secret | Description |
|---|---|
DOCKERHUB_USERNAME | Docker Hub username |
DOCKERHUB_TOKEN | Docker Hub Personal Access Token (Read & Write) |
.
├── .github/workflows/
│ └── docker-publish.yml # CI/CD: auto-build images on push
├── config.yaml # iii engine configuration
├── iii.lock # Worker version lock
├── workers/
│ ├── caller-worker/ # TypeScript — routes HTTP → RPC
│ │ ├── src/worker.ts
│ │ └── package.json
│ └── inference-worker/ # Python — runs Qwen3-0.6B model
│ ├── inference_worker.py
│ └── requirements.txt
├── docker/
│ ├── engine/
│ │ ├── Dockerfile # iii engine container
│ │ └── nginx.conf # Nginx: rate limiting, security headers, health check
│ ├── caller-worker/
│ │ └── Dockerfile # Node.js 20 container
│ └── inference-worker/
│ └── Dockerfile # Python 3.11 + model pre-downloaded
├── docker-compose.vm1.yml # VM1: engine + nginx (public subnet)
├── docker-compose.vm2.yml # VM2: caller worker (private subnet)
├── docker-compose.vm3.yml # VM3: inference worker (private subnet)
├── terraform/
│ ├── main.tf # Provider + AMI data source
│ ├── variables.tf # Configurable inputs
│ ├── vpc.tf # VPC, subnets, IGW, NAT, route tables
│ ├── security_groups.tf # Firewall rules
│ ├── ec2.tf # 3 EC2 instances + user-data
│ ├── outputs.tf # Public IP + curl commands
│ ├── terraform.tfvars.example
│ └── user-data/
│ ├── vm1.sh.tpl # VM1 bootstrap (15 lines)
│ └── worker.sh.tpl # VM2/VM3 bootstrap (ENGINE_IP injected)
└── README.md
state_store.db to an EBS volume.For a 60B-parameter model (~120 GB in FP16):
p4d.24xlarge (8× A100 80 GB) with tensor parallelism; serve via vLLM or TGI for batched inference.16 commits
HCL
59.0%
Python
14.9%
Dockerfile
13.0%
TypeScript
6.8%
Shell
6.3%