| 1 | --- |
| 2 | title: Volumes |
| 3 | description: A disk in one region, attached to one Machine. |
| 4 | --- |
| 5 | |
| 6 | A Fly Volume is a disk in one [region](/fly/compute/regions), |
| 7 | attached to one Machine. |
| 8 | Minimum size is 1 GB. Machines can be replaced. The Volume keeps |
| 9 | the data. |
| 10 | |
| 11 | There is no standalone Volume resource. Create disks with |
| 12 | [`MountVolume`](/providers/fly/mountvolume) or Machine `mounts`. |
| 13 | |
| 14 | Fly cannot share one Volume across two Machines. `count: 3` creates |
| 15 | three Volumes in one name-group, one per replica. |
| 16 | |
| 17 | ## Mount into a Service |
| 18 | |
| 19 | Inside a [Service](/fly/compute/services)'s constructor, bind a disk to a |
| 20 | path and use it as a plain directory at runtime. App and region come |
| 21 | from the parent Service: |
| 22 | |
| 23 | ```typescript |
| 24 | export default class Api extends Fly.Service<Api>()( |
| 25 | "Api", |
| 26 | { main: import.meta.url, region: "iad", count: 3, port: 3000 }, |
| 27 | Effect.gen(function* () { |
| 28 | const disk = yield* Fly.MountVolume({ path: "/data", sizeGb: 1 }); |
| 29 | const fs = yield* FileSystem.FileSystem; |
| 30 | |
| 31 | return { |
| 32 | fetch: Effect.gen(function* () { |
| 33 | const text = yield* fs.readFileString(`${disk.path}/hello.txt`); |
| 34 | // ... |
| 35 | }), |
| 36 | }; |
| 37 | }).pipe(Effect.provide(Fly.MountVolumeLive)), |
| 38 | ) {} |
| 39 | ``` |
| 40 | |
| 41 | At deploy time the binding writes `{ path, sizeGb }` into the |
| 42 | Service. Reconcile creates one Volume per replica and writes |
| 43 | `config.mounts`. At runtime you get `disk.path`. |
| 44 | |
| 45 | `sizeGb` can **grow in place** via `extendVolume` — shrinking is |
| 46 | not supported (Fly cannot shrink a Volume). Encryption and |
| 47 | filesystem type are create-only. |
| 48 | |
| 49 | If a replica Machine is replaced, its Volume re-attaches on the next |
| 50 | deploy — the data rides along. |
| 51 | |
| 52 | ## Attach from a Machine |
| 53 | |
| 54 | For a raw Machine, pass `mounts` on the resource. App and region are |
| 55 | the Machine's: |
| 56 | |
| 57 | ```typescript |
| 58 | const box = yield* Fly.Machine("Box", { |
| 59 | app: Site, |
| 60 | region: "iad", |
| 61 | image: "postgres:16", |
| 62 | mounts: [{ path: "/data", sizeGb: 10 }], |
| 63 | }); |
| 64 | ``` |
| 65 | |
| 66 | Resolved attrs expose `mounts` (replica 0) and `replicas[i].mounts` |
| 67 | (every replica's `volumeId` / `sizeGb` / group `name`). |
| 68 | |
| 69 | ## Snapshots |
| 70 | |
| 71 | Scheduled snapshots default on (`autoBackupEnabled`). Snapshot |
| 72 | retention updates in place. An on-demand snapshot is |
| 73 | [`VolumeSnapshot`](/providers/fly/volumesnapshot) — create is |
| 74 | fire-and-forget; destroy is a no-op (snapshots follow Volume |
| 75 | retention). Point it at a Volume id from the parent Machine or |
| 76 | Service: |
| 77 | |
| 78 | ```typescript |
| 79 | export const Nightly = Fly.VolumeSnapshot("Nightly", { |
| 80 | app: Site, |
| 81 | volumeId: box.mounts[0].volumeId, |
| 82 | }); |
| 83 | ``` |
| 84 | |
| 85 | Restore into a new disk with `snapshotId` on the mount (create-only): |
| 86 | |
| 87 | ```typescript |
| 88 | const restored = yield* Fly.Machine("Restored", { |
| 89 | app: Site, |
| 90 | region: "iad", |
| 91 | image: "nginx:alpine", |
| 92 | mounts: [{ path: "/data", sizeGb: 1, snapshotId: Nightly.snapshotId }], |
| 93 | }); |
| 94 | ``` |
| 95 | |
| 96 | ## Where next |
| 97 | |
| 98 | - [Tutorial part 3](/fly/tutorial/part-3) — mount a disk into a |
| 99 | Service step by step. |
| 100 | - [Services](/fly/compute/services) — `count` and `MountVolume`. |
| 101 | - [`MountVolume` reference](/providers/fly/mountvolume) and |
| 102 | [`VolumeSnapshot` reference](/providers/fly/volumesnapshot). |