How AWS Lambda Execution Environments Work - Firecracker MicroVMs, Lifecycle Phases, and SnapStart Internals

First Published:
Last Updated:

Every AWS Lambda developer eventually asks the same question: why does the first invocation take longer, why does a global variable sometimes survive between requests, and why can two requests never share one environment on a default function? These behaviors are not arbitrary. They fall directly out of how Lambda builds, isolates, freezes, and reuses the small virtual machine that runs your code. This article explains that machinery from the inside, using only officially published information, so that the behavior you observe stops being a set of rules to memorize and becomes something you can reason about.

This is a deliberately "Under the Hood" article. Where AWS has published the internals, I explain them and connect the mechanism to the behavior you can observe and to the design decisions you should make. Where AWS has not published the internals, I say so plainly rather than guess. That honesty is the point: the value of understanding Lambda's execution environment comes from knowing exactly where the documented ground ends.

1. Introduction — Explaining Cold Starts From the Inside

A Lambda "execution environment" is the secure, isolated runtime in which your function code runs. It holds a copy of your function code, any layers, the language runtime, a writable /tmp directory, and a minimal Linux user space. Lambda creates these environments on demand, runs your code inside them, freezes them between requests, reuses them when it can, and eventually discards them. Almost every performance and correctness surprise in Lambda — cold starts, warm starts, "why did my cached value disappear," "why is my counter shared" — is a visible symptom of that lifecycle.

The goal of this article is to trace the mechanism end to end: the isolation substrate (Firecracker microVMs on bare-metal Workers), the lifecycle phases (Init, Restore, Invoke, Shutdown), how cold starts map to those phases, how SnapStart changes the picture by snapshotting an initialized environment, and how the execution environment model has recently expanded beyond the classic function.

Scope and a naming caution. The primary subject of this article is the default Lambda Functions execution environment — the one that has existed since 2014 and that most people mean when they say "Lambda." AWS has recently introduced additional execution models (Lambda Managed Instances and Lambda MicroVMs) and long-running Durable Functions; Section 7 maps how their environments differ, because the phrase "Lambda execution environments" is now genuinely plural. One naming collision matters throughout: the isolation technology under the default environment is a Firecracker microVM (the AWS security whitepaper abbreviates it "MVM"), while Lambda MicroVMs is a separate, newer product. They are related but not the same thing, and Section 3 and Section 7 keep them distinct.

What this article deliberately does not cover. This is the "lower layer" of an existing Lambda cluster on this blog. It explains why cold starts happen and why the environment behaves as it does. It does not enumerate cold-start mitigation tactics, language-by-language tuning, Provisioned Concurrency configuration, or measurement recipes — those live in the AWS Lambda Cold Start Mitigation Guide - Provisioned Concurrency, SnapStart, and Code-Level Techniques, and I link out to it rather than repeat it. For scaling quotas and throttling, see the AWS Lambda Concurrency and Scaling Guide - Reserved and Provisioned Concurrency, Throttling, and Event Source Scaling. For the full historical arc, see AWS History and Timeline regarding AWS Lambda - Overview, Functions, Features, Summary of Updates, and Introduction, and for the wider set of Lambda deep dives, the AWS Lambda Master Index - A Hub for Lambda Articles.

The internal behavior described here is drawn from the AWS Lambda Developer Guide, the Security Overview of AWS Lambda whitepaper (whose standalone pages have since been retired from the AWS documentation site; every fact drawn from it here is corroborated by the live official sources listed in the References), the Firecracker paper published at USENIX NSDI '20 (whose authors are all from AWS, making it a first-party source), the Firecracker project's official design documentation, and AWS official blog posts. Internal details evolve; the descriptions here were verified against those official sources as of 2026-07-18. Wherever a fact is a moving target (such as the set of SnapStart-supported runtimes), I note that it should be re-checked against the current documentation.

2. The Execution Environment Contract

Before opening the box, it helps to state precisely what Lambda promises you from the outside. Everything internal exists to satisfy this contract, so each guarantee is a lens onto a mechanism.

Isolation. Your function runs in an execution environment that is isolated from other functions and from other AWS accounts. AWS states that execution environments are never reused across functions, across versions of the same function, or across accounts. Mechanism: the isolation boundary is a virtual machine, not merely a process (Section 3). Implication: you get strong tenant isolation for free, but you also pay the cost of building a fresh VM-backed environment on a cold start.

Single concurrency (default). On a default function, one execution environment processes exactly one invocation at a time. If ten requests arrive simultaneously, Lambda needs ten environments. Mechanism: the runtime pulls one invocation event, processes it, and only then asks for the next. Implication: your handler never has to be thread-safe against itself on a default function — but this also means concurrency is achieved purely by multiplying environments, and each new environment is a potential cold start.

Reuse without a guarantee. After an invocation finishes, Lambda freezes the environment and keeps it around for some time in anticipation of another request. If one arrives, Lambda thaws and reuses the environment (a "warm start"). But reuse is never guaranteed. Mechanism: Lambda freezes and later thaws the microVM; it may also discard it at any time. Implication: code outside your handler (the "init" or global scope) runs once per environment and its results persist across warm invocations — which is why a cached client or a database connection can be reused, and equally why a mutable global counter appears to "remember" state it should not.

Statelessness you must enforce. Because the environment may be reused but is never guaranteed to be, you must treat any in-memory or on-disk state as ephemeral and opportunistic. It is safe as a cache; it is unsafe as a source of truth.

A writable /tmp. Each environment has a writable /tmp directory (512 MB by default, configurable up to 10,240 MB). Mechanism: /tmp lives in the environment and shares its lifecycle. Implication: files written to /tmp persist across warm invocations in the same environment, and — a subtle point documented by AWS — Lambda does not clear /tmp when it resets an environment after an invoke failure. Do not assume /tmp starts empty.

Bounded duration. A default function invocation may run up to 15 minutes. Mechanism: the function timeout bounds the entire Invoke phase (Section 4). Implication: long or open-ended work needs a different model (Durable Functions, Step Functions, or one of the newer execution models in Section 7).

The rest of this article is essentially a tour of the mechanisms behind these promises.

3. Firecracker MicroVMs — the Isolation Substrate

The foundation of the execution environment is a purpose-built virtualization stack. AWS describes it openly in the Security Overview of AWS Lambda whitepaper and in the Firecracker NSDI '20 paper.

Workers. Lambda runs execution environments on a fleet of Amazon EC2 instances that AWS calls Lambda Workers. According to the whitepaper, Workers are bare-metal EC2 AWS Nitro instances, launched and managed by Lambda in a separate, isolated AWS account that is not visible to customers. A Worker hosts one or more hardware-virtualized microVMs.

Firecracker. Each microVM is created by Firecracker, an open-source Virtual Machine Monitor (VMM) that uses the Linux Kernel's KVM to create and manage minimal virtual machines. The Firecracker NSDI '20 paper — authored by an AWS team (Agache et al.) — frames the core problem: public infrastructure providers need both the strong security of virtualization and the minimal overhead of containers, a trade-off previously considered unavoidable. Firecracker resolves it with a VMM specialized for serverless and container workloads. Its key design choices, per the paper and the project's official design document, are:
  • One Firecracker process per microVM. Each Firecracker instance strictly encapsulates a single VM, which provides a simple, robust model for security isolation. Within that process run an API thread, a VMM thread, and one or more vCPU threads.
  • A minimal device model. Firecracker offers limited emulation — VirtIO block and network devices, plus a serial console — reflecting its specialization rather than a general-purpose hypervisor's broad device support.
  • The jailer. Firecracker is launched by a separate jailer process that sets up resources requiring elevated privileges (such as cgroups and chroot), drops privileges, and then exec()s into the Firecracker binary. After the handoff, Firecracker runs unprivileged.
  • Seccomp filters. Seccomp-bpf filters are loaded per thread before any guest code runs, allowing only the minimal set of system calls Firecracker needs. The design treats the vCPU threads as if they could be running malicious code and enforces barriers accordingly — defense in depth.
  • Rate limiting. Firecracker enforces configurable rate limits on network and disk throughput, so a guest cannot consume unbounded VMM or host-kernel CPU time.

How Lambda applies it. The whitepaper's "Lambda isolation technologies" section lists the container-like Linux mechanisms Lambda layers on top of Firecracker to separate execution environments: control groups (cgroups) to constrain CPU and memory; namespaces to give each environment unique process IDs, user IDs, and network interfaces; seccomp-bpf to limit syscalls; iptables and routing tables to prevent ingress and isolate network connections between microVMs; chroot for scoped filesystem access; and Firecracker configuration for device rate limiting. AWS also states that all data-plane communication to Workers is encrypted with AES-GCM, that Workers have a maximum lease lifetime of 14 hours (after which no new invocations are routed to them and they are gracefully retired), and — importantly — that microVMs are never reused across functions, function versions, or accounts.
Lambda Worker and Firecracker microVM isolation hierarchy inside a Lambda-owned isolated AWS account
Lambda Worker and Firecracker microVM isolation hierarchy inside a Lambda-owned isolated AWS account

The naming disambiguation. Because the whitepaper abbreviates "micro virtual machine" as MVM, and because AWS launched a separate product literally called Lambda MicroVMs (Section 7), it is easy to conflate them. Keep them distinct: the Firecracker microVM / MVM is the isolation primitive underneath a default function's execution environment; Lambda MicroVMs is a distinct, developer-controlled compute product that also uses Firecracker but exposes a very different lifecycle and API. When this article says "microVM" without qualification, it means the Firecracker isolation primitive.

The three-point connection. Because isolation is enforced at the virtual-machine level (mechanism), you get strong protection against cross-function or cross-account data leakage (behavior) — but building that VM-backed environment from scratch is exactly what makes a cold start cost real time, and it is why you should still externalize anything that must survive beyond a single environment (implication).

4. The Execution Environment Lifecycle

With the substrate in place, we can follow an environment through its life. AWS documents four phases for a default function — Init, Invoke, and Shutdown, plus a Restore phase that exists only for SnapStart — and describes them as event-driven: each phase begins with an event that Lambda sends to the runtime and to every registered extension, and each participant signals completion by making a Next request to the Runtime API (runtime) or Extensions API (extensions). Lambda freezes the environment when the runtime and all extensions have completed and there are no pending events.
AWS Lambda execution environment lifecycle: Init or Restore, Invoke, freeze and thaw, and Shutdown phases
AWS Lambda execution environment lifecycle: Init or Restore, Invoke, freeze and thaw, and Shutdown phases

4.1 The Init phase

During Init, Lambda creates (or unfreezes) an environment with the configured resources, downloads the function code and all layers, and then runs three tasks in sequence, which AWS names as sub-phases:
  1. Extension init — start all external extensions.
  2. Runtime init — bootstrap the language runtime.
  3. Function init — run your static initialization code (everything outside the handler).

If SnapStart is enabled, a fourth step runs at the end of Init: any before-checkpoint runtime hook. The Init phase ends when the runtime and all extensions signal readiness with a Next request.

The 10-second limit and where it does not apply. For a default on-demand function, Init is limited to 10 seconds. If the three tasks do not finish in time, Lambda abandons that attempt and retries the initialization at the time of the first invocation, this time bounded by the configured function timeout. On a timeout or crash during Init, Lambda emits an INIT_REPORT log line. Crucially, AWS documents that the 10-second cap does not apply to functions using Provisioned Concurrency, SnapStart, or Managed Instances: for those, initialization code may run for up to 15 minutes, with a limit of 130 seconds or the configured function timeout (maximum 900 seconds), whichever is higher.

When Init happens. For an on-demand function, Init happens at the first invocation (the classic cold start) — though AWS notes Lambda may occasionally initialize environments ahead of requests, while explicitly warning you not to depend on that behavior. With Provisioned Concurrency, Lambda runs Init when you configure the setting and keeps pre-initialized environments ready in advance. With SnapStart, Init happens once, when you publish a function version.

4.2 The Restore phase (SnapStart only)

For a SnapStart function, environments are not initialized from scratch at invoke time. Instead, when you first invoke the function and as it scales up, Lambda resumes a new environment from a previously persisted snapshot — this is the Restore phase. If you have an after-restore runtime hook, it runs at the end of Restore, and you are billed for its duration. The runtime load and the after-restore hook must complete within a 10-second limit; otherwise Lambda raises a SnapStartTimeoutException and emits a RESTORE_REPORT log. When Restore completes, Lambda proceeds to Invoke. Section 6 covers how the snapshot itself is made.

4.3 The Invoke phase

When a request arrives, Lambda sends an Invoke event to the runtime and to each extension; the runtime calls your handler. The function's timeout bounds the entire Invoke phase — runtime plus all extensions must finish within it. AWS makes a precise and often-overlooked point here: there is no independent post-invoke phase, and the billed duration is the sum of all invocation time and is not calculated until the function and all extensions have finished. A slow extension therefore extends your billed duration. The phase ends when the runtime and all extensions each send a Next request; Lambda then freezes the environment.

Failure and reset. If the function crashes or times out during Invoke, Lambda resets the environment. The reset behaves like a Shutdown event — Lambda shuts down the runtime and sends a shutdown event to each extension with the reason — and if the environment is used again, the runtime and extensions are re-initialized alongside the next invocation. One documented subtlety: the reset does not clear the contents of /tmp before the next Init, which is consistent with the regular shutdown behavior.

4.4 The Shutdown phase

If an environment receives no invocations for a period of time, Lambda triggers Shutdown: it shuts down the runtime, sends a Shutdown event to each registered extension so they can stop cleanly, and removes the environment. Extensions receive the event telling them the environment is about to disappear — which is their opportunity to flush telemetry or close connections.

The three-point connection. Because each phase is event-driven and gated on the runtime and extensions signaling completion (mechanism), a heavy extension or slow static initializer directly lengthens what you observe as cold-start latency and billed duration (behavior) — so keeping Init lean and extensions light is a first-order performance decision (implication).

5. Cold Starts Explained From the Inside

With the lifecycle in hand, the cold start stops being mysterious. A cold start is simply the visible cost of the Init phase: Lambda has to download your code, build and start the environment, and run your static initialization before it can run the handler. A warm start is what happens when Lambda reuses a frozen environment — Init is skipped entirely and only the handler runs, which is why warm invocations are typically much faster.

AWS publishes the rough shape of this behavior: cold starts typically occur in under 1% of invocations, and a cold start's duration ranges from under 100 milliseconds to over one second. Cold starts are more common in development and test functions than in production, because they are invoked less frequently and so more often find no warm environment waiting.

The largest contributor is your own init code. AWS is explicit that the biggest source of pre-handler latency is the static initialization code that runs once when a new environment is created — importing libraries, setting up configuration, and opening connections. That code does not run again on warm invocations. This is the mechanism behind a familiar piece of advice: create SDK clients and open connections in the global scope so they are reused, but import only what you need so the one-time cost stays small.

Cold start time is billed. Since August 1, 2025, AWS standardized billing for the Init phase so that initialization time is included in the billed duration across all function configurations (previously, on-demand functions using managed runtimes with ZIP packaging did not pay for Init separately). I mention this only to reinforce the mechanism-to-behavior link — the initialization you see in a cold start is real, measurable, billed work — not to discuss pricing.

Where to go for mitigation. Everything about reducing cold starts — Provisioned Concurrency, SnapStart adoption, per-language tuning, lazy imports, connection reuse, bundling, container-vs-zip trade-offs, and how to measure cold starts — is covered in depth in the AWS Lambda Cold Start Mitigation Guide. This section intentionally stops at why the cold start exists.

6. SnapStart Internals

SnapStart is the most direct example of AWS re-engineering the lifecycle itself, so it deserves a mechanism-level look (while deferring the "should I turn it on" decision to the mitigation guide).

The snapshot. When SnapStart is activated, the Init phase runs once — at the moment you publish a function version — rather than at invoke time. Lambda then takes a snapshot of the memory and disk state of the fully initialized execution environment, persists that snapshot in encrypted form, and caches it for low-latency access. At invoke time (and as the function scales), Lambda resumes environments from this snapshot via the Restore phase instead of paying the full Init cost again. In effect, the expensive one-time work is done ahead of time and then replayed from an image.

Runtime hooks. Because initialization and execution are now separated in time, SnapStart exposes two hook points. A before-checkpoint hook runs at the end of Init, just before the snapshot is taken — the place to release anything that should not be frozen into the image. An after-restore hook runs at the end of Restore, on every resumed environment — the place to refresh anything that must be unique or current.

Priming. The before-checkpoint hook also enables an optimization AWS calls priming: exercising representative code paths during Init, before the snapshot is taken, so that the work they trigger is captured in the image. The official SnapStart launch guidance illustrates the mechanism with Java: bytecode starts out interpreted and is just-in-time compiled on hot paths, so invoking a representative operation in the before-checkpoint hook (the AWS sample performs one read against the DynamoDB table the function uses) causes your code, the AWS SDK, and supporting libraries to be loaded and JIT-compiled — and that compiled state is exactly what every restored environment inherits. The first real invocation then skips that compilation cost. Priming requires understanding the side effects of the code you run early, which is why AWS frames it as an optimization you adopt deliberately rather than a default.

The uniqueness problem. Snapshotting has a systems-level consequence that AWS documents candidly: if many environments are restored from the same memory image, any state captured in that image — including the seeded state of a pseudo-random number generator — is identical across them. That can undermine uniqueness and cryptographic randomness. AWS has hardened the managed runtime to mitigate this: it updated Amazon Linux and OpenSSL to be resilient to snapshot operations, and it validated that Java's built-in java.security.SecureRandom maintains uniqueness after a restore. Software that always draws randomness from the operating system (for example /dev/random or /dev/urandom) is already resilient. But — and this is the design takeaway — if your own code implements uniqueness with custom logic, you are responsible for restoring it, typically in an after-restore hook. This is a direct case of an internal mechanism (memory snapshotting) dictating a correctness concern in your code.

Supported runtimes and limits. As verified against the official documentation on 2026-07-18, SnapStart is available on Java 11 and later, Python 3.12 and later (including Python 3.13), and .NET 8 and later. Other managed runtimes (such as recent Node.js and Ruby versions), OS-only runtimes, and container images are not supported. SnapStart does not support Provisioned Concurrency, Amazon EFS, Amazon S3 Files, or ephemeral storage greater than 512 MB, and it works only on published function versions and aliases that point to them — not on $LATEST. Because the supported-runtime set expands over time, re-check the current documentation before you plan around it.

The three-point connection. Because SnapStart replays an initialized memory image (mechanism), first-invocation latency drops sharply but every restored environment starts from identical in-memory state (behavior) — so you use the restore hook to re-establish per-environment uniqueness and connections, and you confirm your runtime is on the supported list before designing around it (implication).

7. Scaling, Environment Reuse, and the New Execution Models

7.1 Scaling and reuse of the default environment

Scaling on a default function follows directly from single concurrency: because one environment handles one invocation at a time, Lambda serves N concurrent requests by having N environments, each of which had to be initialized at some point. A sudden burst of traffic therefore produces a burst of Init phases — the reason large, spiky workloads see more cold starts. The specifics of concurrency quotas, reserved and provisioned concurrency, burst limits, and event-source scaling are covered in the AWS Lambda Concurrency and Scaling Guide; here the point is only that scaling and cold starts are two views of the same reuse model.

Between invocations, Lambda freezes the environment and retains it "for a period of time" in anticipation of reuse. The exact retention duration is not published (Section 8), which is precisely why reuse must be treated as opportunistic.

7.2 The execution environment is now plural

For most of Lambda's history there was one execution environment model. That is no longer true, and an article about "how Lambda execution environments work" has to acknowledge the newer models — each of which makes different choices about the very properties (concurrency, duration, reuse, lifecycle control) that Sections 2 through 6 established for the default function.
  • Lambda Durable Functions. These extend the default model with state management for long-running workflows. AWS documents additional lifecycle phases for them — beyond Init/Invoke/Shutdown, they add a Wait state (the function can pause without consuming resources) and a Resume state (it restarts from the last checkpoint), with automatic checkpointing — and they can run for up to one year. The practical patterns, checkpoint/replay determinism, and when to choose Step Functions instead are covered in the AWS Lambda Durable Functions Practical Guide - Checkpoint and Replay Determinism and When to Use Step Functions Instead.
  • Lambda Managed Instances. This model runs your function code on customer-owned Amazon EC2 instances (including Graviton4 and memory-optimized families) while Lambda manages provisioning, scaling, patching, and routing. Its execution environment differs from the default in a fundamental way: it supports multi-concurrent invocations, meaning one execution environment can process multiple invocations at the same time. That single change inverts one of the default guarantees — thread safety, state management, and context isolation now become your concern, because the "one invocation per environment" simplification no longer holds. Managed Instances also scale differently: scaling is asynchronous, driven by CPU resource utilization and multi-concurrency saturation rather than by an arriving invocation finding no free environment, so there are no cold starts — and the documentation is explicit about the consequence: if traffic more than doubles within five minutes, invocations may be throttled while Lambda scales instances and execution environments to catch up, and a request routed to a saturated environment is retried on another.
  • Lambda MicroVMs. This is the product whose name collides with the Firecracker microVM primitive, and it is genuinely different from a function. You package application code and a Dockerfile, Lambda builds a MicroVM image and snapshots the fully initialized environment, and then you call an API to run, suspend, resume, and terminate MicroVMs yourself. A MicroVM can run for up to 8 hours per session, preserves memory and disk state across a suspend/resume boundary (with /suspend and /resume hooks), accepts multiple concurrent connections, and exposes a dedicated HTTPS endpoint. AWS states a MicroVM can launch or resume in as little as a second for every 500 MB of snapshot data. Where a default function's lifecycle is fully managed by Lambda, a MicroVM's lifecycle is developer-controlled — a different contract entirely, aimed at persistent, sessionful, or sandboxed workloads such as running user- or AI-produced code.

The following table keeps the models and the naming straight.
TermWhat it isConcurrencyDurationLifecycle control
Firecracker microVM (MVM)Isolation primitive under a default functionOne environment per invocationBounded by invocationManaged by Lambda
Lambda Functions (default)The classic function execution environmentOne invocation per environmentUp to 15 minutesManaged by Lambda
Lambda Durable FunctionsFunction model with checkpointing and wait/resumeOne invocation per environmentUp to one yearManaged by Lambda
Lambda Managed InstancesFunctions on customer-owned EC2Multiple invocations per environmentSustainedManaged by Lambda (customer instances)
Lambda MicroVMsDeveloper-controlled isolated compute productMultiple connections per MicroVMUp to 8 hours per session, suspend/resumeDeveloper-controlled

The three-point connection. Because these models make different choices about concurrency, duration, and lifecycle control (mechanism), the same guarantees that make default-function code simple (single concurrency, statelessness) do not automatically carry over (behavior) — so choosing the right execution model, and re-checking your assumptions when you do, is itself a design decision (implication).

8. What AWS Has Not Published

The discipline of this article is to separate what is documented from what is not. The following are aspects of the execution environment that AWS has, to my knowledge as of 2026-07-18, deliberately not published in detail. I list them so you know exactly where reasoning from documentation ends and speculation would begin — and so you avoid designing around behavior AWS does not commit to.
  • Placement and scheduling. How Lambda decides which Worker hosts a new environment, and how it schedules environments across the fleet, is not published. AWS has not disclosed how many microVMs run on a single Worker.
  • Warm-environment retention time. AWS says a frozen environment is kept "for a period of time" but does not publish that duration, and it can vary. This is why you must never treat reuse as guaranteed or time-bounded.
  • Predictive pre-initialization. AWS states that Lambda may occasionally initialize environments ahead of requests, but explicitly recommends not taking a dependency on this behavior and does not publish the heuristics behind it.
  • Snapshot storage internals. For SnapStart, AWS says the snapshot is encrypted and "cached for low-latency access," but the caching, tiering, chunking, and eviction internals are not published. The observable contract is the Restore phase and its 10-second limit, not the storage design.
  • Proprietary isolation technologies. The whitepaper enumerates the open-source isolation mechanisms (cgroups, namespaces, seccomp-bpf, and so on) but also refers to "AWS proprietary isolation technologies" without fully detailing them.
  • Freeze/thaw internals. AWS documents that environments are frozen and thawed and when, but not the low-level mechanism by which a microVM is frozen and resumed for warm reuse.

If you find yourself needing one of these facts to make a design work, that is a signal to redesign so the correctness of your system does not depend on undocumented behavior.

9. Design Implications for Your Functions

Everything above converges on a handful of concrete design decisions.
  • Use init for what should be reused; keep it lean. Static initialization runs once per environment and its results persist across warm invocations, so it is the right place to construct SDK clients and open connections. But it is also the dominant cost of a cold start and is bounded (10 seconds on default on-demand functions), so import only what you need and lazily load rarely used objects.
  • Treat global scope as a cache, never a store. Because environments are reused but not guaranteed to be, a value in the global scope may survive to the next invocation or may not. Use it to reuse expensive resources; never use it to carry per-request state, and never assume a global counter or accumulator is either fresh or shared as you intend.
  • Do not assume a clean /tmp. /tmp persists across warm invocations in the same environment and is not cleared when Lambda resets an environment after a failure. If your logic depends on /tmp contents, namespace files by request ID or clean up explicitly.
  • Keep extensions light. Extensions participate in every lifecycle phase and can delay the freeze; because billed duration is not finalized until all extensions signal completion, a slow extension costs you latency and money on every invocation.
  • With SnapStart, restore uniqueness and connections in hooks. Because every restored environment starts from the same memory image, use an after-restore hook to re-seed anything that must be unique and to re-establish network connections; confirm your runtime is supported before designing around it.
  • Design for many parallel environments. Concurrency is achieved by multiplying environments, so externalize shared state (to DynamoDB, ElastiCache, and so on) rather than relying on any single environment. If you adopt Managed Instances, revisit thread safety, because one environment can then handle multiple invocations at once.
  • Match the execution model to the workload. Short, event-driven work fits the default function; long-running orchestration fits Durable Functions or Step Functions; sustained, IO-heavy, or thread-parallel work may fit Managed Instances; persistent, sessionful, or sandboxed workloads may fit Lambda MicroVMs.

The first two points come together in the most common pattern of all — creating expensive clients once, in the global scope, so warm invocations reuse them. This is a direct application of "init runs once per environment; the handler runs per invocation":
import boto3

# Function init (global scope): runs once when the environment is created,
# then reused across every warm invocation of this environment.
s3 = boto3.client("s3")

def lambda_handler(event, context):
    # Invoke phase: runs on every invocation; reuses the s3 client above.
    # Keep per-request state here, not in the global scope.
    bucket = event["bucket"]
    return s3.list_objects_v2(Bucket=bucket).get("KeyCount", 0)

For the tactical follow-through on cold starts, see the AWS Lambda Cold Start Mitigation Guide; for scaling behavior, the AWS Lambda Concurrency and Scaling Guide; and for event-driven composition, Event-Driven Serverless Architecture on AWS - Building Resilient Workflows with API Gateway, Lambda, EventBridge, Step Functions, and DynamoDB.

10. Frequently Asked Questions

Is each Lambda invocation a fresh container?
No. Your code runs in an execution environment backed by a Firecracker microVM, and Lambda reuses that environment for subsequent invocations when it can (a warm start). A brand-new environment is built only when none is available to reuse — that is a cold start. Environments are isolated at the virtual-machine level and are never reused across functions, versions, or accounts.

Is a "Firecracker microVM" the same thing as "Lambda MicroVMs"?
No, and the collision is unfortunate. A Firecracker microVM (abbreviated MVM in the AWS whitepaper) is the isolation primitive underneath a default function's execution environment. Lambda MicroVMs is a separate, newer product with a developer-controlled lifecycle, suspend/resume, up to 8-hour sessions, and its own API. Both use Firecracker; they are not the same offering.

Why is my function's first invocation slow?
That first invocation pays for the Init phase: downloading code, building and starting the environment, and running your static initialization before the handler runs. The largest contributor is usually your own initialization code. Subsequent invocations that reuse a warm environment skip Init and are typically much faster.

Does SnapStart eliminate cold starts?
It reduces the dominant cost by replacing the full Init phase with a Restore from a pre-initialized snapshot, but restoring is not free — there is still a Restore phase with a 10-second limit, and only specific runtimes are supported. It changes the shape of the cold start rather than removing it entirely.

Is /tmp shared between invocations?
Within the same execution environment, yes — files in /tmp persist across warm invocations, and Lambda does not clear /tmp even when it resets an environment after a failure. Across different environments, no. Never assume /tmp is empty at the start of an invocation.

Can two invocations run in the same environment at the same time?
On a default function, no — one environment handles one invocation at a time, and concurrency is achieved by adding environments. Lambda Managed Instances change this: they support multi-concurrent invocations within a single environment, which means your code must then be thread-safe.

How long does Lambda keep a warm environment before shutting it down?
AWS does not publish the exact retention duration; it keeps an idle environment "for a period of time." Because it is unspecified and can vary, you should design as if a warm environment may disappear at any moment.

11. Summary

Lambda's execution environment is not a black box so much as an under-documented but coherent machine. A default function runs inside a Firecracker microVM on a bare-metal Nitro Worker, isolated by KVM virtualization plus cgroups, namespaces, seccomp-bpf, chroot, and network controls. That environment moves through documented, event-driven lifecycle phases — Init (with its Extension/Runtime/Function sub-phases), an optional SnapStart Restore, Invoke, and Shutdown — and Lambda freezes and reuses it opportunistically between requests. A cold start is simply the visible cost of Init; a warm start is the reuse of a frozen environment; SnapStart replays a snapshot of an initialized environment to cut that cost, at the price of a uniqueness concern you resolve in a restore hook. Newer models — Durable Functions, Managed Instances, and Lambda MicroVMs — reuse the same substrate but make different choices about concurrency, duration, and lifecycle control, which is why "execution environment" is now genuinely plural. And a short list of internals — placement, retention time, pre-initialization heuristics, snapshot storage, and freeze/thaw mechanics — remains unpublished, which is exactly where your designs should avoid taking a dependency.

Once you see the lifecycle, the behavior follows: reuse explains warm starts and surviving globals, single concurrency explains the environment-per-request scaling model, and VM-level isolation explains both the strong tenant boundary and the real cost of building an environment. For the practical next steps, continue with the AWS Lambda Cold Start Mitigation Guide and the AWS Lambda Concurrency and Scaling Guide, or browse the full set from the AWS Lambda Master Index.

12. References

Official AWS documentation Security and isolation (live AWS official sources) Firecracker (first-party paper and official design) AWS official blogs

Related Articles on This Site


References:
Tech Blog with curated related content

Written by Hidekazu Konishi