Mounting S3 as 'local' storage for development — and the night it silently rewrote three hours of work

I mounted an S3 bucket with s3fs to treat assets as local files for fast dev. It saved me data and time — until caching semantics silently corrupted edits. How I use it now, safely.

Written by: Arjun Malhotra

A laptop on a wooden desk with code visible on the screen, next to a coffee cup
Photo by Austin Distel on Unsplash

The panic starts small. It’s 11:20pm before a demo to a client in Bengaluru. My laptop’s cosy little terminal bell rings, I save a change to an image asset, and a second later the UI in the browser still shows the old asset. I hit refresh, then “revert” in my editor by accident. The change is gone. Three hours of fiddling photos and CSS are overwritten by what looks like an earlier copy.

I had mounted our S3 assets as a filesystem with s3fs. The idea was sensible: avoid downloading hundreds of megabytes of assets, keep builds fast on an intermittent home connection, and let my apps read/write like regular files. For weeks it worked. Then one evening the combined nastiness of s3fs’s cache, eventual consistency on certain object ops, and my mental model of “local file = single source of truth” blew up.

If you’re tempted to mount S3 for development, here’s the exact experience I learned from — including the failure, the tradeoffs, and the setup I still use.

Why I mounted S3 in the first place

The way I did it (the quick version)

What went wrong (the honest failure) Two nights before the demo I edited an image file in-place (crop + save). My editor wrote a temp file and renamed it over the original — normal POSIX behaviour. s3fs caches directory listings and objects locally and lazily syncs writes back to S3. But the default write semantics + the rename trick confused the cache: s3fs removed the cached object and queued an upload of the new file, while another process (a background sync from CI or my phone app) read the older object from S3 and re-wrote it. The end state was S3 holding the old image, my local cache pretending the new one was present, and my editor happily saving a timestamp that didn’t match S3.

I pulled the latest from staging, and git merged the “old” file back into my branch, because my local filesystem had stamped different timestamps. In short: a mounting layer with lazy writes and eventual remote consistency lied to me about which version was authoritative. I lost the last three hours and learned the hard way that “local” ≠ “single writer, single truth” with cloud object stores.

What I wish I’d known

How I use S3 mounts now (the practical setup I actually trust) I still mount S3 for read-heavy local development, but with strict rules and tweaks:

  1. Read-only for primary workflow
  1. If you must write, make it explicit and lock it
  1. Use goofys for metadata accuracy when reads dominate
  1. Cache safely
  1. Monitor requests and bill shock
  1. Treat destructive operations as staging-only

Where this still breaks for me

Final takeaway Mounting S3 as a “local” filesystem is a useful hack when you need fast, read-heavy dev against a shared asset set — especially with Indian internet constraints and mobile data costs. But you must treat it as a convenience layer, not as a source-of-truth filesystem. Make writes explicit, disable lazy tricks for mutating workflows, and accept the tradeoff: speed now, complexity later.

I still use a mount every day. But I no longer let it pretend it’s the one true filesystem.