Serverless functions are easy to describe as stateless because individual function instances are temporary, especially in the broader world of serverless computing.
A request arrives, the platform starts or reuses a function instance, the code runs, and eventually that instance may disappear. From the application’s point of view, there is no guarantee that the same machine, container, process, or memory will be available for the next request.
That does not mean a serverless application has no state. It means important state should not depend on the lifetime of the function that happens to process a request, which is the same architectural boundary behind why sequence numbers carry meaning.
A checkout still needs to remember the customer’s cart. An API still needs user records, authentication data, and transaction history, while a background job may need to know which messages have already been processed. That state exists somewhere; serverless simply pushes most durable state outside the function itself.
Request
│
▼
Serverless Function
│
├── temporary memory
│
└── temporary local files
│
▼
may disappear
Persistent state
│
├── Database
├── Cache
├── Object storage
└── Queue
The practical rule is simple: treat the function as disposable and keep important state somewhere designed to survive it.
A Serverless Function Is Temporary by Design
Traditional applications often run on servers or long-lived processes that remain available for hours, days, or months. Developers can become accustomed to the idea that memory attached to that process will continue to exist until the application is deliberately restarted.
Serverless platforms use a different operating model. Functions are created and removed according to demand, platform scheduling, scaling decisions, deployment changes, failures, and other infrastructure events.
A function instance might handle one request and disappear. Another might remain warm and process hundreds of requests before eventually being recycled.
Your application usually does not control that lifetime.
That uncertainty is what makes local state unreliable.
Local Memory Can Exist Without Being Durable
Suppose a function contains an in-memory variable:
request_count = 0
Each request increments it.
During one warm instance’s lifetime, the value may appear to work exactly as expected:
Request 1 → 1
Request 2 → 2
Request 3 → 3
It is tempting to conclude that the function has persistent state.
Then the platform replaces the instance.
The next request may start with:
request_count = 0
again.
Nothing unusual has happened. The value existed only inside one temporary execution environment.
This distinction matters because temporary state and persistent state are not the same thing. Serverless functions can absolutely hold state in memory while they are alive; the problem is that the application cannot safely assume that state will still be there later.
Warm Instances Can Make the Problem Confusing
Serverless platforms often reuse function instances to avoid paying the startup cost for every invocation.
This is sometimes called a warm invocation.
A function may initialize something once:
load configuration
open reusable connection
build lookup table
and subsequent requests hitting the same instance may find those objects still in memory.
That can be a useful performance optimization. Database connection pools, parsed configuration, SDK clients, and cached read-only reference data can sometimes be reused between invocations.
The mistake is turning that optimization into a correctness requirement.
Imagine a function remembers a shopping cart in memory:
Warm instance A
cart["user-42"] = ["keyboard", "mouse"]
The customer’s next request might reach instance B:
Warm instance B
cart["user-42"] = ?
Instance B has its own memory and knows nothing about what happened in instance A.
Even worse, instance A could disappear at any moment.
Warm reuse is therefore something the application can benefit from, but never something critical state should depend on.
Scaling Creates Several Copies of “Local State”
The problem becomes even clearer when the application receives concurrent traffic.
Suppose demand causes the platform to run four instances:
Incoming requests
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Function A Function B Function C
memory memory memory
Each one has its own isolated memory.
If Function A stores:
current_balance = 500
that does not automatically update Function B or Function C.
The application has created multiple independent versions of what was supposed to be shared state.
This is why serverless architecture usually treats local memory as instance-local cache or scratch space, not as the authoritative location for business data.
As soon as multiple instances can exist, important shared state needs somewhere all of them can reach.
Persistent State Lives Outside the Function
A better mental model is that serverless functions are compute workers operating over external state.
Serverless Functions
┌──────┬──────┬──────┐
│ │ │ │
▼ ▼ ▼ ▼
Shared Services
│
┌───────────┼───────────┐
▼ ▼ ▼
Database Cache Storage
│
▼
Queue
The function can disappear and another one can continue because the important information survives independently.
This changes where architecture decisions happen. Instead of asking how the server keeps state, you ask which external system should own each type of state.
A customer record probably belongs in a database. A short-lived session may belong in a cache, while a large uploaded file belongs in object storage. Work waiting to be processed may belong in a queue.
Serverless removes much of the server-management burden, but it does not remove data architecture.
Databases Hold Durable Application State
Databases are the most obvious place for persistent state.
Suppose a function processes an account update:
POST /profile
The incorrect model is:
Function memory
user.email = "new@example.com"
because that update disappears when the function instance does.
The durable model writes the change to a database:
Function
│
▼
Database
│
└── user.email = "new@example.com"
Now any future function invocation can retrieve the updated record regardless of which instance handles the request.
This is how serverless applications maintain customers, orders, subscriptions, permissions, inventory, transaction records, and most other business data.
The compute may be temporary.
The data is not.
The Database Also Becomes a Coordination Boundary
Externalizing state is not only about durability.
It also gives multiple function instances a shared place to coordinate.
Imagine two requests attempting to purchase the final unit of an item. If each function reads inventory into local memory and updates it independently, both could conclude that the item is available.
A database transaction, conditional write, or other concurrency mechanism can provide the shared coordination that function-local memory cannot, much like a strategy database separates durable decisions from one process’s local memory.
The architecture becomes:
Function A ──┐
├──► Shared Inventory Record
Function B ──┘
That matters because serverless workloads often scale horizontally very quickly. What appears to be one application can suddenly consist of hundreds of concurrent function executions.
Correctness therefore depends on the guarantees provided by the external state system, not on assumptions about one process executing requests sequentially.
Caches Hold State Too, but Usually With Different Guarantees
A distributed cache can also store data outside the function, often as part of a broader distributed system.
This is useful for information that needs to survive between invocations or be shared across instances but does not necessarily require the durability guarantees of the primary database.
Examples include:
- sessions;
- expensive query results;
- rate-limit counters;
- temporary tokens;
- frequently used reference data;
- short-lived workflow information.
The important word is distributed.
An in-memory cache inside one function instance:
Function A
└── local cache
is not equivalent to a shared cache service:
Function A ──┐
Function B ──┼──► Shared Cache
Function C ──┘
The first can improve performance for repeated work on the same warm instance. The second can provide shared state across the application.
They solve different problems.
A Cache Should Not Quietly Become the Only Copy of Important Data
External caches are more persistent than function memory, but they are not always designed to be the permanent system of record.
Entries may expire. Capacity pressure may cause eviction, and some cache configurations prioritize speed over strong durability.
If losing the value would corrupt the business process, the architecture should ask whether the cache is really the correct authoritative store.
A common pattern is:
Function
│
├── check cache
│ │
│ └── hit → return
│
└── miss
│
▼
Database
│
▼
update cache
Here, the cache accelerates access while the database remains authoritative.
Serverless does not change that principle. It simply makes the difference between local and shared caching more visible.
Object Storage Handles State That Does Not Belong in Memory
Files are another form of application state.
A serverless function might receive an image, generate a report, process a PDF, or create a video thumbnail. Saving the finished file only to the function’s local filesystem is risky if that filesystem disappears with the execution environment.
Persistent object storage provides a better home, particularly for assets such as the images discussed in vector vs raster images.
Upload
│
▼
Function
│
▼
Object Storage
│
└── file survives function lifetime
The function can still use local disk as temporary working space if the platform provides it. For example, it might download an image, transform it locally, upload the output, and then allow the temporary files to disappear.
That is a healthy use of ephemeral local state.
The durable result has already been moved somewhere persistent.
Local Files Should Usually Be Treated as Scratch Space
Function environments sometimes expose a temporary filesystem.
That can create the same false sense of durability as warm memory.
A file written during one invocation may still be present during another invocation on the same warm instance. But another instance may not have it, and the platform may remove the original environment entirely.
A safe assumption is:
Local function filesystem
│
└── temporary workspace
not:
Local function filesystem
│
└── permanent application storage
This makes local disk useful for decompression, temporary exports, image transformations, intermediate calculation results, and similar workloads.
Once the result matters beyond the current execution, it should move into durable storage.
Queues Are Persistent State About Work
A queue may not look like state in the same way a database row does, but it stores something important: work that has not been completed yet, which is exactly the kind of workload typically modeled with a message queue.
Imagine an order-processing function.
Instead of requiring one request to complete billing, inventory updates, email delivery, analytics, and shipping preparation synchronously, it may publish messages:
Order Created
│
├──► Payment Queue
├──► Fulfillment Queue
└──► Email Queue
Those queued messages survive independently of any particular function execution.
A worker can fail, disappear, or time out, and another function can later continue processing the outstanding work.
That is another example of serverless state living outside compute.
The queue remembers what remains to be done.
Queues Also Reveal Why “Stateless” Can Be Misleading
Consider a background function that processes an image.
The function itself may begin every invocation with no local knowledge of previous work. Yet the overall system knows:
- which file needs processing;
- which task is pending;
- how many times it has been attempted;
- whether it should be retried;
- where the result should be stored.
That information exists in the queue, storage service, or related database records.
The application is clearly stateful.
The function is merely replaceable.
That is the better concept to carry into serverless architecture.
Treat Functions as Disposable Workers
A robust serverless design assumes a function can disappear after almost any invocation.
That leads naturally to a worker model:
External State
│
▼
Disposable Function
│
▼
External State
The function reads what it needs, performs computation, and writes durable results back out.
If another instance runs next time, correctness does not change.
This makes deployments, scaling, failures, and infrastructure replacement much easier to tolerate because the function itself carries little unique information that needs to be preserved.
The function can be replaced without losing the application’s memory of what happened.
Disposable Does Not Mean Reinitialize Everything
Treating functions as disposable does not mean ignoring warm instances.
There is a useful distinction between state required for correctness and state reused for efficiency.
A database connection client can be initialized outside the main handler and reused when an instance stays warm. A parsed schema or static lookup table might remain in memory, saving repeated initialization work.
If the instance disappears, the next one simply recreates them.
That is safe because losing those objects affects performance, not correctness.
A useful rule is:
If the local value disappears unexpectedly, should the application still produce the correct result?
If the answer is yes, local reuse is probably fine.
If the answer is no, the value belongs somewhere persistent.
User Sessions Show the Difference Clearly
Sessions are a common example.
Suppose a user logs in and the function writes:
session["user"] = 481
only into local memory.
The next HTTP request could reach another instance that has no knowledge of that session.
The user suddenly appears logged out.
Instead, session information needs to be represented in a way that works across temporary compute. Depending on the authentication design, that could mean a shared session store or a signed token carrying enough information for another instance to validate the request.
The important point is architectural rather than tied to one implementation.
Authentication state cannot depend on repeatedly reaching the same function instance unless the platform explicitly provides a reliable mechanism for that behavior.
Shopping Carts Have the Same Problem
A shopping cart is state that must outlive individual requests.
A customer may add an item at 09:00, close the browser, return at 09:30, and complete the purchase through an entirely different function instance.
The cart therefore needs an external representation:
User
│
▼
Function
│
▼
Cart Store
│
└── items persist across requests
That store could be a database, distributed cache, or another appropriate persistent service depending on the business guarantees required.
The serverless function should not care whether it is the same function environment that handled the previous cart update.
That independence is precisely what makes horizontal scaling easier.
Function Retries Make External State Even More Important
Serverless and event-driven systems often retry failed operations.
That introduces another state problem.
Suppose a function receives a payment event, charges the customer, and crashes before acknowledging completion. The platform may invoke it again with the same event.
If the function has no durable way to know the payment was already processed, it could charge the customer twice.
Important workflow state may therefore include an idempotency record such as:
event_id: evt_8921
status: processed
stored outside the function.
On retry, another instance can check the record and avoid repeating an irreversible side effect.
This illustrates why serverless architecture often requires more careful external state handling rather than less. Temporary compute makes retry and replacement easier, but the durable systems around that compute have to preserve enough information for safe recovery.
State Should Be Divided by What It Needs to Survive
Not every value deserves the same storage system.
It helps to classify state according to its required lifetime and guarantees.
| State | Typical location | Why |
|---|---|---|
| Customer or order record | Database | Durable business data |
| Session or rate-limit counter | Distributed cache or database | Shared across instances |
| Uploaded image | Object storage | Durable file storage |
| Pending background job | Queue | Survives worker failure |
| Parsed configuration | Local memory | Safe to recreate |
| Temporary transformed file | Local disk | Needed only during execution |
This prevents the opposite mistake: externalizing absolutely everything even when the value is cheap and safe to recreate.
The goal is not “never use local state.”
It is never make durable correctness depend on ephemeral local state.
Stateful Serverless Workflows Still Fit the Model
Some serverless applications contain long-running workflows involving several steps:
Receive Order
│
▼
Take Payment
│
▼
Reserve Inventory
│
▼
Arrange Shipping
No single function needs to remain alive throughout that workflow.
The state machine can be persisted externally, with each function performing one step and recording the outcome, a pattern that also matters in data protection strategies when recovery steps must survive individual worker failures.
After payment succeeds, durable workflow state might say:
order_id: 9014
payment: complete
inventory: pending
shipping: pending
Another function can continue later.
The workflow itself is highly stateful even though each compute step is temporary.
This is one of the clearest examples of why “serverless means stateless” is too simplistic.
External State Becomes Part of the Application’s Reliability Model
Once durable state sits outside the function, the reliability of those services becomes central.
If the database is unavailable, the disposable function may still run but cannot complete meaningful work. If the queue duplicates messages, functions need safe retry behavior, while stale cache entries can affect what users see.
Serverless therefore shifts some architecture concerns rather than removing them.
You have less responsibility for provisioning and maintaining individual application servers, but more reason to think carefully about:
- database consistency;
- cache invalidation;
- queue delivery semantics;
- retry behavior;
- storage durability;
- idempotency;
- connection limits.
The function may be simple, but the same care around retries and observability is what drives articles like what a checksum error means.
The state architecture still deserves deliberate design.
The Important Boundary Is Durable Versus Ephemeral
“Stateless” often collapses several different ideas into one word.
A serverless function can maintain local variables during execution. It may retain objects between warm invocations, use local temporary storage, and hold cached data while an instance remains alive.
All of that is state.
What the function usually cannot promise is durability.
That gives us a more useful distinction:
Ephemeral state
└── useful while this instance exists
Durable state
└── must survive instance replacement
Temporary computation belongs comfortably inside the function.
Business state, shared workflow state, customer data, durable files, and pending work belong outside it.
Once that distinction is clear, many serverless design decisions become much easier.
Serverless Changes Where State Lives, Not Whether State Exists
A serverless application can be deeply stateful.
It may track thousands of user sessions, millions of database records, terabytes of files, pending queue messages, account balances, workflow progress, and cached results. None of that disappears simply because the compute layer consists of temporary functions.
The architectural shift is that the function itself is no longer expected to be the durable home of that information.
Temporary Compute
│
▼
Read External State
│
▼
Perform Work
│
▼
Write External State
│
▼
Function Can Disappear
That is what makes functions easy to scale and replace. Any suitable instance can process the next request because the important history lives somewhere else.
Serverless isn’t stateless. Serverless works best when the state that matters is deliberately separated from the temporary function processing it. Treat the function as disposable, and keep anything you cannot afford to lose in a database, cache, storage system, queue, or another durable external service.





