Skip to content

Commit d252013

Browse files
asibClaude Codeclaude
authored
Update docs for hot/cold storage pricing (#90)
Co-authored-by: Claude Code <[email protected]> Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent ad5adfa commit d252013

File tree

3 files changed

+64
-36
lines changed

3 files changed

+64
-36
lines changed

.github/workflows/ci.yml

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -355,27 +355,35 @@ jobs:
355355
uses: actions/github-script@v7
356356
with:
357357
script: |
358-
const manifest = ${{ steps.lighthouse.outputs.manifest }};
359-
const links = ${{ steps.lighthouse.outputs.links }};
358+
const manifestRaw = `${{ steps.lighthouse.outputs.manifest }}`;
359+
const linksRaw = `${{ steps.lighthouse.outputs.links }}`;
360360
361361
let body = '### Lighthouse Results\n\n';
362-
body += '| URL | Performance | Accessibility | Best Practices | SEO |\n';
363-
body += '|-----|-------------|---------------|----------------|-----|\n';
364-
365-
for (const result of manifest) {
366-
const url = new URL(result.url).pathname || '/';
367-
const perf = Math.round(result.summary.performance * 100);
368-
const a11y = Math.round(result.summary.accessibility * 100);
369-
const bp = Math.round(result.summary['best-practices'] * 100);
370-
const seo = Math.round(result.summary.seo * 100);
371-
372-
const perfEmoji = perf >= 90 ? '🟢' : perf >= 50 ? '🟠' : '🔴';
373-
const a11yEmoji = a11y >= 90 ? '🟢' : a11y >= 50 ? '🟠' : '🔴';
374-
const bpEmoji = bp >= 90 ? '🟢' : bp >= 50 ? '🟠' : '🔴';
375-
const seoEmoji = seo >= 90 ? '🟢' : seo >= 50 ? '🟠' : '🔴';
376-
377-
const reportLink = links[result.url] ? `[${url}](${links[result.url]})` : url;
378-
body += `| ${reportLink} | ${perfEmoji} ${perf} | ${a11yEmoji} ${a11y} | ${bpEmoji} ${bp} | ${seoEmoji} ${seo} |\n`;
362+
363+
if (!manifestRaw || manifestRaw === '') {
364+
body += '⚠️ Lighthouse failed to collect results. This may be due to the preview deployment not being ready.\n';
365+
} else {
366+
const manifest = JSON.parse(manifestRaw);
367+
const links = JSON.parse(linksRaw);
368+
369+
body += '| URL | Performance | Accessibility | Best Practices | SEO |\n';
370+
body += '|-----|-------------|---------------|----------------|-----|\n';
371+
372+
for (const result of manifest) {
373+
const url = new URL(result.url).pathname || '/';
374+
const perf = Math.round(result.summary.performance * 100);
375+
const a11y = Math.round(result.summary.accessibility * 100);
376+
const bp = Math.round(result.summary['best-practices'] * 100);
377+
const seo = Math.round(result.summary.seo * 100);
378+
379+
const perfEmoji = perf >= 90 ? '🟢' : perf >= 50 ? '🟠' : '🔴';
380+
const a11yEmoji = a11y >= 90 ? '🟢' : a11y >= 50 ? '🟠' : '🔴';
381+
const bpEmoji = bp >= 90 ? '🟢' : bp >= 50 ? '🟠' : '🔴';
382+
const seoEmoji = seo >= 90 ? '🟢' : seo >= 50 ? '🟠' : '🔴';
383+
384+
const reportLink = links[result.url] ? `[${url}](${links[result.url]})` : url;
385+
body += `| ${reportLink} | ${perfEmoji} ${perf} | ${a11yEmoji} ${a11y} | ${bpEmoji} ${bp} | ${seoEmoji} ${seo} |\n`;
386+
}
379387
}
380388
381389
const { data: comments } = await github.rest.issues.listComments({

src/components/react/PricingRates.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { Switch } from '@/components/ui/switch';
44
export const HOURLY_RATES = {
55
cpu: 0.07,
66
ram: 0.04375,
7-
storage: 0.5,
7+
storageHot: 0.5,
8+
storageCold: 0.02,
89
};
910

1011
export function PricingRates() {
@@ -53,16 +54,22 @@ export function PricingRates() {
5354
unit={perSecond ? '/GB-second' : '/GB-hour'}
5455
/>
5556
<RateCard
56-
title="Storage"
57-
rate={`$${HOURLY_RATES.storage.toFixed(2)}`}
57+
title="Hot Storage"
58+
rate={`$${HOURLY_RATES.storageHot.toFixed(2)}`}
5859
unit="/GB-month"
59-
description="Prorated to the hour. Storage is aggregated across all Sprites."
60+
description="Local NVMe cache for active working data, sampled every few seconds."
61+
/>
62+
<RateCard
63+
title="Cold Storage"
64+
rate={`$${HOURLY_RATES.storageCold.toFixed(2)}`}
65+
unit="/GB-month"
66+
description="Object storage for persistent data, measured hourly."
6067
/>
6168
</div>
6269

6370
<p className="text-xs text-[var(--sl-color-gray-2)] pt-2 border-t border-[var(--sl-color-hairline)]">
64-
All compute resources are billed per second. Storage is billed hourly
65-
but aggregated monthly.
71+
All compute resources are billed per second. Hot storage is sampled
72+
every few seconds while active; cold storage is measured hourly.
6673
</p>
6774
</div>
6875
);

src/content/docs/reference/billing.mdx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,39 @@ Compute is billed **per-second** with no minimum. If you run a command for 30 se
5656

5757
## Storage Billing
5858

59-
### How It Works
59+
Sprites use a two-tier storage system: **hot storage** for active working data and **cold storage** for persistent object storage.
60+
61+
### Hot Storage
62+
63+
Hot storage is the local NVMe cache used by the filesystem (JuiceFS) for active working data.
64+
65+
- **Sampled every few seconds** while your Sprite is active
66+
- **Only charged while running**: No hot storage charges when hibernated
67+
- **Automatically managed**: The filesystem handles caching transparently
68+
69+
Hot storage represents the working set of data actively being used during your session.
70+
71+
### Cold Storage
6072

61-
Storage is billed monthly based on the actual data stored on your Sprite's filesystem:
73+
Cold storage is the object storage bucket where all your Sprite's persistent data lives.
6274

75+
- **Measured hourly** by summing the actual size of all objects in your bucket
76+
- **Always charged**: Cold storage accrues 24/7, even when hibernated
6377
- **TRIM-friendly**: You only pay for data actually written
6478
- **Deleted files reduce cost**: Deleting files reduces your storage bill
65-
- **Compression**: Data is compressed in storage
6679

6780
### Starting Allocation
6881

6982
Every Sprite includes 100 GB of storage capacity. Storage does not autoscale yet.
7083

7184
### Typical Usage
7285

73-
| Environment | Storage | Within Plan? |
74-
|------------|---------|--------------|
75-
| Minimal (OS + tools) | ~2 GB | Yes |
76-
| Dev environment | ~5 GB | Yes |
77-
| With datasets | ~10 GB | Yes |
78-
| Large datasets | ~60 GB | +10 GB overage |
86+
| Environment | Hot Storage (active) | Cold Storage | Within Plan? |
87+
|------------|---------------------|--------------|--------------|
88+
| Minimal (OS + tools) | ~1 GB | ~2 GB | Yes |
89+
| Dev environment | ~2 GB | ~5 GB | Yes |
90+
| With datasets | ~3 GB | ~10 GB | Yes |
91+
| Large datasets | ~5 GB | ~60 GB | +10 GB overage |
7992

8093
## Cost Optimization
8194

@@ -136,10 +149,10 @@ Usage and billing information is available through your Fly.io dashboard and org
136149
> Compute billing starts when you execute a command and stops when the command completes or the Sprite hibernates.
137150
138151
**Do I pay while hibernated?**
139-
> No compute charges while hibernated. You only pay for storage.
152+
> No compute or hot storage charges while hibernated. You only pay for cold storage (object storage), which accrues 24/7.
140153
141154
**How is storage measured?**
142-
> Storage is measured based on actual data on disk, not allocated capacity. It's sampled periodically throughout the month.
155+
> Hot storage (NVMe cache) is sampled every few seconds while your Sprite is active. Cold storage (object storage) is measured hourly based on actual data stored, not allocated capacity.
143156
144157
**Are checkpoints billed?**
145158
> Yes, checkpoints count toward your storage usage.

0 commit comments

Comments
 (0)