AWS Core Services and Architecture Questions
Amazon Web Services' core service catalog and how the pieces compose into a working system: EC2, Lambda, S3, VPC, IAM, RDS, and the managed-service ecosystem. Covers service selection within AWS, common reference architectures, the AWS Well-Architected Framework pillars, and operational patterns specific to the platform. For provider-agnostic compute or storage trade-offs, see the cross-cloud entries.
What causes AWS Lambda cold starts? Walk through the factors that influence cold-start latency (package size, runtime, VPC networking) and at least three practical mitigation strategies.
Sample Answer
A cold start is the latency added when Lambda has no warm execution environment available and must create one from scratch before running the handler: pull and unpack the deployment package, start the language runtime, run any extensions, and execute module-level/global initialization code. A warm invocation skips all of that and only runs the handler. The main mitigations are Provisioned Concurrency (or SnapStart, where supported) to remove the cold path entirely, and shrinking what has to happen during init.
What drives cold-start latency
- Package size: a larger deployment artifact takes longer to fetch and unpack before the runtime can even start.
- Runtime choice: interpreted/lighter runtimes (Node.js, Python, Go via provided.al2023) generally start faster than managed runtimes with heavier startup (Java's JVM warm-up and classloading, .NET's CLR init), unless a snapshot-based mechanism is used to skip that cost.
- VPC networking: Lambda's shared Hyperplane ENI (Elastic Network Interface) model removed most of the old per-invocation ENI-creation penalty, but VPC-attached functions can still add first-attach latency and are worth measuring rather than assuming are cheap.
- Memory allocation: CPU scales with configured memory, so a low-memory function can have a slower init in addition to slower execution.
- Init code: expensive global-scope work (constructing SDK clients, opening DB connections, loading a large in-memory model) all runs during the cold path.
Mitigation strategies
- Provisioned Concurrency: pre-initializes a set number of execution environments so invocations land warm. It eliminates cold starts for that reserved headroom but is billed as standing capacity, so it needs to be paired with Application Auto Scaling (scheduled or target-tracking) to track the real traffic shape, or it either overpays for idle capacity or still cold-starts above the provisioned count.
- SnapStart: snapshots an already-initialized execution environment and restores from that snapshot on subsequent cold starts, avoiding repeated init work. It launched for Java and AWS has since extended it to additional managed runtimes; check current per-runtime support before depending on it for a given language, and be aware that anything generated at init time that must be unique per environment (connection handles, random seeds, unique IDs) needs to be explicitly re-initialized after a snapshot restore, not just reused.
- Shrink package and init cost: trim dependencies, avoid bundling unused code, and move rarely-used imports out of the module's top level so they're only loaded when actually needed.
- Right-size memory: because CPU is tied to memory, raising memory can shorten both init and execution time, sometimes lowering total cost despite the higher per-ms rate. This has to be measured per function, not assumed.
- Minimize unnecessary VPC attachment: only attach a function to a VPC when it needs to reach VPC-only resources (e.g., RDS), and use RDS Proxy or VPC endpoints so the shared ENI cost is amortized rather than paid fresh per function.
- Function decomposition, with a caveat: splitting one large multi-purpose function into smaller single-purpose functions reduces each function's package and init size, but it also multiplies the number of cold-start-prone entry points and adds inter-function invocation latency and orchestration complexity. It's a genuine trade-off, not a free win.
Measuring cold-start impact before committing to a fix
Lambda reports an Init Duration in the REPORT log line (and in traces from X-Ray, AWS's request-tracing tool), which is the only reliable way to confirm a latency spike is actually a cold start rather than something else in the request path. To quantify a candidate mitigation, run a controlled comparison: deploy two versions of the same function (for example, Provisioned Concurrency on vs. off, or Node.js vs. Java for the same logic), drive synthetic traffic that's guaranteed to exceed the current warm pool so cold environments are forced on each run, and compare P50/P99 Init Duration and total duration between the versions. That turns "we think this will help" into a measured decision for the specific workload, instead of applying folklore.
Trade-offs and pitfalls
- Provisioned Concurrency without autoscaling either overpays for idle capacity or fails to cover unscheduled bursts; it needs to track the actual traffic shape.
- SnapStart's runtime coverage and its "regenerate anything unique post-restore" requirement are easy to get wrong the first time; verify both before relying on it.
- Decomposing a function purely to shrink cold starts trades fewer, bigger cold starts for more, smaller ones plus added orchestration.
- Optimizing cold starts on a function that's mostly warm under real traffic is wasted effort; measure the actual cold-start rate for the workload before spending engineering time on it.
Explain the differences between S3, EBS, and EFS: storage semantics (object/block/file), durability, typical access patterns, and cost model. For a three-tier app storing user uploads, application binaries, and a database, which would you use for each layer?
Sample Answer
Direct answer
S3 is object storage: you PUT and GET whole objects over HTTP, there's no file system underneath, and it's built for massive scale and durability rather than low latency. EBS (Elastic Block Store) is block storage: a virtual disk attached to a single EC2 instance, the same abstraction as a local SSD, used for anything that needs a real file system and fast random I/O. EFS (Elastic File System) is a managed NFS (Network File System): POSIX file semantics, but shared, so many instances or containers can mount and read/write the same file system at once. For a three-tier app: S3 for user uploads, EBS for the database, and EFS if application binaries or shared assets need to be visible to multiple instances at once, EBS if they don't.
Structured elaboration
| S3 (object) | EBS (block) | EFS (file) | |
|---|---|---|---|
| Access pattern | HTTP API (GET/PUT/DELETE on whole objects) | Block device, mounted as a disk by one EC2 instance (or a small number, with Multi-Attach, in narrow cases) | POSIX file system, mounted over NFS by many clients concurrently |
| Durability | 11 nines of object durability, redundant across multiple Availability Zones (AZs) by design | Replicates within a single AZ; snapshots to S3 give you durability across AZs | Redundant across multiple AZs within a region by design |
| Typical latency character | Higher latency, optimized for throughput and massive parallel access | Low, consistent latency, this is what makes it fit for databases | Between the two; still higher than local block storage |
| Cost model | Pay per GB stored plus per-request, with lifecycle tiers to cut cost on cold data | Pay per provisioned GB, plus IOPS on some volume types | Pay per GB actually stored (no pre-provisioning required by default) |
| Where it's tied to compute | Not tied to any instance | Tied to a single AZ, and normally a single attached instance | Not tied to any single instance |
Container and multi-instance workloads: this is where the block-vs-file distinction shows up sharply. An EBS volume is fundamentally single-attach: one EC2 instance, or one pod through the EBS CSI (Container Storage Interface) driver, can write to it at a time (ReadWriteOnce). EFS, mounted through the EFS CSI driver, supports many pods reading and writing the same file system concurrently (ReadWriteMany), which is why it's the standard choice for shared config, shared uploads directories, or CI/build-runner workspaces across a container fleet, and EBS is the standard choice for a single stateful pod's own local-disk-equivalent storage.
EFS throughput modes: Bursting throughput scales with how much data you have stored, backed by burst credits, fine for spiky-but-moderate workloads. Provisioned throughput lets you set a fixed throughput independent of storage size, for consistently high-throughput needs. Elastic throughput automatically scales up and down with your actual workload without you provisioning anything, and is the default recommendation today for unpredictable or spiky access patterns, since it removes the need to guess a provisioned number or babysit burst-credit balances.
Worked example
For a typical three-tier app (web/app tier, storage for uploads, database):
- User uploads (photos, documents, exports): S3. They're immutable-ish blobs accessed by key, don't need POSIX semantics, and S3's per-request pricing and lifecycle tiers fit "write once, read occasionally" access well.
- Application binaries: depends on deployment model. If instances are deployed from a container image or AMI (Amazon Machine Image), binaries usually don't need separate persistent storage at all. If they do need to live outside the boot volume and be shared across a fleet, EFS. If each instance just needs its own local copy on its own boot/root volume, that's already EBS by default (every EC2 instance's root volume is EBS-backed).
- Database: EBS, provisioned for consistent low-latency IOPS, whether that's the storage backing a self-managed database on EC2 or, more commonly for a new build, the storage layer underneath a managed RDS instance (RDS itself is EBS-backed, just abstracted away from you).
Trade-offs & pitfalls
A common mistake is choosing EFS by default for "shared storage" without checking whether shared write access is actually needed, EFS costs more per GB than S3 at scale and adds NFS-level latency that a single-attach EBS volume wouldn't have, so it should be a deliberate choice, not a default. Another is forgetting EBS volumes are AZ-locked: an instance can't attach a volume from a different AZ, which matters directly for the high-availability design of the database layer. Finally, S3 is not a drop-in file system, if application code assumes it can open, seek, and append to a file in place, S3 will not behave like that; EFS or EBS is the right answer whenever true in-place file mutation is required.
Compare ECS on Fargate, ECS on EC2, and Amazon EKS. For each, describe operational overhead, cost model, scaling behavior, and one workload it's best suited for.
Sample Answer
Direct answer
ECS (Amazon Elastic Container Service) on Fargate is serverless: AWS runs the underlying compute, so there's no node fleet to patch or size, at the cost of a higher per-vCPU price than an equivalently sized, well-utilized EC2 fleet. ECS on EC2 gives you the same ECS scheduler but on instances you own, so you can use Savings Plans, Reserved Instances, or Spot to lower cost, at the cost of owning capacity planning and AMI (Amazon Machine Image) patching yourself. EKS (Amazon Elastic Kubernetes Service) gives you a managed, upstream-compatible Kubernetes control plane with the full Kubernetes ecosystem (Helm, custom operators, portability across clouds), at the cost of the steepest operational learning curve of the three, and you still manage worker node lifecycle unless you also run those on Fargate.
Structured elaboration
| Dimension | ECS on Fargate | ECS on EC2 | EKS |
|---|---|---|---|
| Operational overhead | Lowest: no node patching or capacity planning, simple task definitions | Moderate: you own AMI updates, Auto Scaling Groups, and capacity planning | Highest: Kubernetes-specific operational concepts (access control, networking, ingress routing, custom resources) plus worker node lifecycle |
| Cost model | Pay per vCPU/memory reserved by the task, per second; no idle-node waste but higher unit price | Pay for the EC2 fleet; can combine Spot/Reserved/Savings Plans, but idle capacity is wasted spend | Control plane cost separate from worker cost (EC2 or Fargate); more components (load balancers, CNI, add-ons) can add up |
| Scaling behavior | Fast, per-task; ECS just launches more tasks | Task scaling is bounded by current instance capacity; Cluster Auto Scaling has to add nodes first, which is slower | Pod-level autoscaling (scaling pod replicas and resource limits) plus cluster autoscaler; most flexible but the most moving parts to tune correctly |
| Best-suited workload | Spiky or unpredictable microservices, batch jobs, teams that want minimal ops | Steady-state workloads where cost optimization via committed/Spot capacity matters and the team already runs EC2 fleets | Complex, polyglot microservices needing custom controllers/operators, multi-cloud portability, or deep Kubernetes ecosystem tooling |
Worked example
A 30-microservices startup with a small platform function (two to three engineers, no dedicated Kubernetes experience) is picking an orchestration platform. The deciding factor isn't just today's team size, it's the team's skills-and-growth trajectory:
- If the team has no Kubernetes background and needs to move fast right now, ECS on Fargate lets that small team run all 30 services without a dedicated platform hire, since there's no cluster to operate.
- If the startup expects its platform function to grow and anticipates future needs for custom traffic-management controllers, a service mesh, or portability across cloud providers, investing in EKS earlier (accepting the steeper learning curve now) can be the better call, because migrating orchestration platforms later, once 30 services already depend on one, is expensive and risky to redo.
- If cost predictability at steady state matters more than the team's growth trajectory, and the team already operates EC2 fleets elsewhere, ECS on EC2 with Savings Plans or Spot is a middle ground that avoids both Fargate's per-unit premium and Kubernetes's operational load.
Trade-offs and pitfalls
- Comparing Fargate and EC2 purely on advertised per-vCPU-hour price is misleading for a large, steady-state fleet: a well-utilized EC2 fleet at high average utilization, especially with committed discounts, can end up meaningfully cheaper than the equivalent Fargate spend, while a spiky, low-utilization workload flips that comparison the other way.
- EKS has a control-plane cost that exists separately from whatever you run the worker nodes on, and teams sometimes account only for the worker-node cost when budgeting.
- Running ECS on EC2 well requires actually owning capacity planning and AMI patching as an ongoing discipline; at 30-services scale it's easy to underestimate that as toil until an unpatched AMI or an under-provisioned Auto Scaling Group causes an incident.
How do S3 Versioning and MFA Delete help protect against accidental deletes or overwrites? What operational and cost implications should you be aware of when enabling versioning on a large bucket?
Sample Answer
Direct answer
S3 Versioning keeps every write as a distinct object version instead of overwriting data in place, so an accidental overwrite just creates a new version alongside the old one, and an accidental delete adds a lightweight delete marker, which only hides the object from a normal listing or GET, rather than destroying it. Both are recoverable by fetching or restoring a prior version ID. MFA Delete adds a second, harder-to-automate layer on top: permanently deleting an object version, or changing the bucket's versioning state, requires a valid multi-factor authentication code from the account's root user, which stops both accidental scripted deletes and a compromised non-root credential from erasing history.
Structured elaboration
- Versioning mechanics: enabling versioning is effectively one-way at the bucket level; you can enable or suspend it, but you can never make it as if the bucket was never versioned, since versions created while it was enabled persist.
- MFA Delete specifics: it must be configured using the bucket owner's root account credentials, not just any AWS Identity and Access Management (IAM) admin, and once required, permanently deleting a version or disabling versioning/MFA Delete itself requires that MFA code at request time. That's why it's usually reserved for a bucket's highest-value data rather than applied everywhere.
- Operational cost of versioning: every write, including a rewrite of unchanged content, is billed as new, full-sized storage. A bucket with frequent overwrites can see storage cost grow substantially if nothing prunes old versions. The standard mitigation is a lifecycle rule that transitions or expires noncurrent versions after a chosen window, for example keeping noncurrent versions restorable for roughly 30 days and transitioning anything not needed sooner to a cheaper storage class before final expiry around 90 days, tuned to how much recovery time the team actually needs against storage spend.
- API and tooling overhead: version-aware operations, listing versions, deleting a specific version ID, restoring by copying an old version back as current, need version-ID-aware code. Naive scripts that assume "one object equals one thing" can behave unexpectedly on a versioned bucket, since a plain DELETE just adds a marker and doesn't free any storage.
Worked example
A bucket stores user-uploaded documents, and a bad deploy runs a cleanup script that deletes keys matching a stale prefix pattern, hitting 4,000 live objects by mistake. With versioning on, none of that data is actually gone: the 4,000 objects now show delete markers. Recovery is ListObjectVersions filtered to those keys, then removing the delete marker (or copying the prior version back as current) for each, which S3 Batch Operations can drive at scale from a manifest instead of a one-by-one script. Without versioning, the same script would have caused permanent, unrecoverable data loss.
Trade-offs & pitfalls
- Enabling versioning without a lifecycle rule to prune noncurrent versions is the most common mistake: storage cost creeps up silently, especially on buckets with high overwrite churn, until someone notices the bill.
- MFA Delete is a real operational tax: it must go through the root account, which is incompatible with fully automated, script-driven bulk cleanup unless that workflow can supply an MFA code. That's why teams usually restrict it to a small number of critical buckets.
- Versioning and MFA Delete protect against accidental overwrite, accidental delete, and many scripted mistakes, but neither is a substitute for cross-region or cross-account replication if the actual risk is bucket-level deletion or a compromised account with root-level access; that scenario needs mass-deletion controls beyond versioning alone.
- A delete marker still counts as an object version and, depending on lifecycle configuration, can itself accumulate cost or clutter if never cleaned up.
What does CloudFront provide, and why would you put it in front of S3, an ALB, or API Gateway? Cover caching benefits, invalidation strategies, and origin failover.
Sample Answer
Direct answer
CloudFront is AWS's content delivery network (CDN): a global network of edge locations (points of presence, or POPs) that caches and serves content close to the requester. You put it in front of S3, an ALB (Application Load Balancer), or API Gateway for the same core reason in each case: it terminates TLS and serves cacheable responses from an edge location instead of the origin, which cuts latency, cuts load and cost on the origin, and gives you one place to attach security controls like AWS WAF (Web Application Firewall) and origin failover.
Structured elaboration
Why each origin benefits
- S3: caches static assets at the edge, reduces GET request cost and cross-region data transfer from the bucket, and, combined with Origin Access Control (OAC, the current recommended mechanism, the older Origin Access Identity/OAI still works but OAC is what AWS now recommends for new setups), lets you keep the bucket itself fully private while still serving it publicly through CloudFront.
- ALB: caches static or semi-static responses from an otherwise dynamic app, hides the ALB's origin details from the public internet, and reduces backend load for anything cacheable.
- API Gateway: caches idempotent, repeatable API responses at the edge, cutting both latency and the compute cost of re-running the same request behind the API.
Caching behavior and headers: CloudFront respects the origin's Cache-Control and Expires headers by default, and a distribution's cache behavior settings (minimum, default, and maximum TTL) can override or clamp what the origin sends. Cache-Control: no-store or no-cache should be used for anything sensitive or per-user. The Vary header, or explicitly configuring which headers/cookies/query strings are part of the cache key, matters whenever the response actually differs by those values, otherwise you either get incorrect cache hits (serving one user's response to another) or an accidentally low hit ratio from over-including things in the cache key.
Invalidation strategies
- Prefer asset versioning (a fingerprinted filename or a version query string baked in at build time) for anything you control the deploy pipeline for: a new version is simply a cache miss the first time it's requested, so there's nothing to invalidate.
- Use the CloudFront invalidation API for ad hoc, unplanned purges, a hotfix to an HTML page, for example, keeping in mind that invalidation is not instantaneous across all edge locations and has its own cost profile at high volume.
- Use a short TTL instead of frequent invalidation when a resource changes often and brief staleness is acceptable.
Origin failover: configure an origin group with a primary and a secondary (failover) origin. CloudFront health-checks the primary and automatically routes to the secondary on defined failure conditions (5xx responses, timeouts), which is a straightforward way to get basic availability protection, for example, primary origin an ALB, secondary origin a static S3 fallback page, without building custom failover logic.
Private content: signed URLs (per-object, short-lived access) and signed cookies (grant access to a whole set of paths for a session) are how you gate content behind CloudFront, using a trusted key group, the current recommended mechanism for managing the signing keys. (The older approach, CloudFront key pairs tied to the AWS account root user, still exists but requires root-level access to manage and is not the current best practice; trusted key groups can be managed by any sufficiently permissioned IAM identity.)
Edge customization: CloudFront Functions (lightweight JavaScript, for high-volume, low-latency tasks like header rewrites, redirects, or cache-key normalization) and Lambda@Edge (heavier logic, like verifying a signed JWT or fetching a secret) both let you run code at the edge as part of the request/response cycle, without a round trip to the origin.
Worked example
A documentation site behind CloudFront, with an S3 origin: static assets (JS/CSS bundles) are given fingerprinted filenames and a long max-age (effectively "cache forever," since a new deploy produces new filenames). The HTML pages that reference those bundles get a short TTL, so a deploy is visible within that TTL without needing any invalidation call at all. If a critical typo fix needs to go out immediately, an invalidation targeting just that one HTML path clears it from all edge locations without waiting out the TTL, a deliberate exception to the versioning-first strategy rather than the default way of handling every change.
Trade-offs & pitfalls
Forwarding every header, cookie, and query string into the cache key "to be safe" is a common mistake that quietly collapses your hit ratio to nearly nothing, because almost every request becomes unique. Relying on frequent invalidations as the primary cache-busting strategy instead of versioned asset names is more expensive and slower to propagate than it needs to be. And leaving the origin (an S3 bucket or an ALB) directly reachable from the internet defeats a large part of the reason to use CloudFront in the first place, OAC (for S3) or restricting the ALB/security group to only CloudFront's traffic is what actually enforces "requests must come through the CDN."
Unlock Full Question Bank
Get access to all 33 AWS Core Services and Architecture interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.