ML Inference + Feature Store
AWS architecture template · Serverless ML serving with a dual feature store: Redis for low-latency online features and S3 for offline training set assembly via Athena.
ML Inference + Feature Store is a free AWS architecture template built with Lambda, S3, API Gateway, Athena, CloudWatch, DynamoDB, ElastiCache, IAM and Kinesis. Open it in TheArchitect to edit the diagram, estimate monthly cost, and export the CloudFormation or Terraform that deploys it.
AWS services in this template
- Lambda×2
- S3×2
- API Gateway
- Athena
- CloudWatch
- DynamoDB
- ElastiCache
- IAM
- Kinesis
Deployment notes
Needs input
- Upload model artifact to S3 before invoking model-server Lambda.
- Set Lambda environment variables for model S3 path and cache endpoint.
Generated CloudFormation — 13 resources
| Logical ID | Resource type |
|---|---|
| InferenceAPIapigateway | AWS::ApiGateway::RestApi |
| ModelServerlambda | AWS::Lambda::Function |
| PredictionCacheelasticache | AWS::ElastiCache::CacheCluster |
| OnlineFeatureStoredynamodb | AWS::DynamoDB::Table |
| ModelArtifactss3 | AWS::S3::Bucket |
| FeatureEventskinesis | AWS::Kinesis::Stream |
| FeatureWriterlambda | AWS::Lambda::Function |
| OfflineFeatureStores3 | AWS::S3::Bucket |
| TrainingSetBuilderathena | AWS::Athena::WorkGroup |
| InferenceRoleiam | AWS::IAM::Role |
| p99LatencyAlarmcloudwatch | AWS::CloudWatch::Alarm |
| ModelServerlambdaModelArtifactss3Permission | AWS::Lambda::Permission |
| FeatureWriterlambdaOfflineFeatureStores3Permission | AWS::Lambda::Permission |
View the full CloudFormation template
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Generated by TheArchitect — https://www.thecloudarchitect.dev",
"Metadata": {
"TheArchitect": {
"Edges": [
{
"From": "ModelServerlambda",
"To": "PredictionCacheelasticache",
"Label": "cache check"
},
{
"From": "ModelServerlambda",
"To": "OnlineFeatureStoredynamodb",
"Label": "fetch features"
},
{
"From": "ModelServerlambda",
"To": "ModelArtifactss3",
"Label": "load model"
},
{
"From": "FeatureEventskinesis",
"To": "FeatureWriterlambda",
"Label": "feature events"
},
{
"From": "FeatureWriterlambda",
"To": "OnlineFeatureStoredynamodb",
"Label": "upsert features"
},
{
"From": "FeatureWriterlambda",
"To": "OfflineFeatureStores3",
"Label": "write parquet"
},
{
"From": "OfflineFeatureStores3",
"To": "TrainingSetBuilderathena",
"Label": "training queries"
},
{
"From": "InferenceRoleiam",
"To": "ModelServerlambda",
"Label": "execution role"
},
{
"From": "InferenceRoleiam",
"To": "FeatureWriterlambda",
"Label": "execution role"
},
{
"From": "ModelServerlambda",
"To": "p99LatencyAlarmcloudwatch",
"Label": "duration metric"
}
]
}
},
"Resources": {
"InferenceAPIapigateway": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "ml-inference-api",
"Description": "ML Inference REST API",
"EndpointConfiguration": {
"Types": [
"REGIONAL"
]
}
}
},
"ModelServerlambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"InferenceRoleiam"
],
"Properties": {
"FunctionName": "ml-model-server",
"Runtime": "python3.12",
"Handler": "model.handler",
"MemorySize": 3008,
"Timeout": 30,
"Description": "Loads model from S3 on cold-start, checks Redis prediction cache, reads online features from DynamoDB, returns prediction.",
"Role": {
"Fn::GetAtt": [
"InferenceRoleiam",
"Arn"
]
}
}
},
"PredictionCacheelasticache": {
"Type": "AWS::ElastiCache::CacheCluster",
"Properties": {
"ClusterName": "ml-prediction-cache",
"Engine": "redis",
"EngineVersion": "7.1",
"CacheNodeType": "cache.t3.small",
"NumCacheNodes": 1,
"AtRestEncryptionEnabled": true,
"TransitEncryptionEnabled": true
}
},
"OnlineFeatureStoredynamodb": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "online-feature-store",
"BillingMode": "PAY_PER_REQUEST",
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
]
}
},
"ModelArtifactss3": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "ml-model-artifacts",
"VersioningConfiguration": {
"Status": "Enabled"
},
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": true,
"BlockPublicPolicy": true,
"IgnorePublicAcls": true,
"RestrictPublicBuckets": true
},
"NotificationConfiguration": {
"LambdaConfigurations": [
{
"Event": "s3:ObjectCreated:*",
"Function": {
"Fn::GetAtt": [
"ModelServerlambda",
"Arn"
]
}
}
]
}
},
"DependsOn": [
"ModelServerlambdaModelArtifactss3Permission"
]
},
"FeatureEventskinesis": {
"Type": "AWS::Kinesis::Stream",
"Properties": {
"Name": "ml-feature-events",
"StreamModeDetails": {
"StreamMode": "PROVISIONED"
},
"ShardCount": 2,
"RetentionPeriodHours": 24
}
},
"FeatureWriterlambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"InferenceRoleiam"
],
"Properties": {
"FunctionName": "ml-feature-writer",
"Runtime": "python3.12",
"Handler": "writer.handler",
"MemorySize": 512,
"Timeout": 120,
"Description": "Reads feature events from Kinesis, upserts DynamoDB online store, and appends Parquet to S3 offline store.",
"Role": {
"Fn::GetAtt": [
"InferenceRoleiam",
"Arn"
]
}
}
},
"OfflineFeatureStores3": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "ml-offline-feature-store",
"VersioningConfiguration": {
"Status": "Enabled"
},
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": true,
"BlockPublicPolicy": true,
"IgnorePublicAcls": true,
"RestrictPublicBuckets": true
},
"NotificationConfiguration": {
"LambdaConfigurations": [
{
"Event": "s3:ObjectCreated:*",
"Function": {
"Fn::GetAtt": [
"FeatureWriterlambda",
"Arn"
]
}
}
]
}
},
"DependsOn": [
"FeatureWriterlambdaOfflineFeatureStores3Permission"
]
},
"TrainingSetBuilderathena": {
"Type": "AWS::Athena::WorkGroup",
"Properties": {
"Name": "ml-training-set-builder",
"Description": "Assembles training datasets from offline feature store partitions",
"State": "ENABLED",
"WorkGroupConfiguration": {
"PublishCloudWatchMetricsEnabled": true,
"BytesScannedCutoffPerQuery": 10737418240,
"ResultConfiguration": {
"OutputLocation": "s3://ml-offline-feature-store/athena-results/"
}
}
}
},
"InferenceRoleiam": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": "ml-inference-role",
"Description": "Lambda execution role for model server and feature writer",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
]
}
},
"p99LatencyAlarmcloudwatch": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName": "ml-p99-latency-high",
"AlarmDescription": "Model server p99 duration > 2000 ms — check feature fetch or model load performance",
"Namespace": "AWS/Lambda",
"MetricName": "Duration",
"Period": 60,
"EvaluationPeriods": 5,
"Threshold": 2000,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "notBreaching",
"ExtendedStatistic": "p99"
}
},
"ModelServerlambdaModelArtifactss3Permission": {
"Type": "AWS::Lambda::Permission",
"DependsOn": [
"ModelServerlambda"
],
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "ModelServerlambda"
},
"Principal": "s3.amazonaws.com",
"SourceAccount": {
"Ref": "AWS::AccountId"
},
"SourceArn": {
"Fn::Sub": "arn:${AWS::Partition}:s3:::ml-model-artifacts"
}
}
},
"FeatureWriterlambdaOfflineFeatureStores3Permission": {
"Type": "AWS::Lambda::Permission",
"DependsOn": [
"FeatureWriterlambda"
],
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "FeatureWriterlambda"
},
"Principal": "s3.amazonaws.com",
"SourceAccount": {
"Ref": "AWS::AccountId"
},
"SourceArn": {
"Fn::Sub": "arn:${AWS::Partition}:s3:::ml-offline-feature-store"
}
}
}
}
}Generated by TheArchitect from the diagram above. Review values such as names, CIDR ranges and instance sizes before deploying to your account.
Related templates
- Twitter/X Scale TestCloudFront behavior split, API Gateway VPC Link, public/internal ALBs, private app/data subnets, queues, media, and data stores
- IoT Fleet Telemetry PipelineKinesis stream ingests device telemetry with three parallel Lambda consumers: anomaly detection, device shadow updates, and cold-path S3 archival for Athena analytics.
- CQRS + Event SourcingCommand and Query Responsibility Segregation with DynamoDB Streams as the event bus. Commands append immutable events; a projector builds a denormalised read model.