This repository contains the infrastructure and code for a secure, distributed Small Language Model (SLM) inference system deployed on AWS. Utilizing Terraform for Infrastructure as Code (IaC) and Docker for containerization, the system runs the Gemma 3 270M model on private instances. Worker nodes communicate via the iii framework WebSocket RPC bus, while a public gateway exposes the service via an Nginx reverse proxy.
Note
Submission Status / Known Issues
The core infrastructure (Terraform provisioning, multi-subnet network isolation, NAT Gateway, and Nginx proxy routing) is fully operational. However, the inference-worker container currently faces a startup issue during initialization, meaning the end-to-end inference /v1/chat/completions API endpoint is not yet fully functional.
| Component | Host VM | Network Subnet | Runtime Stack | Responsibility |
|---|---|---|---|---|
| Nginx | vm-gateway |
Public | Native Service | Reverse proxy, forwards inbound port 80 traffic to iii Engine |
| iii Engine | vm-gateway |
Public | Rust Binary | Orchestrator, WebSocket RPC broker, HTTP API handler |
| Caller Worker | vm-gateway |
Public | Bun / TypeScript | Registers inference::get_response RPC, delegates calls |
| Inference Worker | vm-inference |
Private | Python / PyTorch | Loads Gemma 3 270M GGUF model, registers inference::run_inference |
The deployment isolates resources using a multi-subnet VPC design (10.0.0.0/16) in the ap-south-1 region:
- Public Subnet (
10.0.1.0/24): Hosts the gateway instance (vm-gateway). It is the sole public-facing node, allowing inbound SSH (port 22) from designated admin IPs and web traffic (port 80). - Private Subnet (
10.0.2.0/24): Houses the inference engine instance (vm-inference). This subnet has no direct internet ingress. Outbound traffic (e.g., pulling Docker images or model files) is securely routed through an AWS NAT Gateway residing in the public subnet. - Internal RPC Messaging: Communication between the gateway and inference worker utilizes local VPC routing via WebSocket (
ws://<gateway_private_ip>:49134). - Bastion Host Pattern: To ensure maximum security, the private inference instance cannot be accessed directly via SSH. Admin access requires tunneling/jumping through
vm-gatewayas a bastion host.
Below is the sequence diagram illustrating how a public HTTP API request triggers private distributed LLM inference:
sequenceDiagram
actor Client
participant Nginx as Nginx (Gateway:80)
participant Engine as iii Engine (Gateway:3111)
participant Caller as Caller Worker (Gateway:Docker)
participant Inference as Inference Worker (Private VM:Docker)
Client->>Nginx: POST /v1/chat/completions
Nginx->>Engine: Forward HTTP Request
Engine->>Caller: Trigger RPC Callback
Caller->>Engine: Send 'inference::run_inference' Event
Engine->>Inference: Route Event over WebSocket (Port 49134)
Note over Inference: Run Gemma 3 270M CPU inference
Inference->>Engine: Return Text Generation Result
Engine->>Caller: Return Response to Caller
Caller->>Engine: Complete Request
Engine->>Nginx: Send JSON Response
Nginx->>Client: Send HTTP 200 OK Response
- Endpoint:
POST /v1/chat/completions - Headers:
Content-Type: application/json
Sample Request:
curl -X POST http://<GATEWAY_PUBLIC_IP>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "Explain gravity in one sentence."}
]
}'Sample Response:
{
"result": {
"response": "Gravity is the force by which a planet or other body draws objects toward its center.",
"success": "Inference successfully completed via distributed RPC workers."
}
}alchemyst-devops-assignment/
├── docker/ # Dockerfiles for containerized services
│ ├── engine/ # Configuration for the iii engine
│ ├── caller-worker/ # TypeScript/Bun execution container
│ └── inference-worker/ # PyTorch/Python CPU Gemma image
├── nginx/ # Nginx reverse proxy configuration
├── quickstart/ # Local testing configuration & source files
│ ├── config.yaml # iii Engine orchestration settings
│ └── workers/ # Codebase for both caller & inference nodes
│ ├── caller-worker/
│ └── inference-worker/
├── scripts/ # Instance bootstrapping shell scripts
└── terraform/ # Infrastructure as Code resources
The system components are containerized and published on Docker Hub under the cotishq organization namespace:
- Orchestrator:
cotishq/alchemyst-engine(Houses theiiiengine binary and configuration) - API Router:
cotishq/alchemyst-caller(Bun + TypeScript caller-worker) - Inference Engine:
cotishq/alchemyst-inference(Python environment with PyTorch & Gemma 3 270M GGUF model)
- Installed and configured AWS CLI with valid credentials (
aws configure) - Terraform CLI (version
>= 1.3) - Docker Desktop / Engine (for local testing/building if modifying source images)
- An active AWS EC2 Key Pair in the
ap-south-1region
Ensure you are in the project root:
git clone https://github.com/cotishq/alchemyst-devops-assignment.git
cd alchemyst-devops-assignmentCreate a terraform.tfvars file inside the terraform directory with your specific AWS context:
cat <<EOF > terraform/terraform.tfvars
aws_region = "ap-south-1"
key_name = "your-ec2-keypair"
your_ip = "$(curl -s https://checkip.amazonaws.com)/32"
EOFInitialize and apply the configuration:
cd terraform
terraform init
terraform plan -out=tfplan
terraform apply tfplanTake note of the outputs: gateway_public_ip, gateway_private_ip, and inference_private_ip.
Wait approximately 3 minutes for cloud-init bootstrap scripts to finish setting up the systems.
SSH into the Gateway instance to launch the API and Orchestrator containers:
ssh -i ~/.ssh/your-key.pem ec2-user@<GATEWAY_PUBLIC_IP>
sudo -i
cd /opt/alchemyst
docker compose up -dConnect to the private inference instance through the Gateway instance, update the WebSocket address, and launch the inference service:
# SSH into Gateway first
ssh -i ~/.ssh/your-key.pem ec2-user@<GATEWAY_PUBLIC_IP>
# From the Gateway, SSH into the Private Inference VM
ssh -i ~/.ssh/your-key.pem ec2-user@<INFERENCE_PRIVATE_IP>
sudo -i
cd /opt/alchemyst
# Configure the private WebSocket target URL in docker-compose.yml
# Set III_URL to: ws://<GATEWAY_PRIVATE_IP>:49134
vi docker-compose.yml
# Launch the container
docker compose up -dTo tear down the AWS resources:
cd terraform
terraform destroyPrior to deploying this system to production, the following enhancements should be implemented:
- Transport Layer Security (HTTPS): Implement SSL/TLS termination by placing an Application Load Balancer (ALB) in front of Nginx, or using Caddy with Let's Encrypt certificates.
- Access Control & Auth: Protect the public
/v1/chat/completionsendpoint by adding API key authorization, JWT verification, or OAuth2 middleware at the Nginx ingress layer. - Dynamic Secrets Management: Secure connection strings, keys, and endpoints like
III_URLby retrieving them at runtime from AWS Secrets Manager or Systems Manager (SSM) Parameter Store. - Telemetry & Monitoring: Set up centralized log aggregation (e.g., AWS CloudWatch Logs via Fluent Bit) and monitoring metrics using Prometheus and Grafana (leveraging the OpenTelemetry hooks built into
iii). - Access Security (SSM): Disable open SSH port 22 entirely on security groups, and utilize AWS Systems Manager Session Manager for shell access to nodes.
- High Availability: Deploy instances across multiple Availability Zones (Multi-AZ) and place them inside Auto Scaling Groups (ASGs) to automatically recover from hardware failures.
- Payload Validation: Integrate JSON schema validation at the gateway level to reject bad requests early before they consume precious downstream LLM worker cycles.
Scaling to support massive models requires upgrading the infrastructure and compute paradigm:
- Accelerated Compute (GPUs): Migrate inference workloads from CPU instances to GPU-accelerated EC2 instances (e.g.,
g5.xlargewith NVIDIA A10G org6.xlarge). - Decoupled Model Storage: Instead of bundling massive weights in Docker images, store the GGUF/model weights on AWS Elastic File System (EFS) or S3, and mount/download them dynamically on instance boot.
- Dedicated Inference Engines: Replace standard PyTorch/Transformers pipelines with high-throughput engines like
vLLMorllama.cppserver to benefit from continuous batching, KV caching, and optimized kernels. - Asynchronous Processing: Decouple the request flow by putting an Amazon SQS queue between the gateway worker and the inference nodes. This prevents client timeouts during peak load or long generation times.
- Predictive Autoscaling: Configure Auto Scaling Groups for the inference worker pool based on SQS queue depth or custom CloudWatch metrics to dynamically handle load spikes.