When moving local files to S3, is EFS a good interim step?
Question
My app keeps user images on local disk and uses local libraries for image manipulation. When I want horizontal scaling (autoscaling), this becomes the bottleneck. How do I manage the move to S3 while touching the legacy code as little as possible and minimizing latency? Is EFS a good interim step, or should I build a proper object-storage adapter architecture?
Answer
Short answer: don’t pick EFS as the destination — it’s a trap. The real fix is an object-storage adapter, and in Laravel the Flysystem you already have makes that a very short job.
Your blocker isn’t really “local disk”, it’s the “assume a shared mount” assumption. The code expects a raw path, and once you scale, that path isn’t shared across nodes.
- EFS is a trap as the destination. It removes the “local disk” blocker but keeps you on a network filesystem: worse latency, higher cost, and the same coupling (the code still thinks there’s a shared mount). Use EFS only as a short-lived bridge if you genuinely can’t touch the code yet — never as the endpoint.
- The right move is an object-storage adapter. Flysystem is already there in Laravel; set the disk to
s3and route every read/write throughStorage::disk()instead of a raw path. Your business logic shouldn’t know “where the file is”; hide it behind the abstraction the framework gives you. This is boringly proven, and that’s exactly why it’s right. - Don’t assume a shared mount for image manipulation. Pull the file to a
tmpfile and process it, or do it with a worker + presigned URLs. Break the “the disk is always here” assumption; take input/output from object storage and write the result back. - Keep legacy churn minimal. Hide all file access behind the framework’s filesystem abstraction; flip it with a single disk config rather than migrating each call site to
s3by hand. The smaller the surface you touch, the lower the risk. - Migrate with dual-read. Run for a while on “read S3 first, fall back to local” while you backfill existing images in the background. Once everything’s moved, flip reads fully to S3 and decommission local disk.
Bottom line: don’t spend money and latency on EFS. I’d go straight to a Flysystem s3 disk, read/write everything through Storage::disk(), and solve manipulation with a temp file/worker. Migrate with dual-read for zero downtime, and once the backfill is done, remove local disk. Boring, but the kind of architecture you set up once and forget.
Related Reading
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.