Chapter 4: Cloud & AWS Essentials
Master cloud infrastructure for ML — from EC2 GPU instances and S3 storage to IAM security, VPC networking, serverless inference, SageMaker training, and cost optimization strategies.
- Topics
- 8
- Videos
- 3
- Min read
- 77
Chapter Overview
Cloud computing transformed ML from a discipline limited by hardware budgets to one limited only by ideas. Before the cloud, training a large model required purchasing expensive GPU servers and waiting weeks for delivery. Today, you can launch 8x A100 GPUs in under a minute, train for a few hours, and tear them down — paying only for what you used.
This chapter covers the AWS services that every ML engineer needs to know. You will learn to launch GPU instances (EC2) for training, store datasets and model artifacts (S3), secure access with IAM policies, build isolated networks (VPC), deploy serverless inference endpoints (Lambda), use managed training infrastructure (SageMaker), and keep costs under control with billing alerts and spot instances.
The goal is practical fluency: by the end of this chapter, you will be able to take any ML project from your laptop to the cloud — training on GPU instances, storing checkpoints in S3, and deploying models as scalable endpoints — while keeping costs predictable and infrastructure secure.
Chapter Roadmap
Click any topic to jump in
Cloud Concepts
Why cloud beats on-prem for ML — pay-per-use compute, elastic scaling, regions, AZs, and the shared responsibility model.
EC2 runs the GPUs, S3 stores the data and models
EC2 & Compute
GPU instance families (p3/p4/g5), AMIs, EBS volumes, spot vs on-demand, and how to right-size training nodes.
S3 & Storage
Object storage for datasets and checkpoints — buckets, prefixes, storage classes, lifecycle rules, and 11-nines durability.
IAM controls who, VPC controls where
IAM & Security
Users, roles, policies, instance profiles — least-privilege access for training jobs and inference endpoints.
VPC & Networking
Private subnets, security groups, NAT gateways, VPC endpoints — isolating training clusters from the public internet.
Serverless for light traffic, SageMaker for managed ML
Lambda & Serverless
Event-driven inference, API Gateway, cold starts, container images — the cheapest path to production for low-traffic models.
ECR & SageMaker
Container registries for ML images, managed training jobs, hyperparameter tuning, and one-click model deployment.
Cost Management
Billing alerts, cost allocation tags, spot instances, and savings plans — keeping the AWS bill under control.
Topics
8 topics in this chapter
Before cloud computing, training a model meant buying servers, racking them, installing GPUs and running the network yourself. An 8-GPU server costs hundreds of thousands of dollars up front, and then sits idle whenever nobody is training. This chapter starts from that problem: how can an ML team get large amounts of compute exactly when it needs it, and pay nothing when it does not?
Cloud computing answers it by renting. Eight A100 GPUs on an AWS p4d.24xlarge cost about 22 dollars an hour on demand, so a 4-hour run costs under 90 dollars instead of a purchase. AWS, Google Cloud and Azure each offer hundreds of services. For ML engineers the core ones are compute, storage, networking and managed ML platforms. This topic sets up the vocabulary the rest of the chapter uses: the service models (IaaS, PaaS, SaaS), the physical layout of regions and availability zones, the shared responsibility model for security, and the tools that make infrastructure reproducible. Terraform defines it as code, and LocalStack lets you test against an emulator before paying for the real thing.
Definition
Cloud computing, in NIST's definition, is on-demand network access to a shared pool of configurable computing resources (servers, storage, networks, services) that can be provisioned and released quickly with minimal management effort. You pay for what you use, and the provider runs the physical hardware in regions made up of isolated availability zones.
In this topic
- 1On-Prem vs Cloud
- 2IaaS / PaaS / SaaS
- 3Regions
- 4Availability Zones
- 5Shared Responsibility Model
- 6Terraform
- 7LocalStack
On-Prem vs Cloud
Own hardware (high upfront cost, full control) vs rent hardware (pay-per-use, managed infrastructure, elastic scaling). On-prem makes sense when you have consistent, predictable GPU demand and the engineering team to manage infrastructure. Cloud makes sense for variable workloads, rapid experimentation, and teams that want to focus on ML rather than hardware.
Many ML teams use a hybrid approach: on-prem for always-running inference endpoints and cloud for burst training jobs that need 8+ GPUs for a few hours.
Owning has a fixed yearly cost and renting a variable one. Spread over years, on-prem costs a year, while the cloud costs for hours at hourly price . Break-even is . With the numbers above, hours, or 64 percent of the year. That is why the rule of thumb is about utilization rather than price. Steady, high-utilization workloads favor owning. Bursty experimentation favors renting, and so do teams that cannot keep a server busy most of the day.
Your ML team trains 4 hours a day, 5 days a week, on an 8-GPU A100 node. Compare buying a server (assume about 250,000 dollars up front, 40,000 dollars a year to operate, and a 3-year life) with renting a p4d.24xlarge on demand at about 21.96 dollars an hour.
IaaS / PaaS / SaaS
The three service models differ in how much of the stack you run yourself. With IaaS (Infrastructure as a Service, such as EC2), the provider runs the hardware and virtualization. You manage the operating system, drivers, runtime and code. With PaaS (Platform as a Service, such as SageMaker or Lambda), the provider also runs the OS and runtime, so you bring code and data. With SaaS (Software as a Service, such as a hosted model API), you only call an endpoint. Each step up removes operational work but also removes control. Most ML teams train on IaaS or PaaS and consume SaaS APIs where they fit.
Each abstraction layer trades control for operational burden along an inverse curve . IaaS gives maximum (custom kernels, drivers, networking) at maximum . SaaS minimizes but constrains . The optimal layer for a given workload minimizes total cost — most ML teams should use the highest layer their workload tolerates.
Regions
A region is a separate geographic area, such as us-east-1 in Northern Virginia or eu-west-1 in Ireland. Each region is independent, with its own services, prices and quotas. Four things drive the choice. Latency: light in fiber covers about 200 km per millisecond, so a user 6,000 km away pays at least 60 ms per round trip. Law: GDPR and similar rules can require personal data to stay in a jurisdiction. Price, which differs by region. And capacity: new GPU instance types often launch in only a few regions, so check availability before you choose. Data does not leave a region unless you copy it, and cross-region transfer is billed.
Cross-region latency follows the speed of light: in fiber. New York to Frankfurt () has a hard floor of one-way, so round-trip — before any processing. This is why placing inference endpoints near users matters more than CPU speed for latency-sensitive applications.
Availability Zones
Isolated data centers within a region — deploy across 2+ AZs for fault tolerance. Each AZ has independent power, cooling, and networking. AZs within a region are connected by low-latency links (< 2ms), but a failure in one AZ does not affect others.
For ML training, you typically run in a single AZ (training does not need multi-AZ redundancy). For ML inference endpoints, deploy across 2+ AZs so your API stays up even if one data center fails.
If each zone is unavailable a fraction of the time and failures are independent, a service in zones is down only when all are, with probability . At (99.5 percent), two zones give , about 13 minutes of downtime a year instead of 44 hours. The independence assumption is the weak point. Zones in a region share a control plane and software deployments, and regional incidents do happen, so the real figure is worse than . That is why AWS's multi-zone SLA promises 99.99 percent, not 99.9975.
Your ML inference API runs in us-east-1a only. A power outage in that AZ takes your service offline. How would you architect for fault tolerance?
Shared Responsibility Model
AWS secures the cloud (hardware, network, facilities). You secure what is IN the cloud (data, IAM, encryption, app code). This means AWS ensures the physical servers are secure and patched, but you are responsible for who can access your S3 buckets, what IAM policies your training instances use, and whether your data is encrypted.
The most common security mistakes are: overly permissive IAM policies, public S3 buckets, and hardcoded credentials in code.
Security failures decompose into provider-controlled and customer-controlled : total breach probability . AWS reduces to near zero via massive investment, so almost all real-world incidents come from — misconfigured S3 buckets, leaked IAM keys, overly permissive policies. The math says: spend your security effort on the customer side because that is where the residual risk lives.
Terraform
Terraform is an infrastructure-as-code tool: you describe resources such as VPCs, subnets, instances and buckets in .tf files, and Terraform makes the cloud match. terraform plan compares that desired state with its state file and the real cloud and prints a diff of what it would create, change or destroy. terraform apply then executes it. Because the configuration lives in Git, infrastructure changes get code review, history and rollback like any other code. The common failure modes are manual console edits, which cause drift that the next plan will try to undo, and a lost or shared-but-unlocked state file. Store state remotely with locking.
Infrastructure as code reduces drift by enforcing a declarative state that the system reconciles toward via . Terraform's plan-then-apply model converts unplanned manual changes (entropy) into a Markov chain where always converges back to on the next apply — making infrastructure idempotent and reviewable rather than a snowflake.
LocalStack
LocalStack is an emulator that runs AWS APIs such as S3, Lambda, SQS and DynamoDB inside a Docker container on your machine, by default on localhost port 4566. Point boto3 or the AWS CLI at that endpoint (endpoint_url) and the same code runs without an AWS account, without credentials and without a bill. That makes it useful for unit and integration tests in CI, and for developing data pipelines offline. The emulation is not perfect. IAM policies are not enforced by default, some services and edge cases are missing, and performance tells you nothing about production. Treat LocalStack as a fast first check, then test against a real staging account.
Developer iteration speed is dominated by the slowest feedback loop in the cycle. A round-trip to real AWS adds per API call plus deployment overhead; LocalStack drops this to . Over 100 daily test cycles, this saves of developer time per engineer per day — equivalent to several extra hours of focused work per week.
Theory Exercise
Problem:
Your ML startup processes medical imaging data for European hospitals. You need to train models on GPU clusters and serve predictions with < 100ms latency. Discuss which AWS region(s) you would choose, whether to go single-AZ or multi-AZ for training vs. inference, and what GDPR implications affect your architecture.
Hints:
- GDPR requires that EU citizen data stays within the EU unless specific legal mechanisms are in place
- Consider the trade-off between GPU availability (us-east-1 has the most GPU types) and data residency requirements
- Think about whether training and inference have different availability requirements
The previous topic explained why renting compute often beats buying it. The question here is what exactly to rent. Amazon EC2 (Elastic Compute Cloud) offers hundreds of instance types, and the cost of a wrong choice is large. A p4d.24xlarge with eight A100 GPUs costs about 22 dollars an hour, while a g5.xlarge with one A10G costs about 1 dollar. A model that fits on the small one wastes most of the big one.
The GPU families are the ones ML engineers meet most. p3 carries V100s and is the oldest. p4d has eight A100s with 40 GB each (p4de has the 80 GB version), and p5 has eight H100s. The g families (g5 with A10G, g6 with L4) are cheaper single- or few-GPU machines aimed at inference and light training. Pricing models change the bill as much as the hardware does. Spot instances sell spare capacity at a discount that varies by type (56 percent for p4d as this was written), but AWS can reclaim them with two minutes' notice. AMIs, key pairs, security groups and boto3 complete the toolkit for launching, reaching and automating these machines safely.
Definition
Amazon EC2 is AWS's service for renting virtual machines (instances) by the second. An instance type fixes the hardware: vCPUs, memory, GPUs, local storage and network bandwidth. You launch it from an AMI (machine image), reach it with a key pair, filter its traffic with security groups, and pay an on-demand, reserved or spot price.
In this topic
- 1EC2 Instance Types
- 2GPU Instance Families
- 3Spot Instances
- 4On-Demand vs Reserved vs Spot
- 5AMIs (Amazon Machine Images)
- 6Key Pairs
- 7Security Groups
- 8boto3
EC2 Instance Types
An instance type name reads family, generation, attributes and size. In p4d.24xlarge, p is the GPU-training family, 4 the generation, d means local NVMe disks, and 24xlarge the size. Common families are t and m for general purpose, c for compute-optimized, r for memory-optimized, and p, g and inf or trn for accelerators. Within a family, each size step roughly doubles vCPUs, memory and price. GPU counts do not follow the size names, though: g5.xlarge through g5.16xlarge all have one A10G, g5.12xlarge has four, and g5.48xlarge has eight. Read the spec table rather than inferring from the name, and pick the family whose strongest resource is your bottleneck.
Each family is cheapest for one resource. Divide the hourly price by the resource you are short of. A c5.2xlarge has 8 vCPUs for 0.34 dollars an hour, about 4.3 cents per vCPU-hour, while a p4d.24xlarge has 96 vCPUs for 21.96, about 23 cents. Its price pays for the GPUs. A CPU-bound preprocessing job on a p4d therefore pays roughly 5x too much for every core it uses, and the eight GPUs sit idle. Match the instance's strongest resource to the job's bottleneck, measured with a profiler rather than guessed.
GPU Instance Families
The GPU families split into training machines and inference machines. Training: p3 (V100 16 or 32 GB), p4d (8x A100 40 GB), p4de (8x A100 80 GB), and p5 (8x H100 80 GB). Their GPUs are linked by NVLink and NVSwitch, and the instances by EFA networking, both of which multi-GPU training needs. Inference and light training: g5 (A10G 24 GB, g5.xlarge about 1 dollar an hour) and g6 (L4 24 GB, about 0.80). Choose by memory first, since the model, optimizer state and activations must fit. Then choose by interconnect if you train across GPUs, and finally by price per useful hour.
A GPU's balance point is peak FLOPs divided by memory bandwidth. An H100 SXM offers about 989 dense FP16 TFLOPS and 3.35 TB/s, so a kernel needs about 295 FLOPs per byte moved before compute becomes the limit. Large matrix multiplies clear that easily: an 8192-cubed matmul in 16-bit does about 2,700 FLOPs per byte. Elementwise ops, normalization and small-batch decoding fall far below it and are bandwidth-bound. That is why memory capacity and bandwidth, not peak FLOPs, often decide between families for inference, while big-batch training cares about FLOPs and interconnect.
Spot Instances
60-90% cheaper than on-demand — AWS can reclaim with 2-minute warning. Perfect for checkpoint-based ML training. When AWS needs the capacity back, your instance gets a 2-minute warning via the instance metadata endpoint.
The strategy: save checkpoints to S3 every N epochs. If the spot instance is interrupted, launch a new one and resume from the latest checkpoint. For a 10-hour training job, you might be interrupted once or twice, adding 15-30 minutes but saving 70-90% on compute cost.
Since 2017, spot prices move gradually with long-term supply and demand instead of being set by bidding. Agmon Ben-Yehuda and colleagues found that even the earlier 'market' prices were largely set by a hidden reserve price. The expected cost of a checkpointed job is about . Here is interruptions per hour and is the work lost per interruption: half the checkpoint interval plus restart time. With hourly checkpoints, and a 40-minute average loss, overhead is about 3 percent, small next to a 50 to 90 percent discount.
A training job takes 8 hours on a p4d.24xlarge. Compare the cost using on-demand vs. spot instances, assuming one spot interruption that requires 20 minutes of overhead (re-launching + checkpoint recovery).
On-Demand vs Reserved vs Spot
The three EC2 pricing models trade commitment for discount. On-demand is the list price, billed per second, with no commitment. Reserved Instances and Savings Plans exchange a 1- or 3-year commitment for up to about 72 percent off, and suit the baseline you will certainly run, such as an always-on inference fleet. Spot uses spare capacity at a discount that varies by instance type and zone (often 50 to 90 percent), but AWS can reclaim it with two minutes' notice. Use it for checkpointed training and batch jobs. A typical mix commits the steady baseline, runs bursts on demand and puts fault-tolerant work on spot.
These three pricing models trade flexibility for discount along a Pareto frontier. On-demand: commitment, discount. Reserved: 1-3 year commitment, - discount. Spot: no commitment, - discount but interruptible. Optimal mix is — committed compute for the always-on baseline, on-demand for predictable bursts, spot for fault-tolerant batch.
AMIs (Amazon Machine Images)
An AMI is the boot image an instance launches from: an OS root volume snapshot plus launch settings. AWS's Deep Learning AMIs come with NVIDIA drivers, CUDA, cuDNN, NCCL, EFA drivers and PyTorch already installed and tested together. That saves hours of driver debugging and gives every team member the same stack. You can also bake your own AMI from a configured instance, adding your dependencies, so autoscaled instances start ready instead of running a long setup script. The tradeoffs are size and staleness. A large AMI boots more slowly because blocks load lazily from S3, and a baked image misses security patches unless you rebuild it on a schedule.
An AMI is a snapshot of state that EC2 instances boot from. Boot time scales as where is image size and is the EBS read bandwidth. Smaller AMIs (custom-baked vs full Deep Learning AMI) reduce boot time from to , which matters when autoscaling fleets where every minute of cold start is wasted compute.
Key Pairs
A key pair is how SSH proves your identity to a Linux instance. AWS stores the public key and injects it into the instance's authorized_keys at launch. You keep the private key, which AWS does not keep a copy of. Lose it and you lose SSH access to instances that only trust that key. Protect it: chmod 400 the file, since SSH refuses keys that others can read, and never commit it or share it between people. Increasingly you can skip long-lived keys entirely. EC2 Instance Connect pushes a short-lived key per session, and SSM Session Manager gives a shell with no open port 22.
Breaking the cryptography is not the realistic threat. An ED25519 key offers about 128-bit security and RSA-2048 about 112 bits. Even guesses a second for a century cover only about of the possibilities. Real compromises come from the file itself: a key committed to Git, copied to a shared drive or left on a stolen laptop. The useful security controls therefore limit where the key lives and how long it is trusted. Passphrases, per-person keys, short-lived Instance Connect keys and no inbound port 22 all do this.
Security Groups
A security group is a stateful virtual firewall attached to an instance's network interface. It contains only allow rules, each naming a protocol, port range and source (a CIDR block or another security group). Anything not allowed is dropped. Stateful means replies to allowed traffic flow back automatically, so you do not write return rules. A new group allows all outbound traffic and no inbound traffic. Open only what you need: SSH from your IP or VPN range, never 22 to 0.0.0.0/0, and your API port from the load balancer's group. Referencing groups instead of IP ranges keeps rules correct as instances come and go.
Security groups are stateful firewalls — outbound responses are always allowed. The set of reachable instances forms a graph where is instances and is allowed flows. Minimizing to only required edges reduces the blast radius of a compromised instance from (all instances) to its closed neighborhood — typically a - reduction in lateral movement potential.
boto3
boto3 is the AWS SDK for Python. It exposes every EC2 API, so a script can launch an instance with run_instances, wait for it with a waiter, tag it, and terminate it when training ends. That makes it the glue for automated ML workflows, such as a nightly job that launches a GPU spot instance, runs training from a container, uploads the model to S3 and terminates. Two pitfalls: list calls return pages, so use paginators, and always terminate in a finally block, or a crash leaves a GPU instance running.
boto3 wraps the AWS REST API in Python. Every call is an HTTPS request with round-trip latency from outside the region. Batching requests amortizes this overhead — a single list_objects_v2 returning objects is faster than individual head_object calls. The Paginator API hides this batching, but understanding it is essential for performant scripts.
Theory Exercise
Problem:
Your team needs to train a large vision transformer that requires at least 4x A100 GPUs and takes approximately 48 hours. Design a cost-optimized cloud strategy that handles spot interruptions, and calculate the estimated cost for on-demand, spot, and a spot-with-fallback approach.
Hints:
- Consider p4d.24xlarge (8x A100) or look at whether AWS offers 4-GPU configurations
- Spot interruptions average 1-3 per 48-hour period for GPU instances
- Think about checkpoint frequency: too frequent wastes training time, too infrequent loses progress
Compute from the previous topic is disposable: instances start, train and terminate. The data they read and the models they produce must outlive them. Amazon S3 (Simple Storage Service) is where ML teams keep it: datasets, checkpoints, logs, features and production model artifacts. It scales without capacity planning, is designed for eleven nines (99.999999999 percent) of durability, and every AWS service can read from it.
S3 stores objects in buckets. Despite paths like s3://my-bucket/models/v1/model.pt, the key space is flat: the slashes are part of the key name, and 'folders' are only a listing convention. That one fact explains how listing, prefixes and request scaling behave. Cost is the next lever. Storage classes trade price for access cost, from S3 Standard at about 2.3 cents per GB-month down to Glacier Deep Archive at about 0.1 cents. Lifecycle policies move objects between classes, or delete them, as they age. Versioning keeps old copies recoverable. Finally, the tools: aws s3 sync for bulk transfer, s3fs to mount a bucket, and boto3 for scripts and training pipelines.
Definition
Amazon S3 is an object store: a key-value service that stores objects (bytes plus metadata, up to 50 TB each) under unique keys inside buckets. It provides strong read-after-write consistency, very high durability, per-request pricing and storage classes priced by access pattern. Objects are read and written whole over HTTPS rather than edited in place like files.
In this topic
- 1Buckets
- 2Objects
- 3Storage Classes
- 4Lifecycle Policies
- 5Versioning
- 6aws s3 sync
- 7s3fs / s3fs-fuse
- 8boto3
Buckets
A bucket is the top-level container for objects, and the unit where you set region, access policy, versioning, encryption and lifecycle rules. Bucket names are global across all AWS accounts, because they appear in DNS names like name.s3.amazonaws.com. A name like 'datasets' is long gone, so use names such as company-ml-datasets. A bucket lives in one region, which matters for latency, transfer cost and data-residency law. Accounts get up to 10,000 general-purpose buckets by default. Teams usually split buckets by access boundary, such as raw data, curated datasets and production models, and use prefixes inside each bucket for projects. Block Public Access should stay on.
Bucket names live in one global namespace because they are DNS labels, so names are first-come, first-served across every AWS customer. Squatting on a name that your code expects is a real attack: if a deleted bucket's name is re-registered by someone else, writers that still point at it send them your data. Configuration is per bucket, so the number of buckets should follow the number of distinct access policies, not the number of projects. Prefixes give cheaper partitioning inside a bucket, and each prefix gets its own request-rate allowance.
Objects
An object is a blob of bytes plus metadata, stored under a key such as models/v2/resnet50.onnx. Objects can be up to 50 TB. The key space is flat: models/ is not a directory, just a shared prefix, which is why 'renaming a folder' means copying and deleting every object under it. Objects are immutable. To change one you write a new version of the whole object, and there are no appends or in-place edits. S3 has given strong read-after-write consistency since December 2020, so a read after a successful PUT always returns the new data.
Request throughput scales by prefix. S3 supports at least 3,500 writes and 5,500 reads per second per partitioned prefix, and it splits busy prefixes automatically as load grows. Ten prefixes read in parallel can therefore sustain about 55,000 GETs a second. The old advice to put random hashes at the front of keys predates 2018 and is no longer needed. The bigger cost for ML datasets is per-request latency. A million 100 KB images need a million GETs, while a few hundred 500 MB shards (WebDataset, TFRecord) need a few hundred. Shard small files before training.
Storage Classes
Storage classes trade storage price against access cost. In us-east-1, Standard costs about 0.023 dollars per GB-month with no retrieval fee, for data you read often. Standard-IA costs about 0.0125, but adds a per-GB retrieval fee and a 30-day minimum, for data read about monthly. Glacier Instant Retrieval costs 0.004 and Glacier Flexible Retrieval 0.0036, the latter with restores taking minutes to hours. Deep Archive costs 0.00099, with restores taking up to 12 hours and a 180-day minimum. Intelligent-Tiering moves objects automatically for a small monitoring fee. Choose by how often you read the data, not just how cold it feels. Repeatedly reading archived data quickly erases the savings.
Storage class pricing follows access frequency: where is monthly access count. Standard is optimal for , IA for , Glacier for . The break-even points come from per-GB pricing ratios — moving cold checkpoints from Standard (\sim \23/\text{TB-month}\sim $1/\text{TB-month}96%$ on storage if you only need them for compliance.
Your S3 bucket has 5 TB of training logs (accessed daily for 2 weeks, then never), 2 TB of model checkpoints (accessed occasionally for 6 months), and 500 GB of production models (accessed constantly). What storage class strategy minimizes cost?
Lifecycle Policies
A lifecycle configuration is a set of bucket rules that act on objects by age, filtered by prefix or tag. Transition actions move objects to cheaper classes, and expiration actions delete them. For example, logs/ moves to Standard-IA after 30 days, to Glacier Flexible Retrieval after 90, and expires after 365. Constraints matter: objects must spend 30 days in Standard before moving to an IA class. Each transition request has a cost, so moving millions of tiny objects can cost more than it saves. Archive classes bill minimum durations if you delete early. With versioning on, add rules for noncurrent versions too.
Lifecycle policies are Markov processes on objects: each object transitions through states (Standard IA Glacier deleted) based on age. The expected cost per object is where is the steady-state probability of being in state , is the per-GB cost there, and is the residence time. Tuning transition thresholds against access patterns can cut storage bills .
Versioning
With versioning enabled, every overwrite of a key creates a new version instead of replacing the bytes. A delete adds a delete marker that hides the object without removing earlier versions. You can list versions, read any one by its version ID, and restore an old one by copying it back on top. For ML artifacts this guards against the common accident of a training job writing model.pt over yesterday's good model. Every version is billed, though, so a checkpoint overwritten hourly accumulates hundreds of full copies. Pair versioning with a lifecycle rule that expires noncurrent versions after N days. For deliberate model versions, put the version in the key.
Versioning turns S3 from a mutable store into an append-only log where every PUT creates a new version. Storage cost grows as where is the size of each version, and a delete marker only hides the latest version. Lifecycle rules that expire old versions after days bound the storage growth to rather than unbounded accumulation.
aws s3 sync
aws s3 sync copies a directory tree between local disk and S3, or between buckets, transferring only files that are new or differ in size or modification time. Transfers run in parallel and large files use multipart upload, so syncing a dataset directory is far faster than a loop of single copies. Flags shape the result: --exclude and --include filter by glob (for example, skipping pycache and .pyc files), --delete removes destination files missing from the source, and --dryrun shows what would happen. Run --dryrun before any --delete, because a sync from the wrong directory with --delete will wipe the destination's extra files.
aws s3 sync computes the symmetric difference between local and remote, then transfers only the diff. Time complexity is for the listing phase plus for the transfer. For incremental dataset updates, this is dramatically cheaper than a full re-upload — a dataset with of changes syncs in seconds instead of hours.
s3fs / s3fs-fuse
s3fs-fuse mounts a bucket as a local directory, so unmodified code can call open() on S3 objects. It is convenient for browsing, small scripts and legacy tools, but the mapping from files to objects leaks. Each small read becomes an HTTP request with tens of milliseconds of latency. A random write or append rewrites the whole object. That makes it a poor training data loader: thousands of small images are read at network latency instead of SSD speed. For training, stage data to local NVMe, read large shards sequentially, or use a purpose-built client such as Mountpoint for Amazon S3.
The cost of a mounted bucket is latency times request count. With about 20 ms per small GET and one request per file, reading a million images sequentially takes seconds, about 5.5 hours. From local NVMe at about 0.1 ms each, it takes under 2 minutes. Parallelism helps, since 64 concurrent readers divide the time by roughly 64, but the fix that scales is fewer, larger requests: packing the images into 500 MB shards turns a million requests into a few hundred.
boto3
The boto3 S3 client gives scripts and pipelines direct access: upload_file and download_file for files, get_object and put_object for bytes, and list_objects_v2 (through a paginator, since each call returns at most 1,000 keys) for listings. upload_file and download_file use the transfer manager, which splits large files into parts and moves them concurrently. Tune it with TransferConfig through multipart_threshold, multipart_chunksize and max_concurrency. Generate presigned URLs to let a client upload or download one object without AWS credentials. On EC2, SageMaker or Lambda, let boto3 pick up the role's temporary credentials instead of passing keys.
Transfer time is bounded by the slowest of the per-connection rate times the number of connections, and the instance's network. A single S3 connection often moves around 100 MB/s, so a 100 GB checkpoint copied serially takes about 17 minutes. With max_concurrency of 16 and 64 MB parts, the transfer can approach a 10 Gb/s link's 1.25 GB/s and finish in under 2 minutes. Past the point where the network saturates, more concurrency only adds retries. So size concurrency to the instance's bandwidth.
Every resource from the previous topics, instances and buckets alike, is reachable through an API, and every API call is checked by AWS Identity and Access Management (IAM). IAM decides who can do what, so getting it wrong turns a single leaked credential or compromised notebook into a breach of the whole account.
The core objects are identities and policies. Users are long-lived identities for people or legacy tools, while roles are identities that services and people assume temporarily. Policies are JSON documents listing allowed or denied actions on resources, under conditions. Evaluation starts from deny: a request succeeds only if some policy allows it and none explicitly denies it. The guiding rule is least privilege, meaning each identity gets only the permissions its job needs. For ML teams that means a training job that can read the dataset bucket and write checkpoints but cannot delete production data, and an endpoint that can read model artifacts but cannot launch instances. Roles replace stored access keys, groups organize human permissions, aws-vault keeps local keys encrypted, and MFA guards the accounts that matter most.
Definition
AWS IAM is the service that authenticates principals (users, roles, federated identities) and authorizes each API request. It evaluates JSON policies naming the allowed or denied actions, the resources they apply to and optional conditions. A request is denied unless some policy allows it, and an explicit deny always wins.
In this topic
- 1IAM Users
- 2IAM Roles
- 3IAM Policies
- 4Principle of Least Privilege
- 5Managed Policies
- 6IAM Groups
- 7aws-vault
- 8MFA
IAM Users
An IAM user is a long-lived identity inside one account, with a console password, up to two access keys for the CLI and SDKs, or both. Each person gets their own user, never a shared login, so CloudTrail can attribute every action and offboarding means disabling one identity. Access keys never expire on their own, which makes them the most dangerous credential AWS issues. AWS's current guidance is to avoid IAM users for people where possible. Use federation or IAM Identity Center, which hand out temporary credentials, and keep users for the rare tool that can only take static keys.
A static key's risk grows with how long it lives. If a key leaks at a uniformly random moment in its life of days and nobody notices, an attacker has it for days on average. A key created years ago and never rotated gives an attacker years. Temporary role credentials cap the window at the session length, an hour by default. Detection helps, but leaked keys pushed to public repositories are scraped by automated bots, so assume a leaked key is used before anyone notices.
IAM Roles
A role is an identity with permissions but no long-term credentials. A trusted principal assumes it through STS and receives temporary credentials (an access key, secret and session token) that expire, typically after 1 hour and at most 12. Its trust policy says who may assume it, such as the EC2 service, a Lambda function, a GitHub OIDC identity or another account, and its permission policies say what it can do. Attach a role to a training instance through an instance profile, and boto3 fetches and refreshes credentials from the metadata service automatically. Nothing is written to disk, so there is nothing to commit, copy or forget to rotate.
Roles issue temporary credentials valid for via STS. The expected blast radius of a leaked credential is . Roles reduce lifetime by vs hardcoded keys (hours vs months/years), cutting the integral of risk over time by the same factor — making them the default identity for any non-human principal.
IAM Policies
A policy is a JSON document of statements. Each statement has an Effect (Allow or Deny), Actions such as s3:GetObject, Resources given as ARNs like arn:aws:s3:::ml-datasets/, and optional Conditions, such as requiring MFA or a source VPC endpoint. Identity policies attach to users, groups and roles. Resource policies, such as bucket policies, attach to the resource and name principals. AWS evaluates every applicable policy together. An explicit deny anywhere wins, otherwise any allow grants access, and anything not allowed is implicitly denied. A frequent bug comes from resource scope: s3:ListBucket applies to the bucket ARN, while object actions apply to bucket/.
Policies are JSON predicates evaluated against tuples. Evaluation is monotonic: adding a Deny only restricts, never expands. The least-privilege ideal is to find the minimal policy such that — every extra permission is a potential pivot point in a breach.
You need to create an IAM policy for a training instance that can read datasets from S3, write model checkpoints, and log to CloudWatch — but nothing else. Write the policy statement.
Principle of Least Privilege
Grant minimum permissions needed — start with zero access, add permissions as needed. This is the most important security concept in cloud computing. Instead of starting with admin access and trying to restrict it, start with no access and add only what each user or service needs.
For ML teams, this means: training instances can read data and write models, inference endpoints can read models and serve predictions, and only administrators can modify infrastructure.
Least privilege minimizes the cardinality of the permission set subject to the constraint that all required actions are allowed. Each unnecessary permission increases attack surface combinatorially: with permissions across resources, the action-resource matrix has cells, any of which a compromised principal can exploit. Halving roughly halves the breach blast radius.
Managed Policies
AWS managed policies, such as AmazonS3ReadOnlyAccess or AmazonSageMakerFullAccess, are written and updated by AWS for common job functions. They are quick to attach and stay current as services add actions, but they are broad by design. AmazonS3FullAccess grants every S3 action on every bucket in the account. Customer managed policies are ones you write: reusable, versioned and scoped to your buckets, regions and tags. Inline policies are embedded in a single identity and deleted with it. A sensible path is to prototype with an AWS managed policy, then replace it with a scoped customer policy, using IAM Access Analyzer to generate one from the actions CloudTrail shows were actually used.
AWS Managed Policies are pre-built sets of permissions maintained by AWS. They are convenient ( effort to attach) but tend to be over-permissive — AmazonS3FullAccess grants actions across all buckets in the account. The trade-off: managed policies trade fine-grained control for maintenance simplicity. Use them for rapid prototyping; tighten to inline policies for production.
IAM Groups
A group is a collection of IAM users that share attached policies, for example ml-engineers, data-scientists or admins. You manage permissions on the group, and membership decides who gets them. Adding a new hire to data-scientists grants the full set at once, and removing someone revokes it. Without groups, policies end up attached to individuals, and over time each person's permissions drift into a unique, unreviewable mix. Groups cannot be nested, cannot be principals in a resource policy, and cannot be assumed like roles. In organizations that use IAM Identity Center, permission sets assigned to directory groups play the same part for federated users.
Groups apply policies to sets of users in instead of per-user. The benefit is amortized: a single policy change propagates to all group members instantly. For a team of engineers, group-based policy management cuts the number of policy-attachment operations from per change to — a reduction in admin overhead and a smaller surface for policy drift.
aws-vault
aws-vault is an open-source CLI that stores your long-term AWS access keys in the operating system's keychain instead of plaintext in ~/.aws/credentials. For each command it requests short-lived STS credentials, optionally after an MFA prompt and a role assumption, and passes them to the process as environment variables, as in aws-vault exec ml-dev -- python train.py. Malware or a careless backup that reads your home directory finds no permanent key, and anything it captures from a running process expires within the hour. The tool protects keys on developer laptops. For servers and CI, use roles and OIDC instead, so there are no long-term keys to store at all.
aws-vault stores credentials in OS keychains and issues short-lived STS tokens on demand. Long-lived keys never sit in ~/.aws/credentials plaintext, eliminating the most common leak vector (accidental git commit, laptop theft). The math: keychain encryption leak probability drops from (plaintext) to (encrypted at rest, decrypted only on use).
MFA
Multi-factor authentication requires a second proof, such as a code from an authenticator app or a hardware security key, in addition to a password or access key. Enable it on the root user first, then on every human user. In IAM policies, the aws:MultiFactorAuthPresent condition key lets you require MFA for dangerous actions, for example denying ec2:TerminateInstances or s3:DeleteBucket without it. Phishing-resistant factors (FIDO2 security keys and passkeys) are stronger than one-time codes, because they are bound to the real sign-in domain and cannot be relayed through a fake login page. MFA does not protect access keys, which is another reason to prefer temporary credentials.
A second factor helps only to the extent that the two factors fail independently. If phishing captures a password with probability , adding a one-time code that the same fake page also relays leaves the risk near , because one attack defeats both. A FIDO2 key signs a challenge bound to the real domain, so a lookalike site gets nothing it can replay, and the attacker needs the physical key as well. That is why guidance on phishing-resistant MFA singles out security keys and passkeys over SMS or app codes for administrators and root.
IAM, from the previous topic, decides which API calls succeed. It does not decide which packets reach an instance. That is the network's job, and on AWS the network is a Virtual Private Cloud. A VPC is an isolated, private address space that you carve into subnets, connect to the internet only where you choose, and filter at two layers. A GPU instance in a well-built VPC cannot be reached from the internet at all, yet it can still read its dataset from S3 and push metrics.
The usual layout puts a public subnet in each availability zone for load balancers and NAT gateways, and private subnets for training instances, model servers and databases. Route tables decide where traffic goes. An internet gateway makes a subnet public, a NAT gateway lets private instances make outbound connections, and VPC endpoints carry traffic to S3 and other AWS services privately. Security groups filter traffic at each instance and are stateful. Network ACLs filter at each subnet and are stateless. Defining all of this in Terraform keeps every environment's network identical and reviewable.
Definition
An Amazon VPC is a logically isolated virtual network in one AWS region, defined by a private CIDR block such as 10.0.0.0/16. It is divided into subnets, each in one availability zone. Route tables, internet and NAT gateways, and VPC endpoints control where traffic can go, while security groups and network ACLs filter which traffic is allowed.
In this topic
- 1VPC
- 2Subnets
- 3Security Groups
- 4NACLs (Network ACLs)
- 5Internet Gateway
- 6NAT Gateway
- 7VPC Endpoints
- 8Terraform for VPC
VPC
A VPC is your private network inside one AWS region. When you create it you choose an IPv4 CIDR block, such as 10.0.0.0/16, which holds 65,536 addresses. Every subnet, instance and interface in the VPC takes its private address from that range. Nothing is reachable from the internet by default. Public access exists only where you add an internet gateway, a route to it and a public IP. Plan the CIDR before you build. It is hard to change later, and if two VPCs or an office network overlap, they cannot be peered or connected with a VPN.
CIDR arithmetic is powers of two. A /n prefix leaves host bits, so a block holds addresses: a /16 holds 65,536, a /20 holds 4,096 and a /24 holds 256. AWS reserves 5 addresses in every subnet (network, VPC router, DNS, one for future use, and broadcast), so a /24 gives 251 usable addresses. Size subnets for the biggest scale-out you expect plus headroom. Container platforms such as EKS assign a VPC IP to every pod, which exhausts a /24 far faster than counting instances suggests.
Subnets
A subnet is a slice of the VPC's CIDR block that lives in exactly one availability zone. 'Public' and 'private' describe routing, not a setting. A public subnet's route table sends 0.0.0.0/0 to an internet gateway, and a private subnet's either has no default route or sends it to a NAT gateway. The standard pattern is one public and one private subnet per zone, across at least two zones. Load balancers, bastions and NAT gateways go in the public subnets. Training instances, model servers and databases go in the private ones.
Subnets partition a VPC's CIDR into smaller blocks tied to a single AZ. A public subnet has a route to an Internet Gateway; a private subnet does not. The traffic flow requires both a route table entry AND a public IP. Private subnets force outbound traffic through a NAT Gateway, hiding instances behind a single egress IP and blocking unsolicited inbound connections.
Security Groups
In a VPC, security groups are the main per-instance firewall, and their key feature is that rules can reference other security groups instead of IP ranges. A rule such as 'allow TCP 5000 from api-sg' admits any interface that carries api-sg, wherever it is and however many there are, so autoscaled instances need no rule changes. Groups are stateful, so replies to allowed traffic return automatically, and they hold only allow rules, which are combined: traffic passes if any rule matches. Give each tier its own group (load balancer, API, model server, database) and chain them, so each tier accepts traffic only from the one in front of it.
Security groups are stateful (allow-list at instance level) — outbound responses are automatically allowed. The set of reachable instances forms a graph where edges are allowed flows. Minimizing reduces the blast radius of a compromised instance from (all instances) to its closed neighborhood , typically a - reduction in lateral movement.
Your ML inference API needs to accept HTTP traffic from the internet on port 8000 and connect to a model server on port 5000 in a private subnet. Define the security group rules.
NACLs (Network ACLs)
A network ACL is a stateless firewall attached to a subnet, evaluated before traffic reaches any security group. Rules are numbered and checked in order, the first match wins, and unlike security groups they can deny as well as allow. Stateless means return traffic is not remembered. If you allow inbound HTTPS, you must also allow outbound replies on the ephemeral ports (1024 to 65535). Forgetting this is the classic NACL outage. The default NACL allows everything, while a newly created custom NACL denies everything until you add rules. Most teams leave NACLs permissive and use them only for coarse subnet-wide blocks, such as denying a known-bad CIDR.
NACLs are stateless subnet-level firewalls (allow + deny rules). Stateless means return traffic needs explicit rules. Compared to stateful security groups, NACLs require as many rules (inbound + outbound for each flow) but provide a defense-in-depth layer at the subnet boundary — an attacker bypassing a security group still has to bypass the NACL.
Internet Gateway
An internet gateway is the VPC component that connects public subnets to the internet. It is horizontally scaled and highly available, and AWS imposes no bandwidth limit or hourly charge for it. It does two jobs. It is the target of a subnet's 0.0.0.0/0 route, and it performs one-to-one translation between an instance's private IP and its public or Elastic IP. Both conditions are needed for internet access: a subnet route to the gateway and a public IP on the instance. Either alone does nothing. One gateway attaches per VPC. Private instances that only need outbound access should use a NAT gateway instead.
An IGW provides bidirectional internet routing for instances with public IPs. It is a horizontally-scaled, fully-managed component with no bandwidth limits — unlike NAT Gateways which have per-AZ throughput caps. IGWs do not perform NAT; they require each instance to have its own public IP. The trade-off: simple at small scale, IP-exhaustion problems at large scale.
NAT Gateway
A NAT gateway lets instances in private subnets start outbound connections, for pip install, apt or calls to external APIs, while blocking any connection started from the internet. It sits in a public subnet with an Elastic IP, and private route tables send 0.0.0.0/0 to it. Each one lives in a single zone, so put one per zone to survive a zone failure and to avoid cross-zone transfer. It starts at 5 Gbps and scales automatically to 100 Gbps. It is also a classic cost trap: about 0.045 dollars an hour plus 0.045 per GB processed, including S3 traffic, unless a gateway endpoint carries that traffic instead.
NAT cost has two parts: about 0.045 dollars an hour (roughly 33 dollars a month) per gateway, plus 0.045 dollars per GB processed. A training cluster in a private subnet that pulls a 1 TB dataset from S3 through NAT pays about 45 dollars in processing for every pass. A gateway endpoint for S3 costs nothing and takes that traffic off the NAT path entirely. Daily re-downloads of 1 TB come to about 1,350 dollars a month through NAT and zero through the endpoint, so adding the endpoint is usually the first network cost fix.
VPC Endpoints
A VPC endpoint gives private subnets access to an AWS service without the internet, an internet gateway or a NAT gateway. Gateway endpoints exist for S3 and DynamoDB. They are entries in route tables and cost nothing. Interface endpoints, built on PrivateLink, place elastic network interfaces in your subnets for services such as ECR, CloudWatch, STS and SageMaker, and are billed per hour and per GB. Endpoint policies and bucket conditions like aws:sourceVpce let you require that a bucket only be reached through your endpoint. For ML, the S3 gateway endpoint is nearly mandatory: it removes NAT charges on dataset reads and keeps the traffic on AWS's network.
VPC endpoints route traffic to AWS services over AWS's private backbone instead of the public internet, eliminating NAT Gateway charges and reducing latency by . Gateway endpoints (S3, DynamoDB) are free; interface endpoints cost \sim \0.01/\text{hour}1000\times$ over.
Terraform for VPC
Writing the network in Terraform turns dozens of console clicks into reviewed code. A typical VPC needs the VPC itself, a public and a private subnet per zone, route tables and their associations, an internet gateway, NAT gateways, an S3 endpoint and security groups. That is easily 20 to 30 resources, wired together by references. The community terraform-aws-modules/vpc module packages that pattern, so dev, staging and production can share one definition and differ only in inputs such as the CIDR and the number of NAT gateways. terraform plan shows a reviewer exactly which rule or route changes, which is how you catch an accidental 0.0.0.0/0 before it ships.
Defining a VPC in Terraform converts manual click-ops (unrepeatable, error-prone, slow) into declarative code that can be reviewed, versioned, and re-applied. The math: a typical VPC has resources (VPC, subnets, route tables, IGW, NAT, SGs); Terraform applies them in dependency order in , vs of manual console clicks with high error rate.
Every compute option so far, EC2 included, makes you choose an instance size and pay while it runs, busy or idle. For work that arrives in bursts, such as resizing an image when it lands in S3, scoring a request a few times a minute or running a nightly cleanup, most of that time is idle. AWS Lambda removes the server from the picture. You upload a function and attach a trigger, and AWS runs as many copies as events require, from zero to the thousands. You pay per request and per millisecond of execution.
For ML engineers Lambda fits the glue around models: preprocessing triggered by uploads, routing and validation in front of an endpoint, scheduled jobs, and small CPU models such as gradient-boosted trees or compact ONNX networks. Its limits decide everything else. Memory goes up to 10 GB (with CPU scaling alongside), runs last at most 15 minutes, there are no GPUs, and an idle function pays a cold start when it is next invoked. Container images up to 10 GB fit larger dependencies. AWS SAM and the Serverless Framework turn functions, triggers and permissions into deployable configuration.
Definition
AWS Lambda is a serverless, event-driven compute service. You supply a function, as a zip package or a container image, plus its memory setting, and Lambda runs it in isolated, short-lived execution environments whenever a trigger fires. It scales concurrency automatically and bills per request and per GB-second of execution, up to 15 minutes per call.
In this topic
- 1Lambda Functions
- 2Triggers
- 3Cold Starts
- 4Layers
- 5Memory/Timeout Limits
- 6Container Image Support
- 7AWS SAM
- 8Serverless Framework
Lambda Functions
A Lambda function is a handler, such as def handler(event, context) in Python, plus configuration: runtime, memory, timeout, environment variables and an execution role. Each invocation gets an event (an HTTP request, an S3 notification, a queue batch) and returns a result. Lambda runs the handler in an execution environment, a Firecracker microVM, that it creates on demand, reuses for later calls and discards when idle. Concurrency scales by adding environments, up to a default limit of 1,000 per region. Pricing is 0.20 dollars per million requests plus about 0.0000167 dollars per GB-second. Functions must be stateless between calls, so keep state in S3, DynamoDB or a queue.
Lambda cost is requests plus compute: , with duration in seconds and memory in GB. A million 200 ms calls at 512 MB use 100,000 GB-seconds, about 1.67 dollars of compute plus 0.20 in requests, or 1.87 dollars, before the free tier of 400,000 GB-seconds a month. The comparison flips with steady load. At 50 requests a second all month, the same function costs about 240 dollars, while a small always-on instance that keeps up costs a few tens of dollars.
Triggers
A trigger connects an event source to a function, and the source sets the invocation mode. Synchronous sources, such as API Gateway, function URLs and the SDK's RequestResponse calls, wait for the result and return errors to the caller. Asynchronous sources, such as S3 notifications, EventBridge schedules and SNS, put the event on an internal queue, and Lambda retries failures twice before sending them to a dead-letter destination. Poll-based sources, such as SQS, Kinesis and DynamoDB streams, have Lambda read batches, and a failing batch is retried. Design handlers to be idempotent, because retries can deliver an event twice.
Lambda's event sources form a directed graph where events flow into the function and responses flow out. The throughput ceiling is . For S3 events, the default per-account concurrency cap of means Lambda can process uploads simultaneously — beyond that, events queue up and latency grows. Async triggers retry on failure; sync triggers (API Gateway) propagate the error to the caller.
Cold Starts
A cold start happens when no idle execution environment is available, so Lambda must create one. That means starting a microVM, downloading the code or image, starting the runtime and running your initialization code before the handler. Afterwards the environment stays warm for an unpublished, variable period and serves later calls without that cost. Cold starts are around 100 to 300 ms for a small Python function, but importing PyTorch or loading a model can add seconds. Load the model at module level, outside the handler, so the cost is paid once per environment rather than per request. Use provisioned concurrency to keep environments initialized when latency matters.
How many requests are cold depends on traffic, not on the function. With roughly steady arrivals, a busy function rarely needs a new environment, so the cold share is near zero. Shahrad and colleagues found most functions in Azure's production trace were invoked less than once an hour. For those, nearly every call is cold. Bursts also create new environments: a jump from 10 to 200 concurrent requests forces about 190 cold starts at once. Provisioned concurrency fixes the number of warm environments ahead of time, and you pay for them whether or not they are used.
Layers
A layer is a zip archive of libraries, a custom runtime or data that Lambda extracts into /opt for any function that references it. Layers let several functions share one copy of numpy, scikit-learn or a common utilities package, so deployment packages stay small and dependency upgrades happen in one place. They do not raise the size limit: function code plus all layers must fit in 250 MB unzipped. That is too small for PyTorch, and model weights do not belong in a layer anyway. Load weights from S3, or switch to a container image, which allows up to 10 GB. Layers are versioned, so pin a version number.
Layers separate dependencies from function code, deduplicating shared libraries across functions. A ML inference function with of model weights becomes a function plus a layer — and that layer can be reused across functions for a total of of unique storage instead of . Storage cost drops , deployment time drops proportionally.
Memory/Timeout Limits
Memory is the one sizing knob. It runs from 128 MB to 10,240 MB, and CPU is allocated in proportion: at 1,769 MB a function gets the equivalent of one full vCPU, and at the top setting about six. Timeout runs from 1 second to 15 minutes. For inference, more memory often lowers cost, because the extra CPU shortens each call by more than the price per millisecond rises. AWS Lambda Power Tuning measures this across settings. Anything that needs a GPU, more than 10 GB of RAM or runs longer than 15 minutes belongs on ECS, Batch or SageMaker.
Lambda allocates CPU proportional to memory: doubling memory roughly doubles vCPU. The cost-vs-latency Pareto curve has a sweet spot where higher memory speeds up the function enough to lower total cost (). For ML inference, this often lands around — below that, you are CPU-starved; above, you pay for unused memory. The -min timeout caps Lambda's use to short jobs; longer jobs need Step Functions or Fargate.
Container Image Support
Lambda can run a container image of up to 10 GB from ECR instead of a zip package. That lifts the 250 MB limit, so PyTorch, ONNX Runtime and their native libraries fit, and you can build with the same Dockerfile workflow you use elsewhere. The image must implement the Lambda Runtime API. The simplest route is an AWS base image such as public.ecr.aws/lambda/python, or adding the runtime interface client to your own. Lambda caches and lazily loads image chunks, so a large image does not necessarily start slower than a zip. Initialization code that imports heavy libraries or downloads weights still dominates cold starts.
Container Lambdas allow images up to vs the zip limit, unlocking PyTorch / TensorFlow inference. The trade-off is cold start time: zip Lambdas start in , container Lambdas in . Provisioned concurrency keeps containers warm at a flat hourly cost — worth it once .
AWS SAM
The AWS Serverless Application Model is an open-source framework for defining serverless applications in a template.yaml. It is an extension of CloudFormation: resource types such as AWS::Serverless::Function and AWS::Serverless::Api expand at deploy time into the full set of functions, roles, permissions and event mappings. sam build packages code and dependencies, sam local invoke and sam local start-api run functions in a local container for testing, and sam deploy creates or updates the CloudFormation stack. Because the template lives in Git and deploys through CloudFormation, changes are reviewable and roll back as a unit. It is AWS-only, which is the main tradeoff.
SAM (Serverless Application Model) is a transpiler that expands a small DSL into full CloudFormation. A -line SAM template produces a -line CloudFormation stack with all the implicit IAM roles, log groups, and event source mappings. The leverage ratio of makes SAM the practical choice for serverless infrastructure — defining the same stack in raw CloudFormation is tedious and error-prone.
Serverless Framework
The Serverless Framework is a third-party CLI that deploys functions from a serverless.yml file describing functions, events and resources. Running serverless deploy packages the code and, on AWS, generates and applies a CloudFormation stack, much as SAM does. Its strengths are a large plugin ecosystem, for example serverless-python-requirements for building native dependencies in Docker, and support for several cloud providers from one configuration format. Recent major versions need a Serverless account and a paid license above a free tier for larger organizations, so check the terms before standardizing on it. For AWS-only teams, SAM or Terraform avoids that dependency.
The Serverless Framework is a multi-cloud abstraction over Lambda, Cloud Functions, Azure Functions, etc. It provides plugins, local emulation, and deployment automation. The trade-off vs SAM: cross-cloud portability at the cost of an extra abstraction layer and dependency on the Serverless company. For AWS-only shops, SAM is simpler; for multi-cloud, Serverless Framework wins.
Related Problems on PixelBank
Lambda, from the previous topic, works for small models and glue code. It cannot train a large model or serve one that needs a GPU. This topic covers the AWS path for that work: package everything as a container, store it in a registry, and let a managed service run it on the right hardware.
Amazon ECR (Elastic Container Registry) is AWS's private Docker registry. Repositories are private by default, access goes through IAM, images can be scanned for known vulnerabilities, and SageMaker, ECS, EKS and Lambda pull from it with their roles. Lifecycle policies keep it from filling with images from every commit. Amazon SageMaker is the managed ML platform. A training job starts the instances you ask for, pulls your image, copies input data from S3 to a fixed path, runs your code, uploads the model directory back to S3 and shuts down. You pay only for that time, and managed spot training can cut it further. Endpoints serve the resulting model behind an autoscaling HTTPS API. Bringing your own container keeps your framework and code your own, while the Python SDK drives the workflow from a notebook or a CI job.
Definition
Amazon ECR is a managed, IAM-integrated container registry. Amazon SageMaker is AWS's managed ML platform: training jobs run a container on provisioned instances against S3 data and write the model artifacts back to S3, and endpoints host a model behind an autoscaling HTTPS API. Bring-your-own-container connects the two through the /opt/ml path conventions.
In this topic
- 1Amazon ECR
- 2ECR Lifecycle Policies
- 3SageMaker Training Jobs
- 4SageMaker Endpoints
- 5SageMaker Estimator
- 6Bring Your Own Container (BYOC)
- 7SageMaker Studio
- 8SageMaker SDK
Amazon ECR
ECR hosts private repositories of OCI and Docker images. Each image is addressed by a repository URI, account.dkr.ecr.region.amazonaws.com/name, plus a tag or digest. Authentication uses IAM: aws ecr get-login-password gives docker a 12-hour token, and AWS services pull with their own roles, so no registry password is stored anywhere. Images are encrypted at rest, can be scanned on push for known CVEs, and can be replicated to other regions or accounts. Storage costs about 0.10 dollars per GB-month. Pulls are free within the same region, but pulls from private subnets still go through NAT unless you add the ECR and S3 endpoints.
Registries store layers, not whole images. Two 5 GB images that share a 4 GB CUDA and PyTorch base cost about 6 GB of storage, not 10, and a node that already has the base pulls only the 1 GB that differs. Harter and colleagues measured why this matters: pulling accounted for 76 percent of container start time, yet only 6.4 percent of the pulled data was read. Order Dockerfile layers so the big, stable ones come first and are shared. Pull from ECR in the same region over a VPC endpoint, which avoids both NAT charges and cross-region transfer.
ECR Lifecycle Policies
A lifecycle policy is a set of prioritized JSON rules that ECR applies to a repository to expire images automatically. Rules select images by tag status (tagged, untagged, or tags matching a prefix or pattern) and by either count (imageCountMoreThan) or age (sinceImagePushed). For example, keep the 20 most recent images tagged with a release prefix, and expire untagged images after 1 day. A CI pipeline that pushes an image per commit needs this, or storage grows without limit. Use the console's preview to test a policy first, and never select by age alone on tags production depends on.
Without expiry, storage grows linearly with pushes. With a count rule, it is capped at about the retained count times the unique bytes per image. Because layers are shared, a new image usually adds only its changed layers, perhaps 200 MB of code and dependencies on top of a shared 5 GB base. One hundred retained images would then cost about 5 + 100 x 0.2 = 25 GB, or about 2.50 dollars a month, not 500 GB. The rule's real value is bounding the worst case, such as a base-image rebuild on every commit, which makes every layer unique.
SageMaker Training Jobs
A training job is one managed, ephemeral run. You specify the image, instance type and count, input channels in S3, hyperparameters, an output path and an IAM role. SageMaker provisions the instances, pulls the image, copies each channel to /opt/ml/input/data/<channel> (or streams it in fast-file or pipe mode), runs your entry point, then tars /opt/ml/model and uploads it to S3 before releasing the instances. Billing covers only the instances' running time, which removes the forgotten-instance failure. Managed spot training uses spare capacity at up to 90 percent off. Set a checkpoint S3 URI and resume from /opt/ml/checkpoints, because an interrupted job restarts from scratch otherwise.
SageMaker training jobs are ephemeral: spin up an instance, run your container, shut down. The total cost is where is wall-clock training time (including setup overhead ) and is the per-instance price. For jobs , the overhead is amortized; for jobs, the overhead dominates and you should batch multiple experiments per instance.
SageMaker Endpoints
A real-time endpoint hosts one or more models on instances you choose, behind an HTTPS API that you call with InvokeEndpoint. You pay per instance-hour whether or not traffic arrives. An endpoint configuration defines production variants, each a model on an instance type with an instance count and traffic weight, which enables A/B tests and canary shifts between model versions. Application Auto Scaling adjusts instance counts on metrics such as invocations per instance. Updating an endpoint uses blue-green deployment, with optional automatic rollback on CloudWatch alarms. For rare or bursty traffic, consider serverless inference or asynchronous inference instead, which can scale down to zero instances.
A real-time endpoint bills about per month for instances at hourly price , idle or not. Two ml.g5.xlarge instances run to a little over 2,000 dollars a month. Spread across 10 rarely used models through a multi-model endpoint, the per-model cost falls to about a tenth, in exchange for load latency when a cold model is first called. When traffic is a few requests an hour, serverless inference, which bills per millisecond of work, or batch transform is usually far cheaper.
SageMaker Estimator
The Estimator is the SageMaker Python SDK class that defines a training job. You construct it with an image URI, or a framework class such as PyTorch with a framework version and entry_point script, plus a role, instance type and count, hyperparameters, and options such as use_spot_instances, max_wait and checkpoint_s3_uri. Calling estimator.fit({'train': 's3://bucket/train'}) submits the job, streams its logs and returns when it finishes. estimator.model_data then holds the S3 URI of model.tar.gz, and estimator.deploy creates an endpoint from it. The abstraction is convenient, but it hides real API calls. Read the resulting job in the console to understand what it actually ran.
The Estimator API wraps training jobs in a Python class that handles container packaging, instance provisioning, hyperparameter passing, and artifact retrieval. The leverage: lines of Python replace lines of boto3 + IAM + S3 plumbing. The cost: opinionated abstractions that work great for standard training but require escape hatches for unusual setups.
Bring Your Own Container (BYOC)
Bring your own container means SageMaker runs your Docker image instead of one of its framework images. The contract is a set of paths and commands. For training, SageMaker runs the image with the argument train. Your code reads hyperparameters from /opt/ml/input/config/hyperparameters.json and data from /opt/ml/input/data/<channel>, writes the model to /opt/ml/model, and writes a failure message to /opt/ml/output/failure. For hosting, it runs serve, and your server must answer GET /ping and POST /invocations on port 8080. The sagemaker-training-toolkit implements the training side for you. BYOC is how you get unusual dependencies or custom CUDA builds while keeping managed spot, logging and lifecycle.
BYOC lets you run any container image as a SageMaker training job, as long as it follows the input/output convention (/opt/ml/input/, /opt/ml/model/, /opt/ml/output/). This converts SageMaker from a framework-locked PaaS into a managed batch executor — getting the auto-scaling, spot integration, and managed lifecycle without giving up your custom training code or dependencies.
SageMaker Studio
SageMaker Studio is a web IDE for ML, with JupyterLab and code-editor spaces running on instances you pick, CPU or GPU, plus panels for experiments, training jobs, pipelines, the model registry and endpoints. Spaces store work on a persistent volume, while the compute behind them can be stopped and restarted on a different instance type. That makes it easy to prototype on a small CPU instance and switch to a GPU only for the step that needs it. The common failure is cost. A GPU space left running overnight bills like any instance, so configure idle shutdown.
Studio is a cloud IDE for ML built on JupyterLab. It eliminates the local-vs-cloud impedance mismatch: notebooks, data, training jobs, and endpoints all live in the same managed environment. The math: time-to-first-experiment drops from (set up local env, install drivers, copy data) to (open Studio, open notebook, run cell).
SageMaker SDK
The SageMaker Python SDK is a high-level library over the SageMaker APIs. Estimators and framework classes launch training jobs, Model and Predictor classes deploy and call endpoints, Processing and Transformer classes run preprocessing and batch inference, and the workflow module defines SageMaker Pipelines. It handles plumbing such as uploading a local source directory to S3, picking framework image URIs for a region, and polling jobs, all of which you would otherwise script with boto3. The SDK's major versions have changed APIs, so pin the version in requirements.txt. For infrastructure that should be reproducible and reviewed, such as endpoints and roles, prefer Terraform or CloudFormation.
The SageMaker Python SDK is a high-level wrapper over the SageMaker REST API. It provides Pythonic abstractions (Estimator, Predictor, Pipeline) that hide CloudFormation/IAM/JSON complexity. The trade-off: high productivity for standard ML workflows, but escape hatches needed for non-standard setups (custom networking, multi-account deployments, exotic instance types).
The previous topics made it easy to start powerful resources with one command. This one is about the bill that follows. A p4d.24xlarge costs about 22 dollars an hour on demand, roughly 527 dollars a day or 16,000 dollars a month. A team where a few people each forget one GPU instance, or one NAT gateway quietly carrying all their S3 traffic, can lose a month's budget before anyone looks.
Cost management is about getting more experiments from the same budget, not about austerity. The pricing levers come first. On-demand prices are the baseline. Spot capacity sells at a discount, 56 percent for p4d as this was written, in exchange for interruptions. Reserved Instances and Savings Plans trade a 1- or 3-year commitment for up to 72 percent off. Then come the controls. Budgets and billing alerts catch runaway spend, though with a lag of hours. Cost Explorer shows where money goes, by service and by tag. Right-sizing matches instances to measured utilization. Infracost puts the price of a Terraform change on the pull request before it ships. The habits matter as much as the tools: tag everything, auto-stop idle GPUs and checkpoint spot jobs.
Definition
Cloud cost management is the practice of controlling spend by choosing the right pricing model for each workload (on-demand, spot or committed) and sizing resources to measured use. It relies on tagging spend to owners, setting budgets with alerts, and estimating the cost of infrastructure changes before they are deployed.
In this topic
- 1On-Demand Pricing
- 2Spot Instances
- 3Reserved Instances
- 4Savings Plans
- 5Billing Alerts
- 6AWS Cost Explorer
- 7Right-Sizing
- 8Infracost
On-Demand Pricing
On-demand is EC2's list price: no commitment, no upfront payment, billed per second for Linux after a one-minute minimum. Examples in us-east-1 are about 21.96 dollars an hour for a p4d.24xlarge, 1.01 for a g5.xlarge and 0.04 for a t3.medium. You pay for every second an instance is running, whether or not it does useful work. On-demand fits development, experiments of unknown length and short spikes. It also serves as the reference point for every other model's discount. Prices change, and AWS cut P4 and P5 prices by up to 33 and 45 percent in 2025, so read current figures rather than old notes.
On-demand cost is simply hours times rate, so the waste comes from hours, not the rate. An 8-GPU node left running for a 72-hour weekend costs dollars. That is about as much as running the same node for nine full 8-hour training days on spot at its current discount. Before negotiating discounts, measure the fraction of billed hours that do useful work. Idle development instances and forgotten notebooks are often the largest single line in an ML team's bill.
Spot Instances
Spot instances use EC2's spare capacity at a discount that varies by instance type, zone and time. The Spot Instance Advisor recently showed about 56 percent off for p4d.24xlarge and 86 percent for p3.2xlarge. In return, AWS can reclaim an instance with a two-minute warning, delivered through instance metadata and EventBridge. Spot pays only if interruptions are cheap. Checkpoint to S3 every 15 to 30 minutes, watch for the warning and save immediately, and resume automatically. Spread requests across several instance types and zones, because the scarcest GPU types are also the most frequently reclaimed.
Interruption cost is work lost plus restart time. With checkpoints every minutes, an interruption loses about minutes of work on average, plus a few minutes to relaunch and reload. For a 4-hour job at 0.05 interruptions an hour, you expect 0.2 interruptions. With and a 5-minute restart, that adds about minutes, under 2 percent. Without checkpoints, one interruption near the end loses nearly the whole job. Note that the Spot Advisor's interruption buckets are historical monthly frequencies, not guarantees.
Reserved Instances
A Reserved Instance is a billing discount, not a machine. You commit to an instance type in a region (Standard RI) or an exchangeable family (Convertible RI) for 1 or 3 years, paying all, part or nothing upfront, and matching usage is billed at the reduced rate. Savings can reach about 72 percent for a 3-year all-upfront Standard RI. The catch is that you pay for every committed hour whether or not it is used. That makes RIs right for a steady base load, such as an inference fleet that never scales below four instances, and wrong for experiments.
A commitment pays off only if you use it enough. With a discount , you pay of the on-demand rate for every hour, used or not. You therefore beat on-demand when utilization exceeds . At a 40 percent discount, break-even is 60 percent of hours, and at 60 percent, it is 40 percent. Commit only to the floor of your usage graph, the level you never drop below, and cover everything above it with on-demand or spot. Over-committing turns a discount into waste.
Savings Plans
A Savings Plan commits you to a fixed amount of compute spend per hour for 1 or 3 years. Matching usage up to that amount is billed at discounted rates, and anything above it at on-demand. Compute Savings Plans apply to EC2 in any family or region, and to Fargate and Lambda, with savings of up to 66 percent. EC2 Instance Savings Plans lock to one family in one region in exchange for up to 72 percent. The flexibility is the point: when you migrate from p4d to p5, a Compute Savings Plan keeps applying, while a Standard Reserved Instance for p4d would sit unused.
Size the commitment to the minimum of your hourly spend, not the average. If spend moves between 8 and 20 dollars an hour, an 8-dollar plan is fully used every hour. A 14-dollar plan, sized to the average, is under-used in every hour below 14, and the unused commitment is still billed. Cost Explorer's Savings Plans recommendations look back 7, 30 or 60 days and suggest the commitment that maximizes net savings. Re-run them before buying, and layer plans over time rather than committing everything at once.
Billing Alerts
Billing alerts tell you about spend before the invoice does. AWS Budgets lets you set cost or usage budgets, monthly, quarterly, daily or for specific tags, with notifications by email or SNS at actual or forecasted thresholds. Budget actions can go further and apply an IAM deny policy or stop specific instances when a threshold is crossed. The limit is latency. Budgets data refreshes up to three times a day, so an alert can arrive 8 to 12 hours after the spend. Pair budgets with faster controls, such as idle-instance shutdown and CloudWatch alarms on usage.
Alerts bound losses only to the extent that someone acts on them. Overspend is roughly burn rate times the sum of detection delay and reaction time. At 22 dollars an hour, a budget alert with a 12-hour data lag plus a 4-hour reaction costs about 350 dollars. Waiting for the monthly invoice costs about 16,000. Thresholds at 50, 80 and 100 percent of budget give warnings while there is still room to act. Automated actions remove the human reaction time from the sum, which is why budget actions and idle-shutdown alarms beat email alone.
A researcher forgets to terminate a p4d.24xlarge instance over a 3-day weekend. What is the cost, and how could billing alerts have prevented this?
AWS Cost Explorer
Cost Explorer is the billing console's analysis tool. It charts costs and usage over time, filters and groups them by service, linked account, region, instance type, usage type, purchase option or cost-allocation tag, and forecasts the coming months from past spend. Grouping by usage type is how you find surprises such as NAT gateway bytes, cross-region transfer or EBS snapshots. Grouping by a project or owner tag answers who spent what, provided tags are activated as cost-allocation tags and applied consistently. The same console offers rightsizing and Savings Plans recommendations. Review it weekly, and treat untagged spend as a bug to fix.
Cost anomalies stand out as differences, not totals. Comparing each service's spend with the same day last week, , removes weekly seasonality, and a jump in one service points at its cause. A spike in EC2-Other is often NAT or EBS, and a spike in data transfer is often a cross-region copy. AWS Cost Anomaly Detection automates this with a model of each service's normal spend and alerts on deviations. Its detection still depends on billing data that arrives hours late, so anomalies show up the same day, not the same minute.
Right-Sizing
Right-sizing means matching the instance to measured usage rather than guessing large. Collect CPU, memory, GPU utilization and GPU memory over representative periods. CloudWatch covers CPU, but memory needs the CloudWatch agent and GPU metrics need DCGM or nvidia-smi exporters. Then pick the smallest type that meets the latency or throughput target. AWS Compute Optimizer suggests changes for EC2, EBS, Lambda and ECS. For ML, the common wins are serving a model that fits in 24 GB on one g5 or g6 instead of a p-family node, halving an instance whose CPU sits below 40 percent, and moving to a newer generation that delivers more work per dollar.
Waste is the gap between paid and used capacity, times time. Within a family, each size step roughly halves price: m5.4xlarge costs 0.768 dollars an hour and m5.2xlarge 0.384. A service peaking at 35 percent CPU on the larger one would peak at about 70 percent on the smaller and save half. Rzadca and colleagues found Google's Autopilot cut the slack between limit and actual use from 46 percent for manually configured jobs to 23 percent. Manual sizing tends to overestimate, so measure before resizing.
Infracost
Infracost is an open-source tool that reads Terraform code or a plan, looks up cloud list prices, and reports the monthly cost of the resources and the difference a change makes. In CI it posts that diff as a pull-request comment, for example '+16,000 dollars a month: aws_instance.trainer changed from g5.xlarge to p4d.24xlarge', so reviewers see the cost of a change next to the code. It prices from list rates and resource attributes, so usage-based charges such as data transfer, requests and NAT processing need a usage file of estimates. It also ignores your discounts, so treat its numbers as a list-price guide.
Infracost computes the dollar cost of Terraform changes before they are applied, surfacing the financial impact in PR comments. The math: catching a cost mistake at PR time costs engineer-minute; catching it on the monthly bill costs hours of investigation plus the wasted spend. Infracost shifts cost decisions left, where they are cheap to fix.