Amazon EFS: Shared Workspaces Across Multi-Turn Coding-Agent Tasks

0 views

Turn 1 cloned acme/monorepo (4.2 GB, 90s). Turn 2’s Fargate Spot sandbox cloned it again. Turn 3’s CodeBuild project cloned it a third time — then GitHub rate-limited your bot identity. Amazon EFS is the unfair advantage when a coding agent needs the same worktree across Lambda, Fargate, and CodeBuild steps without re-cloning: mount once, mutate carefully, evict on session end. Pair with Fargate Spot sandboxes for compute and S3 conditional writes for durable artifacts — this post is the shared workspace layer.

⚡ TL;DR: Use EFS Access Points per tenant/session under /tenants/{id}/sessions/{sid}. Prefer Elastic throughput for spiky agent clones; Provisioned only when you can predict steady MiB/s. Lock NFS security groups, enforce POSIX via access points, and never store secrets on EFS. Related: MemoryDB session state, Step Functions graphs, CodeBuild sandboxes.

When re-cloning becomes the bottleneck

Multi-hop agent graphs often look like:

  1. Planner (Lambda) decides “run tests”
  2. Sandbox (Fargate Spot) needs the repo
  3. Heavyweight lint (CodeBuild) needs the same tree
  4. Apply/PR step needs the mutated tree again

If each step starts from git clone, you pay network, forge API, and wall-clock on every hop. ElastiCache/MemoryDB hold session cursors, not trees (MemoryDB). S3 holds artifacts, not a live POSIX worktree. EFS fills the gap: NFS-mounted shared disk for the active session.

Store Latency Shared POSIX Best for
Re-clone each step ❌ Slow N/A Tiny repos only
S3 sync each step Medium ❌ Object API Artifacts / tarballs
EBS on one task Fast ❌ Single AZ/task Single long sandbox
EFS Access Point Good ✅ Multi-compute Multi-step same tree
MemoryDB / Redis ✅ Sub-ms ❌ Bytes/keys Cursors, scratch JSON

Access points, not naked mounts

Never mount the filesystem root into an agent task. Use EFS Access Points that enforce a chroot-like path and POSIX uid/gid so tenant A cannot ls tenant B’s worktree.

typescript
// ✅ EFS + access point per tenant session (CDK)
import * as efs from "aws-cdk-lib/aws-efs";
import * as ec2 from "aws-cdk-lib/aws-ec2";

const fs = new efs.FileSystem(this, "AgentWorkspaces", {
  vpc,
  encrypted: true,
  performanceMode: efs.PerformanceMode.GENERAL_PURPOSE,
  throughputMode: efs.ThroughputMode.ELASTIC, // ✅ spiky agent clones
  // ❌ Bursting alone often throttles after large monorepo clones
  removalPolicy: cdk.RemovalPolicy.RETAIN,
});

const ap = new efs.AccessPoint(this, "TenantSessionAp", {
  fileSystem: fs,
  path: "/tenants/placeholder/sessions/placeholder",
  createAcl: { ownerGid: "1000", ownerUid: "1000", permissions: "750" },
  posixUser: { uid: "1000", gid: "1000" },
});

// Security group: NFS 2049 from sandbox SG only
fs.connections.allowDefaultPortFrom(sandboxSg, "NFS from agent sandboxes");
python
# ❌ Mounting EFS root with root posix — cross-tenant footgun
mount = {
    "sourceVolume": "efs-root",
    "containerPath": "/mnt/efs",
    "readOnly": False,
}
# any task can walk /mnt/efs/tenants/*/...

For dynamic sessions, create the access point (or a directory under a parent AP) at session start from a control-plane Lambda with elasticfilesystem:CreateAccessPoint, tag it tenant_id / session_id, and delete on DynamoDB Streams expiry hooks.

Wiring Lambda, Fargate, and CodeBuild to the same tree

Fargate / ECS: add an efsVolumeConfiguration with authorizationConfig.accessPointId and transitEncryption: ENABLED.

Lambda: configure a file-system mount (same AP). Keep Lambda for light reads (grep, small patches); heavy npm ci belongs on Fargate/CodeBuild — Lambda storage and time limits still apply.

CodeBuild: use file_system_locations / VPC config so the build project mounts the same AP. Prefer this over git clone in buildspec when the tree already exists from a prior Step Functions task.

json
// ✅ ECS mount fragment — access point + transit encryption
{
  "name": "workspace",
  "efsVolumeConfiguration": {
    "fileSystemId": "fs-0abc",
    "transitEncryption": "ENABLED",
    "authorizationConfig": {
      "accessPointId": "fsap-0def",
      "iam": "ENABLED"
    }
  }
}

Orchestrate with Step Functions: Task A populates /work/repo, Task B runs tests on the same path, Task C packages a diff to S3 and marks the session ready for eviction.

Throughput modes that match agent traffic

  • Elastic — default pick for coding agents: clone spikes, then quiet turns. You pay for metered throughput without babysitting MiB/s.
  • Provisioned — only if CloudWatch shows sustained throughput needs (large binary assets, many parallel tenants on one FS).
  • Bursting — easy to hit credit exhaustion after a few fat monorepo clones; avoid as the sole mode for production agents.

Watch StorageBytes, PercentIOLimit, Throughput (and Elastic’s metered metrics). If many tenants share one FS, consider one FS per OU/tier to bound noisy neighbors — not one giant FS for the whole SaaS.

Security groups and IAM

  1. EFS mount targets in private subnets
  2. NFS 2049 only from sandbox / CodeBuild / Lambda ENI security groups
  3. IAM auth on mounts (elasticfilesystem:ClientMount, ClientWrite, ClientRootAccess denied)
  4. KMS CMK for encryption at rest; separate from data-plane tool keys (KMS grants)
  5. No secrets, PATs, or .env on EFS — use Secrets Manager (next post)
  6. Cap what accounts can create with SCPs
json
// ✅ IAM for task role — mount specific AP only
{
  "Effect": "Allow",
  "Action": ["elasticfilesystem:ClientMount", "elasticfilesystem:ClientWrite"],
  "Resource": "arn:aws:elasticfilesystem:us-east-1:123:access-point/fsap-0def",
  "Condition": {
    "Bool": { "elasticfilesystem:AccessedViaMountTarget": "true" }
  }
}

What EFS is not

  • ❌ Not your source of truth for merges — push commits / upload patch artifacts to S3/git
  • ❌ Not a vector DB — use OpenSearch Serverless or Bedrock KB for semantic recall
  • ❌ Not a substitute for CodeArtifact package caches (optional overlay cache OK, but pin registries)
  • ❌ Not infinitely cheap — Elastic throughput + storage for large trees add up; evict cold sessions

Production checklist

  • [ ] One access point (or enforced path) per tenant session; no root mounts
  • [ ] Transit encryption + IAM auth enabled on all mounts
  • [ ] NFS SG locked to agent compute SGs only
  • [ ] Elastic throughput unless metrics justify provisioned
  • [ ] Step Functions (or equivalent) owns populate → mutate → artifact → evict
  • [ ] Session TTL deletes AP/data; Streams/expiry hooks wired
  • [ ] Secrets never written to the worktree; .gitignore / cleanup on exit
  • [ ] CloudWatch alarms on IO limit / storage growth per FS

FAQ

Q: Can Lambda and Fargate write concurrently to the same files?
A: POSIX locking is weak across NFS for many tools. Prefer single-writer per session (orchestrator mutex) or separate worktrees with merge at the end.

Q: EFS vs re-hydrating a tarball from S3 each step?
A: Tarballs win for immutable snapshots; EFS wins when multiple steps mutate the same tree interactively. Many teams use both: nightly warm tarball → EFS, then incremental git fetch.

Q: Multi-AZ?
A: EFS is regional and mount-target HA across AZs — good for sandboxes spread across AZs. Do not confuse that with cross-region DR; replicate artifacts to S3 for that.

Amazon EFS turns multi-turn coding agents from “clone farmers” into “workspace sharers.” Mount through access points, meter throughput honestly, and always graduate durable results off NFS onto git/S3 before the session dies.

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.