EFS bills for every GB you store, so a file system that only grows quietly becomes expensive. Lifecycle management fixes that by moving cold files to cheaper storage classes automatically. Separately, performance and throughput modes decide how fast the file system is and how you pay for that speed.
This page shows how to set a lifecycle policy that tiers files to Infrequent Access and Archive, how to bring files back on access, and how to choose (and change) throughput mode. All operations use @aws-sdk/client-efs.
# --- Python (boto3) ---import boto3efs = boto3.client("efs")# Tier cold files down, and pull them back to primary on first access.efs.put_lifecycle_configuration( FileSystemId="fs-0123", LifecyclePolicies=[ {"TransitionToIA": "AFTER_30_DAYS"}, {"TransitionToArchive": "AFTER_90_DAYS"}, {"TransitionToPrimaryStorageClass": "AFTER_1_ACCESS"}, ],)
// --- TypeScript (AWS SDK v3) ---import { EFSClient, PutLifecycleConfigurationCommand } from "@aws-sdk/client-efs";const efs = new EFSClient({});// Tier cold files down, and pull them back to primary on first access.await efs.send(new PutLifecycleConfigurationCommand({ FileSystemId: "fs-0123", LifecyclePolicies: [ { TransitionToIA: "AFTER_30_DAYS" }, { TransitionToArchive: "AFTER_90_DAYS" }, { TransitionToPrimaryStorageClass: "AFTER_1_ACCESS" }, ],}));
When to reach for this:
A file system holds data that is written once and rarely read again.
Storage cost is climbing and most files have not been touched in weeks.
You want frequently re-read files to move back to fast primary storage automatically.
You need to change throughput behavior as a workload's access pattern shifts.
Create a file system with an explicit throughput mode, apply a full lifecycle policy, then read it back to confirm - and adjust throughput mode later with UpdateFileSystem.
# --- Python (boto3) ---import boto3, uuid, timeefs = boto3.client("efs")# 1. Create with generalPurpose performance and elastic throughput.fs = efs.create_file_system( CreationToken=str(uuid.uuid4()), PerformanceMode="generalPurpose", # fixed for life of the file system ThroughputMode="elastic", # scales up/down automatically Encrypted=True,)fs_id = fs["FileSystemId"]while efs.describe_file_systems(FileSystemId=fs_id)["FileSystems"][0]["LifeCycleState"] != "available": time.sleep(3)# 2. Apply a full lifecycle policy (this REPLACES any existing one).efs.put_lifecycle_configuration( FileSystemId=fs_id, LifecyclePolicies=[ {"TransitionToIA": "AFTER_30_DAYS"}, {"TransitionToArchive": "AFTER_90_DAYS"}, {"TransitionToPrimaryStorageClass": "AFTER_1_ACCESS"}, ],)# 3. Read it back.cfg = efs.describe_lifecycle_configuration(FileSystemId=fs_id)print("policies:", cfg["LifecyclePolicies"])# 4. Later: switch throughput mode without recreating the file system.efs.update_file_system(FileSystemId=fs_id, ThroughputMode="bursting")
// --- TypeScript (AWS SDK v3) ---import { EFSClient, CreateFileSystemCommand, DescribeFileSystemsCommand, PutLifecycleConfigurationCommand, DescribeLifecycleConfigurationCommand, UpdateFileSystemCommand,} from "@aws-sdk/client-efs";import { randomUUID } from "node:crypto";const efs = new EFSClient({});// 1. Create with generalPurpose performance and elastic throughput.const fs = await efs.send(new CreateFileSystemCommand({ CreationToken: randomUUID(), PerformanceMode: "generalPurpose", // fixed for life of the file system ThroughputMode: "elastic", // scales up/down automatically Encrypted: true,}));const FileSystemId = fs.FileSystemId!;let state = "";do { const d = await efs.send(new DescribeFileSystemsCommand({ FileSystemId })); state = d.FileSystems?.[0]?.LifeCycleState ?? ""; if (state !== "available") await new Promise((r) => setTimeout(r, 3000));} while (state !== "available");// 2. Apply a full lifecycle policy (this REPLACES any existing one).await efs.send(new PutLifecycleConfigurationCommand({ FileSystemId, LifecyclePolicies: [ { TransitionToIA: "AFTER_30_DAYS" }, { TransitionToArchive: "AFTER_90_DAYS" }, { TransitionToPrimaryStorageClass: "AFTER_1_ACCESS" }, ],}));// 3. Read it back.const cfg = await efs.send(new DescribeLifecycleConfigurationCommand({ FileSystemId }));console.log("policies:", cfg.LifecyclePolicies);// 4. Later: switch throughput mode without recreating the file system.await efs.send(new UpdateFileSystemCommand({ FileSystemId, ThroughputMode: "bursting" }));
What this demonstrates:
PerformanceMode is set once at creation; ThroughputMode starts as elastic.
PutLifecycleConfiguration sends the complete rule set - it overwrites, not merges.
TransitionToPrimaryStorageClass: AFTER_1_ACCESS promotes files back to fast storage when read.
UpdateFileSystem changes throughput mode live, without recreating the file system.
EFS has three storage classes per file system: primary (Standard), Infrequent Access (IA), and Archive. Lifecycle policies decide when a file moves between them based on how long since it was last accessed.
TransitionToIA - move to IA after N days without access. Valid values include AFTER_1_DAY, AFTER_7_DAYS, AFTER_14_DAYS, AFTER_30_DAYS, AFTER_60_DAYS, AFTER_90_DAYS, up to AFTER_365_DAYS.
TransitionToArchive - move to the cheapest Archive class after a longer idle period (a value of AFTER_90_DAYS or greater), for data you almost never read.
TransitionToPrimaryStorageClass - AFTER_1_ACCESS moves a file back to primary the moment it is read again, so recently-hot files regain full speed.
PutLifecycleConfiguration replaces the entire configuration each call. There is no add-one-rule API - always send the full list you want to end up with. To clear all rules, send an empty LifecyclePolicies list.
IA and Archive cut per-GB storage cost sharply, but reads from them incur a per-GB retrieval charge and slightly higher latency on the first access. The math works when data is genuinely cold: rarely read means you save on storage far more than you pay on the occasional retrieval. For data read often, keep it on primary - tiering it just adds retrieval fees.
generalPurpose - lowest per-operation latency; the right choice for almost every workload, and the default. It has a ceiling on operations per second that only very large parallel workloads hit.
maxIO - higher aggregate throughput and operations for massively parallel workloads (big data, media processing), at the cost of slightly higher latency per operation.
PerformanceMode cannot be changed after creation. To switch it, create a new file system with the desired mode and migrate the data (for example with AWS DataSync).
bursting - throughput scales with stored size and accumulates burst credits; good for spiky, size-correlated workloads.
elastic - throughput scales automatically to demand with no planning; best for unpredictable or spiky access, and you pay for what you use.
provisioned - a fixed throughput you set (ProvisionedThroughputInMibps), independent of size; use when you need guaranteed throughput on a small file system.
ThroughputMode is changeable with UpdateFileSystem, though AWS limits how often you can switch (roughly once a day and after a cooldown), so it is not a per-minute dial.
PutLifecycleConfiguration overwrites everything. Sending one rule wipes the others. Fix: always send the complete rule set you want; treat it as a replace, not a patch.
PerformanceMode is permanent. You cannot change generalPurpose to maxIO later. Fix: choose at creation; if wrong, create a new file system and migrate with DataSync.
Tiering hot data adds cost. IA/Archive retrieval fees can exceed the storage savings for frequently read files. Fix: only tier data that is genuinely cold; pair with AFTER_1_ACCESS so re-read files return to primary.
Expecting instant throughput-mode switches.UpdateFileSystem limits how often ThroughputMode changes. Fix: plan mode changes; do not build logic that flips modes frequently.
Assuming provisioned throughput is free.provisioned bills for the throughput whether or not you use it. Fix: use elastic for spiky loads and reserve provisioned for steady, guaranteed needs.
Forgetting the file system must be available. Applying lifecycle before LifeCycleState is available fails. Fix: poll describe_file_systems first, since EFS has no waiter.
They move files that have not been accessed for a set period to Infrequent Access and then Archive, which cost far less per GB than primary storage. You pay a retrieval fee on the rare reads, netting large savings on genuinely cold data.
Does PutLifecycleConfiguration merge with existing rules?
No. It replaces the entire configuration. Always send the complete set of rules you want, and send an empty list to remove lifecycle management entirely.
What does TransitionToPrimaryStorageClass do?
With AFTER_1_ACCESS, it promotes a file back to fast primary storage the first time it is read again after being tiered down. It keeps recently-hot files fast automatically.
Can I change PerformanceMode later?
No. PerformanceMode is fixed when the file system is created. To switch between generalPurpose and maxIO, create a new file system and migrate the data, for example with AWS DataSync.
Which PerformanceMode should I pick?
generalPurpose for almost everything - it has the lowest latency. Choose maxIO only for massively parallel workloads that hit the general-purpose operations ceiling, accepting slightly higher per-operation latency.
What is the difference between bursting and elastic throughput?
bursting scales throughput with stored size and uses burst credits; elastic scales automatically to demand with no capacity planning and pay-per-use. Elastic suits spiky or unpredictable access; bursting suits steady, size-correlated workloads.
Can I change ThroughputMode after creation?
Yes, with UpdateFileSystem, but AWS limits how frequently you can switch (about once a day plus a cooldown). It is a deliberate adjustment, not a real-time dial.
Is there a downside to tiering to IA or Archive?
Reads from IA/Archive incur a per-GB retrieval charge and slightly higher first-access latency. For frequently read files that can cost more than it saves, so only tier data that is truly cold.
What are the valid TransitionToIA values?
Named day thresholds: AFTER_1_DAY, AFTER_7_DAYS, AFTER_14_DAYS, AFTER_30_DAYS, AFTER_60_DAYS, AFTER_90_DAYS, and longer up to AFTER_365_DAYS. Pick the idle window that matches your access pattern.
How do I remove all lifecycle rules?
Call PutLifecycleConfiguration with an empty LifecyclePolicies list. Because the call replaces the whole configuration, an empty list clears every rule.