| 1 | --- |
| 2 | title: Volumes |
| 3 | description: Block disk in a Project. Mount it into a Service with MountVolume. |
| 4 | --- |
| 5 | |
| 6 | A [`Railway.Volume`](/providers/railway/volume) is block disk in a |
| 7 | [Project](/railway/compute/projects). Create it disconnected (no |
| 8 | `service`) and attach later with |
| 9 | [`MountVolume`](/providers/railway/mountvolume), or pass `service` to |
| 10 | attach at create time. |
| 11 | |
| 12 | The disk outlives deploys. `mountPath` updates in place. Changing |
| 13 | `project`, `environment`, or `region` replaces the Volume. |
| 14 | |
| 15 | ## Create a Volume |
| 16 | |
| 17 | Pass a Project and a mount path. Alchemy generates a unique name. |
| 18 | The volume is disconnected until you attach a Service. |
| 19 | |
| 20 | ```typescript |
| 21 | const site = yield* Railway.Project("Site"); |
| 22 | const data = yield* Railway.Volume("Data", { |
| 23 | project: site, |
| 24 | mountPath: "/data", |
| 25 | }); |
| 26 | ``` |
| 27 | |
| 28 | :::caution[Changing `project` replaces the Volume] |
| 29 | The Volume is created in the new Project. The old Volume is deleted. |
| 30 | ::: |
| 31 | |
| 32 | ## Mount into a Service |
| 33 | |
| 34 | Inside a [Service](/railway/compute/services)'s constructor, bind the Volume |
| 35 | to a path and use it as a plain directory at runtime: |
| 36 | |
| 37 | ```typescript |
| 38 | export default class Api extends Railway.Service<Api>()( |
| 39 | "Api", |
| 40 | { project: Site, main: import.meta.url, port: 3000 }, |
| 41 | Effect.gen(function* () { |
| 42 | const disk = yield* Railway.MountVolume(Data, { path: "/data" }); |
| 43 | const fs = yield* FileSystem.FileSystem; |
| 44 | |
| 45 | return { |
| 46 | fetch: Effect.gen(function* () { |
| 47 | const text = yield* fs.readFileString(`${disk.path}/hello.txt`); |
| 48 | // ... |
| 49 | }), |
| 50 | }; |
| 51 | }).pipe(Effect.provide(Railway.MountVolumeLive)), |
| 52 | ) {} |
| 53 | ``` |
| 54 | |
| 55 | At deploy time the binding writes `{ path, volumeId }` into the |
| 56 | Service. Reconcile attaches the volume via `volumeInstanceUpdate`. |
| 57 | At runtime you get `disk.path`. |
| 58 | |
| 59 | Railway allows one volume per Service. It does not give each replica |
| 60 | its own disk. A second volume on the same Service is |
| 61 | `Railway.MultipleVolumes`. |
| 62 | |
| 63 | ## Attach at create time |
| 64 | |
| 65 | Pass `service` to attach when the Volume is created: |
| 66 | |
| 67 | ```typescript |
| 68 | const api = yield* Railway.Service("Api", { |
| 69 | project: site, |
| 70 | image: "hashicorp/http-echo", |
| 71 | port: 5678, |
| 72 | }); |
| 73 | const data = yield* Railway.Volume("Data", { |
| 74 | project: site, |
| 75 | mountPath: "/data", |
| 76 | service: api, |
| 77 | }); |
| 78 | ``` |
| 79 | |
| 80 | Updating `service` attaches or moves the volume in place. The |
| 81 | target Service must not already have a volume. |
| 82 | |
| 83 | ## Environment |
| 84 | |
| 85 | Defaults to the Project's primary environment. Pass a |
| 86 | [Environment](/railway/compute/environments) (or `{ environmentId }`) |
| 87 | to target another one. |
| 88 | |
| 89 | ```typescript |
| 90 | const staging = yield* Railway.Environment("Staging", { project: site }); |
| 91 | const data = yield* Railway.Volume("StagingData", { |
| 92 | project: site, |
| 93 | environment: staging, |
| 94 | mountPath: "/data", |
| 95 | }); |
| 96 | ``` |
| 97 | |
| 98 | :::caution[Changing `environment` replaces the Volume] |
| 99 | The Volume is created in the new environment. The old Volume is |
| 100 | deleted. |
| 101 | ::: |
| 102 | |
| 103 | ## Region |
| 104 | |
| 105 | Omit `region` to use Railway's default. Changing it replaces the |
| 106 | Volume. See [Regions](/railway/compute/regions). |
| 107 | |
| 108 | ```typescript |
| 109 | const data = yield* Railway.Volume("Data", { |
| 110 | project: site, |
| 111 | mountPath: "/data", |
| 112 | region: "us-west2", |
| 113 | }); |
| 114 | ``` |
| 115 | |
| 116 | ## Backups |
| 117 | |
| 118 | [`Railway.VolumeBackup`](/providers/railway/volumebackup) is a |
| 119 | snapshot of a volume instance. The volume should be attached to a |
| 120 | Service — Railway only backups mounted volumes. `lock` drops the |
| 121 | expiration. `schedules` (`DAILY` / `WEEKLY` / `MONTHLY`) is |
| 122 | volume-instance state. |
| 123 | |
| 124 | ```typescript |
| 125 | const snap = yield* Railway.VolumeBackup("Nightly", { |
| 126 | volume: data, |
| 127 | lock: true, |
| 128 | schedules: ["DAILY", "WEEKLY"], |
| 129 | }); |
| 130 | ``` |
| 131 | |
| 132 | Restore is destructive to the volume instance. Call |
| 133 | `restoreVolumeBackup` — it is not a reconciler step. Volume backups |
| 134 | are Pro-plan gated (`RailwayForbidden` on Hobby). |
| 135 | |
| 136 | ## Where next |
| 137 | |
| 138 | - [Tutorial part 3](/railway/tutorial/part-3) — mount a disk into a |
| 139 | Service step by step. |
| 140 | - [Services](/railway/compute/services) — `MountVolume`. |
| 141 | - [`Volume` reference](/providers/railway/volume), |
| 142 | [`MountVolume` reference](/providers/railway/mountvolume), and |
| 143 | [`VolumeBackup` reference](/providers/railway/volumebackup). |