Rent and Rent-Exemption
The previous page, Transaction Fees and Local Fee Markets, priced a moment of the machine: the compute and block space a single transaction consumes as it executes. That fee is paid once and it’s gone. But a transaction usually leaves something behind — a new account, a larger data buffer, a program’s state. That leftover doesn’t consume the machine for a moment; it consumes it forever. Somebody has to pay for that.
This page is about the price of persistence. Solana’s whole design bet is that “state lives in named,
external accounts” — the account model you met when you built solmini.
That bet has a bill attached: every one of those accounts sits in validator memory for as long as it exists,
on every validator in the cluster. Rent, and specifically rent-exemption, is the mechanism that keeps
that bill from bankrupting the network. It follows directly from the throughline of this book — how do you
build a single global state machine that runs at hardware speed without falling apart? — because a state
machine whose state grows without bound is a state machine that eventually falls apart.
The problem: storage isn’t free, and it never ends
Section titled “The problem: storage isn’t free, and it never ends”A transaction fee is a one-time charge for a one-time action. Storage is different in kind. When you create an account with a kilobyte of data, you are not asking the network to do a kilobyte of work once — you are asking every validator, now and in the future, to keep that kilobyte resident so it can be read and written at hardware speed. The cost recurs on every machine, every epoch, indefinitely.
That asymmetry is dangerous. If storage were free, the incentives would be perverse:
one-time work forever-storage ───────────── ─────────────── pay a fee, work happens, done pay nothing, everyone stores it, forever cost is bounded and settled cost is unbounded and never settled │ ▼ anyone can bloat the global state for free → validator RAM/disk grows without limit → the "single global state machine" stops fitting on commodity hardwareA blockchain’s state has to be held by every full participant. If a single actor can spray millions of tiny accounts into existence at no cost, they inflate the working set every honest validator must carry. The machine that was supposed to run at hardware speed now can’t — because its state no longer fits in the hardware. Rent is the price of persistence: the economic answer to “who pays to keep this byte alive?”
Rent-exemption: pay two years up front, then never again
Section titled “Rent-exemption: pay two years up front, then never again”The naive design would be to literally charge accounts rent every epoch — a trickle of lamports deducted for the storage they occupy, like a metered utility. Early Solana did exactly this, and it was a mess: accounts slowly bled to death, wallets had to be topped up, and every program had to reason about time-varying balances. So the model was inverted into something cleaner.
Today, an account must be rent-exempt. Rent-exemption means the account holds a minimum lamport balance proportional to its size — set at roughly two years’ worth of the rent it would have paid — and in exchange it is permanently exempt from rent collection. It pays a lump-sum deposit once and is then safe.
Metered rent (deprecated) Rent-exemption (today) ───────────────────────── ────────────────────── deduct a little every epoch hold ~2 years of rent as a deposit balance drifts down over time balance is a fixed floor, untouched forget to top up → account dies stay above the floor → account livesThe rule the runtime actually enforces is blunt: an account that is not rent-exempt cannot be created, and a transaction that would leave an account below its rent-exempt minimum fails. There is no slow decay to worry about anymore. Either you fund the account to its exemption threshold up front, or the instruction that tries to create it is rejected. The floor is a hard invariant, checked at transaction time.
An account whose balance somehow fell below the threshold would become eligible for collection, and a collected account can be purged — its data dropped from the validators’ working state, its lamports swept. In practice, because the runtime refuses to leave an account under-funded, rent-exempt accounts simply never get into that state. The exemption isn’t a discount you opt into; it’s the only stable place to be.
The deposit scales with size
Section titled “The deposit scales with size”The exempt minimum is not a flat fee. It is proportional to how much of every validator’s memory the account occupies — which is to say, proportional to its data size in bytes. A bigger buffer means a bigger deposit, because a bigger buffer is a bigger forever-cost.
The size the network charges for is your declared data length plus a fixed per-account overhead (a small number of bytes of metadata every account carries — its lamports, owner, flags, and so on). So even a zero-data account isn’t free: it still occupies a slot in the accounts database.
rent-exempt minimum ≈ (account_overhead + data_len_bytes) × lamports_per_byte_year × 2 years (the exemption horizon)
┌───────────────┬───────────────────────────────────────────┐ │ account size │ rent-exempt deposit (intuition, not a quote)│ ├───────────────┼───────────────────────────────────────────┤ │ 0 bytes data │ smallest — overhead only │ │ ~165 bytes │ small — e.g. an SPL token account │ │ ~1 KiB │ larger — a modest program state struct │ │ ~10 MiB (max) │ largest — the per-account size ceiling │ └───────────────┴───────────────────────────────────────────┘Two consequences fall out of that proportionality. First, account data has a hard size cap — on the order of ~10 MiB per account — because unbounded per-account size would mean unbounded per-account cost and reintroduce exactly the bloat problem rent exists to prevent. Second, the size you choose at creation matters: you pay for the space you allocate, whether or not you fill it, so over-allocating a data buffer “just in case” is over-paying a deposit on every validator’s behalf.
It’s a deposit, not a fee — you get it back
Section titled “It’s a deposit, not a fee — you get it back”Here is the distinction that trips people up, and the one worth fixing firmly: the rent-exempt balance is a refundable deposit, not a fee. A transaction fee leaves your control the instant it’s paid — it’s burned and rewarded to validators, gone. The rent-exempt lamports are different: they are still yours. They sit in the account as a bond guaranteeing its footprint is funded, and the moment you no longer need the account, you close it and reclaim every last lamport.
fee rent-exempt deposit ─── ─────────────────── paid once, gone held while the account lives pays for work already done reserves space you're occupying never refunded fully refunded on closeClosing an account is an explicit operation: you set its data length to zero and transfer its entire lamport balance out to a recipient of your choice. Once its balance hits zero, the runtime reclaims the account — the slot is freed on every validator, and the deposit you parked there flows back to you. So the lifetime cost of an account you eventually close is just the transaction fees to open and close it; the deposit was only ever on loan to the network as a promise to cover the space.
This is why “rent” is a slightly misleading name. You are not renting in the sense of money you’ll never see again. You are posting a security deposit sized to the space you occupy, refunded when you vacate.
Under the hood — why the two-year lump sum instead of a meter
Section titled “Under the hood — why the two-year lump sum instead of a meter”Why fix the deposit at two years of rent and drop metering entirely? Because a metered model forces every account and every program to reason about time: balances drift, accounts can die between uses, and a long-dormant account (a cold wallet, an infrequently-touched config) is exactly the kind that would quietly starve and get purged. That is a terrible property for a state machine whose whole promise is durable, predictable state.
The lump-sum model turns a time-varying liability into a fixed floor. Once funded, an account’s minimum balance never changes and it never decays — a cold wallet you don’t touch for five years is exactly as alive as it was on day one. The network trades “collect a trickle continuously” for “collect two years at once and never think about it again,” which is a far better fit for a system that must run millions of accounts at hardware speed without a background reaper sweeping through memory. The two-year horizon is the size of the bond, not a countdown; nothing expires at year two.
Tie-back: this is the bill for the account model
Section titled “Tie-back: this is the bill for the account model”Everything on this page is the economic dual of a design decision you already met. Solana separates code (stateless programs) from state (accounts the program owns) so the runtime can know a transaction’s footprint up front and schedule it in parallel — the account model from Day 28. That separation is what makes hardware-speed execution possible. But “state lives in named external accounts” means state is external, named, and persistent — and persistence has to be paid for. Rent-exemption is who pays.
For you as a developer, the practical rule is direct: creating an account means funding it to the
rent-exempt minimum up front. This lands most concretely on PDAs (program-derived addresses) — accounts
your program owns at a deterministic address. When your program creates a PDA, someone (usually the user
sending the transaction, acting as the payer) must transfer it enough lamports to be rent-exempt for the size
you’re allocating, in the same transaction that creates it. Under-fund it and the create instruction fails.
In Anchor this is the space you declare and the payer you name:
#[derive(Accounts)]pub struct Initialize<'info> { // The PDA this instruction creates. Anchor computes the rent-exempt // minimum for `space` and makes `payer` fund it — in this same tx. #[account( init, payer = user, space = 8 + CounterState::SIZE, // 8-byte discriminator + your data )] pub counter: Account<'info, CounterState>,
#[account(mut)] pub user: Signer<'info>, // pays the fee AND the deposit pub system_program: Program<'info, System>,}The 8 + is the account discriminator you met on Day 28 — the 8-byte type tag Anchor prepends — and it
counts toward the size you’re paying rent-exemption for. Pick space too small and your struct won’t fit;
pick it too large and you’ve over-funded a deposit you didn’t need. The number is load-bearing, and now you
know why: every byte in space is a byte every validator holds, priced into the lamports user must post.
The architect’s lens
Section titled “The architect’s lens”- Why does it exist? Because account storage is a forever cost borne by every validator, and any resource that’s free and permanent will be abused until it breaks. Rent-exemption puts a price on persistence so that occupying the global state has a funded cost.
- What problem does it solve? State bloat. It stops anyone from cheaply spraying accounts across the network and inflating the working set every validator must keep resident, which would eventually push the state off commodity hardware and break hardware-speed execution.
- What are the trade-offs? Every account needs upfront capital proportional to its size, which raises the cost of creating on-chain data and forces developers to size buffers carefully. In return, accounts are stable, never decay, and the deposit is fully refundable — you pay in locked capital, not in ongoing fees.
- When should I avoid it? You can’t avoid it for on-chain accounts — it’s a runtime invariant. But you can avoid incurring it: keep large, cold, or ephemeral data off-chain (or in logs/events) and store only the minimal authoritative state on-chain, so you post the smallest deposit that still works.
- What breaks if I remove it? The network’s storage economics collapse: accounts become free to create and free to keep, state grows without bound, validators’ memory and disk requirements climb until only a few large operators can keep up, and decentralization — and eventually liveness — erodes.
Check your understanding
Section titled “Check your understanding”- Why is storing an account fundamentally different from paying a one-time transaction fee, and why does that difference make free storage dangerous for the network?
- What does it mean for an account to be rent-exempt, and roughly how much must it hold to qualify?
- Two accounts are created, one with 100 bytes of data and one with 4 KiB. Which needs the larger rent-exempt deposit, and why does the deposit scale that way?
- A developer says “rent is a fee you pay to keep your account alive.” Correct them: what is the rent-exempt balance actually, and what happens to it when the account is closed?
- In an Anchor
#[account(init, payer = user, space = N)], what two distinct costs doesusercover, and what determines the size of the second one?
Show answers
- A transaction fee pays for a one-time action that completes and settles; the cost is bounded. Storage imposes a recurring, unbounded cost — every validator, now and forever, must keep the account resident so it can be read and written at hardware speed. If that were free, anyone could bloat the global state at no cost, inflating the working set until it no longer fits on commodity hardware and the state machine can’t run at speed.
- An account is rent-exempt when it holds a minimum lamport balance proportional to its size — set at roughly two years’ worth of rent — in exchange for permanent exemption from rent collection. The runtime refuses to create or leave an account below this threshold, so exempt accounts never decay and never get purged.
- The 4 KiB account needs the far larger deposit. The exempt minimum is proportional to the account’s data size (plus a fixed per-account overhead), because a bigger buffer occupies more of every validator’s memory forever — so its forever-cost, and therefore its deposit, is proportionally larger.
- It’s a refundable deposit, not a fee. The lamports remain the account’s own balance, held as a bond that its storage footprint is funded. When you close the account (zero its data, transfer its balance out), you reclaim every lamport of the deposit; the network frees the slot. Only the open/close transaction fees are truly spent.
usercovers (a) the transaction fee for executing the instruction, and (b) the rent-exempt deposit that funds the new account. The deposit’s size is determined byspace(the declared data length, including the 8-byte discriminator) times the network’s lamports-per-byte-year rate over the two-year horizon — morespacemeans a larger deposit.