Fix AWS Lambda Read-Only File System Error: Cannot Write to /tmp Explained

Logeshwaran.C

If your Lambda function throws "Read-only file system" or a write to /tmp is failing anyway, here's the direct answer: AWS Lambda only lets you write to one folder, /tmp, and everywhere else in the execution environment — your code's own folder, any Lambda layers, and the home directory — is locked read-only by design, on purpose, for every single function, every time. The part almost nobody expects: /tmp itself can quietly fill up over dozens or hundreds of invocations, not just one, because Lambda often reuses the same environment and keeps whatever you left behind in /tmp from the call before — so a function that "worked fine for three weeks" and then suddenly can't write anything usually isn't broken. It's full.

⚡ Quick Answer

Writing outside /tmp → change the path in your code to /tmp/yourfile.ext (or set the library's cache/home variable to point at /tmp). See Fix 1.

Error mentions /home/sbx_userXXXX → that's a library trying to use your "home folder," which doesn't really exist here. See Fix 2.

Error says "No space left on device" or ENOSPC, not "read-only" → you filled /tmp. Clean it up (Fix 5) or raise the ephemeral storage limit up to 10,240 MB (Fix 4).

If none of those match — especially if the error names a file inside /var/task and happens on the very first invocation — jump straight to the deployment-package permissions section. It's a different bug wearing the same costume.

Jake found this out the hard way on a Tuesday morning, which is somehow always when it happens. His phone shop runs a small Lambda function that generates a PDF receipt every time a customer trades in a device, then emails it to them. It had been running for six weeks without a single complaint. Then, out of nowhere, every trade-in that morning failed silently — no receipt, no email, and three annoyed customers standing at the counter while he tried to figure out why his own website had stopped working.

"The logs say 'Read-only file system,'" he told Ethan over the phone, reading it off his screen like it was a foreign language. "I didn't change anything. How does a file system just... become read-only?"

"It didn't become anything," Ethan said. "It was always read-only. You just hadn't hit the wall yet." That's the sentence that ends up mattering most in this whole topic, so it's worth sitting with it before touching any code.

Which Error Are You Actually Looking At?

"Read-only file system" is the umbrella term, but it covers at least four genuinely different problems, and the fix for one will do nothing for the others. Before changing a single line of code, look at the exact path named in the error. That path is the whole diagnosis.

What the error names What's really happening Where to go
/var/task/... Your code, or a library, is trying to write inside the folder your deployment package was unpacked into. That folder is never writable. Fix 1
/home/sbx_userXXXX A library assumes it has a normal home directory to cache things in. Lambda gives it a fake one that can't be written to. Fix 2
/tmp/... but it still fails Usually not "read-only" at all — check for ENOSPC / "no space left on device." You've filled the ephemeral storage. Fix 3
/opt/... Something is trying to write into a Lambda layer's mount point. Layers are read-only too, always. Edge cases
"Permission denied" / EACCES on first deploy This is not a filesystem restriction at runtime — it's a broken deployment package. Deployment packages

🙋‍♂️ Jake's Reality Check

"So which one is mine? I don't even know what half these folder names mean."

Copy the exact path out of your CloudWatch log and match it against the table above. Nine times out of ten, the folder name alone tells you the whole story before you've read another word of code.

Why Everything Except /tmp Is Locked Down

A Lambda function doesn't run on a server you own. Every time your function is invoked, AWS runs it inside something called an execution environment — think of it as a tightly sealed shipping container built just for your code, running on a small isolated virtual machine that AWS's own documentation dedicates to a single AWS account. Inside that container, AWS packs a private copy of your function's code, any Lambda layers you've attached, the language runtime you picked (Node.js, Python, Java, and so on), and one writable folder: /tmp. Everything else in that container is sealed shut before your code ever runs a single line.

Ethan's analogy for Jake: "Picture a hotel room. You get to use the little desk drawer — that's /tmp. You don't get to repaint the walls or move the furniture bolted to the floor. The walls are the code AWS already shipped into that room for this one guest. You didn't do anything wrong by trying to use them — the room was just never designed for that."

This isn't an accident or an oversight Amazon forgot to patch. It's a deliberate security boundary. AWS's own security documentation on Lambda's isolation model describes each execution environment as containing a dedicated, disposable copy of your function code, your layers, your runtime, a writable /tmp directory, and a minimal Linux user space — and nothing about that list includes a general-purpose writable disk. The read-only code directory means nobody, including a bug in your own code, can accidentally corrupt the deployment package mid-invocation. It also means AWS can safely reuse and recycle that same packaged code across thousands of parallel invocations without one invocation's write accidentally leaking into another's.

It's also worth being honest about what this restriction is not about. It isn't AWS trying to make your life harder, and it isn't a quota you can raise by asking support or upgrading a plan. Every AWS account, from a free-tier hobby project to the largest enterprise workload, gets the exact same read-only boundary around the code folder, because the boundary protects the isolation model itself, not any particular customer's usage tier. That's a genuinely different category of limit than something like a concurrency quota, and it's why no amount of IAM permission tweaking or execution-role adjustment will ever unlock it.

✅ Why this is the one to use

Don't fight the restriction. Every attempt to "unlock" /var/task, chmod your way around it, or run your function as a different user is wasted effort — the read-only mount is enforced above your code, not by a permission flag you can flip. The only real fix in every single case is redirecting the write to /tmp, or, when /tmp genuinely isn't the right tool, reaching for Amazon EFS instead. There is no third option.

Fix 1: Point Your Code (and Its Libraries) at /tmp

This is the fix for the most common version of the error — your own code tries to write a file using a relative path, or a path like ./output.csv or tmp/data.json without the leading slash, and it lands somewhere read-only by accident. It's a one-character bug more often than people expect: tmp/file.json (relative, treated as living inside your read-only code folder) is a completely different location from /tmp/file.json (absolute, the one folder Lambda actually lets you write to). AWS re:Post has fielded this exact typo more than once — a developer writing to tmp/filename.json, getting a read-only error, "fixing" it by adding the leading slash, and then hitting a second error because the file they expected to already be there, from a previous step, obviously wasn't — Lambda starts every fresh execution environment with /tmp completely empty.

Fixing this is almost always a one-line change:

  1. Search your handler code for every place a file gets opened, written, or a directory gets created — not just the obvious ones. Check anything that logs to a file, writes a cache, extracts a zip, or renders an image or PDF.
  2. Confirm each of those paths starts with /tmp/, not a relative path and not any other absolute folder.
  3. If the file needs a subfolder (for example /tmp/exports/receipt.pdf), create that subfolder first with your language's "make directory" call — Lambda gives you an empty /tmp, it doesn't create subfolders for you.
  4. Re-deploy and re-check the log. If the error is gone but a new one about "no such file or directory" appears, you skipped step 3.

The trickier version of this fix is when the write isn't in code you wrote at all — it's buried inside a library you imported. A PDF-generation package, an image-processing library, or an SDK might default to writing its scratch files relative to the current working directory, which inside a Lambda execution environment is /var/task, the read-only folder your code lives in. Most well-behaved libraries expose a setting for this — a constructor option, a config value, or an environment variable — that lets you tell it "use this folder instead." Read that library's own documentation for the exact option name rather than guessing; the fix is always the same shape (redirect to /tmp), even when the setting name is different for every library. If a library genuinely offers no way to change its write location at all, that's a sign the library itself may not be a good fit for a Lambda-style read-only environment, and it's worth checking whether the library's own project has an "AWS Lambda" or "serverless" note in its documentation before building further on top of it.

Fix 2: The $HOME Directory Trap (sbx_userXXXX errors)

If your error names a path like /home/sbx_user1051 or /home/sbx_user1083, you're not dealing with your own code at all — you're dealing with a library that assumes every program runs on a normal computer with a normal logged-in user and a normal home folder to keep settings and cache files in. Lambda's execution environment does technically have something that looks like a home directory path, but it isn't a real writable folder — it's part of the same locked-down filesystem as everything else. Command-line tools, machine-learning libraries, and SDKs that were originally built for a developer's laptop are the most frequent offenders here, because on a laptop that assumption is completely reasonable.

This shows up constantly with libraries that load large pretrained models or maintain their own local cache — the library tries to create a hidden folder in what it thinks is your home directory the first time it runs, fails, and throws the exact "read-only file system" error at import time, often before your handler code has run a single line. It's confusing precisely because the traceback points at library internals you never touched, which makes it look like something is broken in the library itself rather than in how it's being run.

🕐 What changed between versions

  • Before: for years, Lambda's /tmp storage was fixed at a flat 512 MB for every function, with no way to change it.
  • Now: AWS made ephemeral storage a configurable setting, letting you dial it anywhere from 512 MB up to 10,240 MB in 1 MB increments, and later expanded that larger-storage option into additional AWS Regions.
  • What that means for you: the "only 512 MB, take it or leave it" advice you'll still find in a lot of older blog posts and forum answers is out of date — if space itself is the problem, you likely don't need to redesign anything, just turn a dial.

The general fix for the home-directory trap is to explicitly point whichever environment variable that specific library reads for its home or cache location at /tmp instead, using Lambda's own environment variables feature (set under your function's Configuration tab, or in your infrastructure-as-code template). Which exact variable name matters depends entirely on the library — some read the standard HOME variable, others define their own cache-directory setting, and a smaller number don't expose a setting at all and instead rely on whatever the underlying operating system reports. Check that library's own setup or troubleshooting documentation for the correct variable name rather than assuming; setting the wrong one will silently do nothing, and setting the right one usually resolves the entire error in one deploy. As a general troubleshooting habit, it helps to set the variable, redeploy, and check whether the exact same path still appears in the new error — if it's changed to a different folder, you've successfully redirected the library and just need to redirect the new path too; if it's identical, you set the wrong variable.

Fix 3: When /tmp Itself Runs Out of Room

Here's the part that catches experienced developers off guard, not just beginners: once you've correctly pointed everything at /tmp, you can still get an error that looks a lot like "read-only file system" — except the underlying message is actually about space, not permission. Check the exact wording in your log. "No space left on device," or an error code referencing ENOSPC, means the folder itself is writable — you've simply filled it.

This is where the counterintuitive part of Lambda's design actually bites. AWS's own documentation on how the execution environment works is explicit that /tmp is not wiped clean after every single invocation the way most people assume a "temporary" folder should be. When Lambda reuses an execution environment for a later invocation — which it does constantly, to avoid the slower cold-start path — whatever files were left behind in /tmp from the previous invocation are still sitting there. AWS's own security whitepaper spells out the implication directly: data written by one invocation can be read by a later invocation of the same function, if that later invocation happens to land in the same execution environment. That's intentional, and genuinely useful for caching large files you'll need repeatedly. It's also exactly how a "small" temp file quietly becomes a full disk three weeks later, with nothing in your code having changed at all.

⚠️ What this actually breaks

A function that writes a 40 MB file per invocation and never deletes it will run perfectly for roughly 12 invocations against the default 512 MB ephemeral storage limit — as long as Lambda keeps reusing the same execution environment — then fail on the 13th with no code change and no deployment in between. It looks like a random, unexplainable outage. It's arithmetic.

Fix 4: Raising the Ephemeral Storage Limit

If your function genuinely needs more room — downloading a large machine-learning model, unpacking a big zip file, converting a large image or video, or handling sizeable files pulled from S3 — the honest fix is often simply to give it more space, rather than rewriting your logic to squeeze into 512 MB. AWS calls this setting ephemeral storage, and it controls the size of the function's /tmp directory directly. The default is 512 MB, and it can be set to any whole number between 512 MB and 10,240 MB, in 1 MB increments. All data written there is automatically encrypted at rest with a key AWS manages for you — you don't configure that part, it's just always on.

Method Where you set it Best for
Console Function → Configuration tab → General configuration → Edit One-off fixes, testing a size quickly
AWS CLI update-function-configuration --ephemeral-storage Scripted changes, CI/CD pipelines
AWS SAM EphemeralStorage: Size property in template.yaml Infrastructure-as-code, repeatable deployments

Doing it from the console is the fastest way to confirm the fix works before you commit to it anywhere permanent:

  1. Open the Functions page in the Lambda console and choose the function that's failing.
  2. Select the Configuration tab, then General configuration.
  3. Choose Edit.
  4. Set the Ephemeral storage value to whatever you need, anywhere from 512 MB up to 10,240 MB, in 1-MB steps.
  5. Choose Save, then re-run the invocation that was failing.

If you'd rather script it, the AWS CLI equivalent is a single command: aws lambda update-function-configuration --function-name my-function --ephemeral-storage '{"Size": 1024}'. In a SAM template, the same setting lives directly under your function's properties as EphemeralStorage: Size: 10240.

Ethan's blunt opinion here, aimed squarely at Jake's instinct to rewrite half his codebase: "If the honest answer is 'my function legitimately needs 2 GB of scratch space,' don't spend a weekend building a workaround. Change one number. This is the cheapest fix on this entire page, and people skip straight past it because it feels too easy." Jake pushed back on that one: "Cheap in effort, sure. What about cheap in the bill?" Ethan's answer was honest rather than reassuring: "That part depends on your account and Region, so don't take a number from a blog post as gospel — check your own Lambda cost details before you crank it up to the max out of habit."

Fix 5: Stopping the Slow Leak — Cleaning Up Between Invocations

Raising the ephemeral storage limit buys you room, but it doesn't fix the actual habit of leaving files behind. If your function's job is genuinely temporary — convert this file, resize this image, extract this zip, send the result somewhere else — the file it created has no reason to still exist once that invocation finishes. AWS's own security guidance recommends exactly this: delete files from /tmp before your function exits, rather than assuming the next invocation will get a clean slate.

There's a second, quieter reason to clean up, beyond just running out of disk: because the same execution environment can be reused by later invocations of the same function, a file left behind from one customer's request can technically still be readable during a completely different customer's request, if that later request happens to land in the same warm environment. AWS's own documentation flags this directly and recommends naming any files you do need to keep around per-invocation with a unique identifier — something like a UUID — specifically so a different invocation reusing the same environment can't accidentally read or overwrite something meant for someone else.

✅ Why this is the one to use

Treat cleanup as part of the function's actual job, not an optional nicety at the end. A short "delete what I just made" step, run right before your handler returns, costs almost nothing in runtime and removes an entire category of "it worked in testing" bugs that only show up after real production traffic.

The one case where you deliberately want to keep files in /tmp is when they're genuinely reusable across invocations — a large reference file, a machine-learning model, a lookup table that's expensive to fetch every time. For that pattern, check whether the file already exists in /tmp at the start of your handler before re-downloading it; if it's there, use it; if not, fetch it once and leave it. That's the intended use of a warm execution environment's persistence, and it can meaningfully cut your function's average run time. The discipline that matters is knowing which category a given file falls into — genuinely reusable, or genuinely single-use — and treating the two differently in code, rather than letting every file default to "just leave it there and hope."

The Impostor: Deployment Package Permission Errors

Here's a genuine surprise for anyone who's assumed every "permission denied" from Lambda is about /tmp: sometimes it isn't a filesystem restriction at runtime at all. It's a broken .zip file. AWS's own knowledge base documents this exact scenario — you upload a deployment package, and instead of your function running, you immediately get a "permission denied" or "unable to import module" error, often for a file that has nothing to do with anything you're writing.

The cause is almost always how the package was built, not what your code does at runtime. Lambda requires the files and folders inside your deployment package to carry standard, globally readable Unix permissions. For interpreted languages like Python or Node.js, that means individual files need permission 644 and folders need 755. For compiled languages, like Go, both the executable files and the folders around them need 755. This trips people up constantly when the package is built on Windows, or built by a continuous-integration tool that doesn't preserve Unix-style permissions the way a Linux build environment does — the zip file that worked perfectly on a developer's laptop simply doesn't carry the right permission bits once it's rebuilt somewhere else.

🙋‍♂️ Jake's Reality Check

"I build my deployment package on my Windows laptop before uploading it. Is that the actual problem?"

It can be, yes. Because Lambda enforces POSIX (Linux-style) permissions and Windows doesn't track files the same way, the safest approach is building the package on a POSIX-compliant system — a Linux machine, macOS, Windows Subsystem for Linux, or a Docker container — so the permission bits Lambda expects actually make it into the zip file.

The tell that separates this from a genuine /tmp problem: it usually happens instantly, on the very first invocation after a fresh deploy, and it names a file that's part of your own uploaded code or a dependency — something like index.js sitting right inside /var/task — rather than a file your handler is actively trying to write mid-run. If your function ran successfully at least once before this error appeared, it's not a deployment-permissions problem; go back to the diagnostic table above.

Container Image Functions: Same Rules, Different Packaging

If your function is deployed as a container image rather than a plain .zip file, the underlying execution environment model is the same one described throughout this post — a dedicated, isolated environment with a single writable directory at /tmp. Packaging your code as a Docker image doesn't grant your function a general-purpose writable filesystem; it changes how the code gets onto AWS, not what it's allowed to do once it's running. Anything your Dockerfile bakes into the image — installed packages, model files copied in at build time, application code — behaves the same way the contents of /var/task do for a .zip-based function: available to read, not available to write.

This matters most for teams who containerized a function specifically because it needed heavier dependencies — a large machine-learning library, a headless browser, a compiled binary — and assumed the extra flexibility of a container also meant a more permissive filesystem. It doesn't. Anywhere your application inside that container tries to write, whether that's a cache folder, a log file, or a scratch directory, still needs to land in /tmp, and it's still subject to the same ephemeral storage size limit described earlier in this post.

A subtler version of this trips up teams building the image itself, rather than teams running it. If your Dockerfile has a build-time step (a RUN instruction, for example) that tries to write to /tmp as a way of "preparing" data ahead of time — generating a cache file, pre-downloading a model — that write happens during the image build, on Docker's own build filesystem, not inside a real Lambda execution environment at all. Files created that way do get baked into the image and are readable at runtime, which is often exactly what you want for something like a bundled reference file. But it's worth being clear-eyed about the distinction: a build-time write into what will become /tmp in the finished image is not the same thing as a runtime write into the live, size-limited ephemeral storage your function gets when it's actually invoked, and code that assumes it can keep writing more data into that same location once the function is running will still hit the same read-only or space limits as any other container-packaged function.

When /tmp Isn't Enough: Mounting Amazon EFS

Everything above assumes /tmp is the right tool — and for genuinely temporary, per-invocation, or per-warm-environment scratch space, it is. But /tmp has a ceiling that no configuration change will lift: 10,240 MB, and it's never shared between different execution environments, meaning two concurrent invocations of the same function can each have their own, completely separate /tmp with no visibility into each other's files.

Jake ran into the shape of this problem long before he ever touched Lambda, back when his shop only had one shared till drawer for two registers. Two staff members reaching for the same drawer at the same time meant somebody's cash count was always slightly wrong at closing. Splitting into two separate, properly assigned drawers fixed it instantly — and that's more or less what separate /tmp folders per execution environment are doing for you automatically, whether you asked for it or not. The catch is the opposite of Jake's till problem: sometimes you actually want one shared drawer that every register can see and write to at the same time, and that's precisely the job /tmp was never built for.

For that use case, AWS Lambda supports mounting an Amazon Elastic File System (EFS) volume directly to a local directory inside your function, giving your code shared, concurrent, persistent storage that survives well beyond any single execution environment's lifetime. Setting this up is a genuinely different piece of infrastructure than flipping the ephemeral storage number, though: your function needs to run inside a VPC with a mount target for the file system in each Availability Zone it connects to, and its execution role needs the elasticfilesystem:ClientMount permission (plus elasticfilesystem:ClientWrite if it needs to write, not just read) — both included in the AWS-managed policy AmazonElasticFileSystemClientReadWriteAccess. Whoever configures the connection in the console or via infrastructure code also needs elasticfilesystem:DescribeMountTargets to verify the mount targets exist.

⚠️ What this actually breaks

Reaching for EFS to solve a problem that's really just "my /tmp is too small" is over-engineering that adds VPC networking, mount-target management, and IAM permission complexity you didn't need. Try raising ephemeral storage first. Only move to EFS when the requirement is genuinely about sharing data between separate invocations or separate functions at the same time, not just needing more room for one function's own scratch work.

Edge Cases: VPC, Provisioned Concurrency, SnapStart, and Layers

A handful of configurations change the details around this problem without changing the core rule.

Lambda layers: a layer's contents get mounted at /opt inside your execution environment, and that mount point is read-only in exactly the same way your function's own code folder is. If your error names a path under /opt, the fix isn't different — whatever's trying to write there needs to write to /tmp instead. Layers exist to share code and dependencies across functions, not to provide extra writable disk.

Provisioned concurrency and SnapStart: both of these features change how quickly and how often Lambda prepares an execution environment ahead of an actual invocation, but they don't change what's writable inside it once your code runs. SnapStart in particular saves a snapshot of an already-initialized environment's memory and disk state and reuses it for faster starts — which is a good reminder that whatever you wrote to /tmp at initialization time could genuinely still be present when a later invocation resumes from that snapshot, so the same cleanup discipline from Fix 5 applies just as much here, arguably more.

VPC-connected functions: running your function inside a VPC (which is required for the EFS option above) doesn't change the read-only rules for /tmp, /var/task, or /opt in any way. VPC configuration governs network access — what your function can talk to — not filesystem permissions inside the execution environment. Don't expect VPC networking changes to fix a read-only file system error on their own; they solve a completely different category of problem.

Execution environments aren't kept forever, even if you never stop calling the function: AWS is explicit that Lambda periodically terminates execution environments for maintenance and runtime updates, on a schedule of a few hours, regardless of how continuously your function is being invoked. Don't design any logic around the assumption that a warm environment, or the files sitting in its /tmp, will be there indefinitely. Anything genuinely important belongs in S3, DynamoDB, or EFS — not in the temp folder of an environment AWS can recycle without warning.

Libraries That Misbehave: Headless Browsers, ML Models, and Git

Certain categories of tooling hit this wall so consistently that it's worth naming them directly, because the confusion is usually the same each time: the tool wasn't built with a read-only-filesystem environment in mind, and it assumes it can write scratch files wherever it feels like.

Headless browsers used for screenshots or PDF rendering often try to launch with a default user-data or profile directory that lives outside /tmp. Most support an explicit launch option for the user-data directory or executable path — point it at a folder inside /tmp that your code creates first.

Machine-learning libraries that download pretrained models frequently cache those models in a hidden folder under what they assume is your home directory, which, as covered above, isn't writable in Lambda. These libraries almost always expose a cache-directory setting for exactly this reason, because container and serverless environments hit this constantly — check the library's own documentation for the correct configuration name and redirect it to /tmp.

Command-line tools invoked from within your function — version control tools, image or document converters shelled out to as external processes — often assume a real, writable home directory for their own config and cache files. If you're shelling out to an external binary from your handler, treat it the same way as any other library: find its home-directory or cache-path setting and redirect it before invoking it.

Ethan, when Jake asked why AWS doesn't just fix these libraries for everyone: "It's not really AWS's job to patch every library on Earth. These tools were built assuming a normal desktop or server. Lambda is neither of those things, on purpose — that's the whole point of it being cheap and fast to run. The tradeoff is that you sometimes have to tell an old tool where the new world's one writable drawer is."

The Privacy Angle: What's Actually Sitting in /tmp

Once you know that /tmp content can persist across multiple invocations of the same function, a reasonable next question is what that means for anything sensitive your function ever writes there — a customer's uploaded document, a generated invoice with someone's name and address on it, an exported report. AWS encrypts everything written to /tmp at rest automatically, using a key AWS itself manages; that part isn't something you need to configure or worry about forgetting. But encryption at rest protects the data from someone getting at the underlying disk — it doesn't protect it from your own function's next invocation quietly reading a file it didn't create.

AWS's guidance on this is specific: never assume data written to /tmp during one invocation is private to that invocation, because a later invocation reusing the same warm execution environment can read it. If you're processing anything sensitive — personal information, uploaded files, generated documents containing customer data — the practical response is the same cleanup discipline covered in Fix 5, applied a little more strictly: delete the file the moment you're done with it, and if two different requests could theoretically be handled by the same warm environment back to back, use unique, unpredictable filenames (a UUID, not a sequential counter or the customer's own name) so one invocation can't accidentally stumble onto a file left by another.

Catching This Before Your Customer Does: Monitoring /tmp Usage

The most frustrating version of this whole problem is exactly what happened to Jake: nothing changes on your end, and weeks later the function starts failing anyway, because the disk filled up gradually rather than all at once. The fix from a code standpoint is Fix 5 — delete what you don't need. The fix from an operational standpoint is knowing about it before your customers do.

A practical habit worth building into any function that writes anything meaningful to /tmp: log the amount of free space remaining, or the size of the largest files present, right before your handler returns, especially in early development and testing. Because Lambda automatically sends everything your function writes to standard output into CloudWatch Logs, that single log line is enough to build a rough trend over time just by scanning your function's log group — no separate monitoring tool required to get started. It costs almost nothing, and it turns a mystery ENOSPC failure three weeks from now into a clear, rising trend you can see coming in your logs long before a real customer hits it.

Once you've confirmed the trend actually exists, the next step is usually one of two things, not both: either the cleanup habit from Fix 5 was genuinely missing somewhere and needs fixing at the source, or the workload legitimately needs more room than the current ephemeral storage setting allows and it's time to revisit Fix 4. Trying to solve a genuine "not enough space for this workload" problem purely with more aggressive cleanup will just trade one failure mode (a full disk) for another (deleting a file your own function still needed), so it's worth being honest with yourself about which of the two you're actually looking at before picking a fix.

Automating the Fix So It Never Happens Again

Once a function has hit this bug once, the sane move is to make it structurally impossible to hit again, rather than trusting that everyone who touches the code later remembers the rule. A few habits do most of the work:

Build a single "temp path" helper in your codebase that every part of your function calls instead of writing raw path strings. If that one helper always returns a path under /tmp, nobody on your team can accidentally reintroduce a relative path or a hardcoded folder somewhere else in the code, because there's only one place paths get built.

Wrap cleanup in a "finally" block (or your language's equivalent) around any code that writes to /tmp, so the delete happens whether the function succeeds or throws an error partway through. A function that only cleans up on the success path will still leak files every time it fails, which, for a function that's already misbehaving, is exactly when you don't want extra debris piling up.

Set ephemeral storage deliberately, not by accident. If your function's expected workload genuinely needs more than the 512 MB default, set that explicitly in your infrastructure template rather than discovering the limit in production and bumping it reactively. Infrastructure-as-code tools like AWS SAM make this a single line, and it documents the decision for the next person who opens the template.

If the real problem is... Fix effort Do this first
A wrong or relative path Minutes Fix 1
A library assuming a home directory Minutes to an hour Fix 2
Files piling up over time An hour, code change Fix 5, then Fix 3
Genuinely large per-invocation files Minutes, config only Fix 4
Multiple invocations need shared, concurrent writes Hours to days, new infra Amazon EFS

Frequently Asked Questions

Why can't I write anywhere except /tmp in AWS Lambda?

Because every Lambda invocation runs inside a sealed, disposable execution environment that AWS builds fresh for your function, and only one folder in that environment, /tmp, is designated as writable. Your code's own folder, any attached layers, and the rest of the filesystem are locked read-only as a security and isolation measure, not a bug or a limitation you can configure away.

Is /tmp really writable, or can it fail too?

It's genuinely writable, but it has a size limit — 512 MB by default, configurable up to 10,240 MB. Once you exceed that limit, writes to /tmp fail too, but the error is about running out of space, not about permission, so check the exact wording in your log rather than assuming it's the same bug.

How much space do I get in /tmp by default?

512 MB, for every function, unless you explicitly configure a larger value. That default hasn't changed, but the ceiling above it has — you can now raise it yourself.

Can I increase the /tmp size, and does it cost extra?

Yes, you can raise it to anywhere between 512 MB and 10,240 MB, in 1 MB increments, through the console, the AWS CLI, or infrastructure-as-code tools like AWS SAM. Whether a given size costs more than the default in your account depends on your current Lambda pricing details, which can vary and change over time, so check the Billing section of your own AWS account or the Lambda pricing page directly rather than relying on a fixed number from any article, including this one.

Why did my function work for weeks and then suddenly throw a read-only or no-space error?

This is almost always /tmp filling up gradually, not a sudden new bug. Lambda reuses execution environments, and files left in /tmp from earlier invocations can still be there later. If your function writes files without deleting them, the disk fills invocation by invocation until it hits the ephemeral storage limit.

Does /tmp get wiped between every invocation?

No, not necessarily. It's wiped when Lambda creates a brand-new execution environment (a cold start), but if your function's invocation is handled by a reused, already-warm environment, whatever was in /tmp from before is still there. This is intentional and documented by AWS as a way to amortize expensive downloads across multiple invocations, but it means you can't assume a clean /tmp every time.

Is data in /tmp shared between different Lambda functions or different customers?

No. Each execution environment is dedicated to a single function, and AWS's own documentation states this storage is not accessible or shared across execution environments, and execution environments are never shared across AWS accounts. What can happen is data from one invocation being read by a later invocation of the very same function, if both land in the same reused environment. That's a different, much narrower thing than cross-customer or cross-function sharing.

I'm getting "Read-only file system: /home/sbx_userXXXX" — what is that path?

That's a stand-in home directory some part of your function's environment presents to libraries that expect one. It behaves like the rest of the read-only filesystem, not like a real home folder you can write to. This error almost always comes from a library trying to cache or configure itself in what it assumes is your home directory; the fix is redirecting that library's home or cache setting to /tmp.

My Node.js function says "EACCES: permission denied, open '/var/task/...'" — is that the same bug?

It can be either of two different problems. If it happens on the very first invocation right after a deploy, it's likely a deployment package built without the correct file permissions (644 for files, 755 for folders, on interpreted runtimes). If it happens after your function has already run successfully several times, it's more likely your code or a library attempting to write into your read-only code folder mid-run, which needs the same /tmp redirect as any other read-only error.

Does a Lambda container image (Docker-based function) have the same restriction?

Yes. The execution environment model, including the single writable /tmp directory, applies regardless of whether your function is packaged as a .zip file or as a container image. Packaging format changes how your code gets deployed to AWS, not what your code is allowed to write to once it's running.

Can I use a Lambda layer to add a writable folder?

No. Layers are mounted at /opt inside your execution environment, and that mount point is read-only, the same as your function's own code folder. Layers are meant for sharing code, libraries, and dependencies across functions, not for adding writable disk space.

What's the difference between /tmp filling up and a true read-only file system error?

A true read-only error means the destination folder itself can never be written to, no matter how much free space exists anywhere. A full /tmp is a completely writable folder that has simply reached its configured size limit; the error message wording is usually different (mentioning "no space" or an ENOSPC-style code) and the fix is different too: clean up files or raise the ephemeral storage size, not redirect the path.

How do I permanently share files between multiple Lambda invocations or functions?

Use storage designed for that, not /tmp. Amazon S3 works well for storing individual files that multiple invocations or functions need to read or write. For a genuinely shared, concurrent, persistent filesystem that behaves more like a traditional network drive, AWS Lambda supports mounting an Amazon EFS file system directly, though that setup requires running your function inside a VPC with the appropriate mount targets and IAM permissions configured.

Will increasing memory size also increase /tmp space?

No, they're two separate settings. Memory size controls the compute resources (and, indirectly, proportional CPU) allocated to your function. Ephemeral storage is a distinct setting that specifically controls the size of /tmp, and you configure it independently of memory.

Does provisioned concurrency or SnapStart change how /tmp behaves?

Not the fundamental rule, no — /tmp is still the only writable directory. But both features change when and how an execution environment's initial state, including whatever is already in /tmp, gets prepared and reused, particularly SnapStart, which saves a snapshot of the environment's memory and disk state. If your function uses either feature, the discipline of cleaning up /tmp before the handler finishes matters at least as much, since a snapshot can preserve leftover files across more invocations than a typical warm environment would.

What's the safest way to clean up /tmp so it doesn't fill up silently?

Delete any file your function created as soon as you're done with it, ideally in a "finally"-style block so cleanup happens even if the function errors out partway through. If you need a file to persist across invocations intentionally (a reusable model or reference file), check for its existence at the start of your handler rather than always recreating it, and give per-invocation temporary files unique, unpredictable names so a reused environment can't confuse one invocation's file for another's.

Revision note. Written September 2026. This will need a fresh look if AWS changes the ephemeral storage size limits, pricing, or the way execution environments are reused. If you're reading this at 11pm with three customers waiting on a receipt that won't generate, you're not doing anything wrong — this genuinely trips up experienced teams, and the fix really is usually just one path, one number, or one delete statement away.

Related