Skip to content

Railway.Volume reference

Source: src/Railway/MountVolume.ts

Mount a Railway.Volume into a Service or Railway.Function.

yield* Railway.MountVolume(volume, { path: "/data" }) inside a Service/Function impl registers { mounts: [{ volumeId, path }] } on the host. Reconcile attaches the volume via volumeInstanceUpdate.

Railway allows one volume per service. A second mount is Railway.MultipleVolumes. Railway does not give each replica its own disk.

Yield MountVolume inside init. Provide MountVolumeLive. At runtime you get disk.path.

export default class Api extends Railway.Service<Api>()(
"Api",
{ project: Site, main: import.meta.url },
Effect.gen(function* () {
const disk = yield* Railway.MountVolume(Data, { path: "/data" });
const fs = yield* FileSystem.FileSystem;
return {
fetch: Effect.gen(function* () {
const text = yield* fs.readFileString(`${disk.path}/hello.txt`);
return HttpServerResponse.text(text);
}),
};
}).pipe(Effect.provide(Railway.MountVolumeLive)),
) {}

Source: src/Railway/Volume.ts

A Railway.Volume is block disk in a Project. Create it disconnected (no service) and attach later with MountVolume, or pass service to attach at create time.

Railway has no labels. Ownership is stamped into the volume name via createPhysicalName. mountPath updates in place. Changing project, environment, or region replaces the Volume.

Pass a Project and a mount path. Alchemy generates a unique name. The volume is disconnected until you attach a Service.

const site = yield* Railway.Project("Site");
const data = yield* Railway.Volume("Data", {
project: site,
mountPath: "/data",
});

mountPath is the path in the container. Updating it is in place.

const data = yield* Railway.Volume("Data", {
project: site,
mountPath: "/app/data",
});

Defaults to the Project’s primary environment. Pass a Railway.Environment (or { environmentId }) to target another one.

const staging = yield* Railway.Environment("Staging", { project: site });
const data = yield* Railway.Volume("StagingData", {
project: site,
environment: staging,
mountPath: "/data",
});

Omit region to use Railway’s default. Changing it replaces the Volume.

const data = yield* Railway.Volume("Data", {
project: site,
mountPath: "/data",
region: "us-west2",
});

sizeMB on attributes is the observed plan default. Railway’s public VolumeCreateInput has no size field; growing is Pro-dashboard-only. Passing sizeMB on props is IaC-parity documentation — it is not applied.

const data = yield* Railway.Volume("Data", {
project: site,
mountPath: "/data",
});
// data.sizeMB is the plan default (e.g. 5120 on Hobby)

Pass service to attach at create time. Omit it and attach later with MountVolume. One volume per service.

const api = yield* Railway.Service("Api", {
project: site,
image: "hashicorp/http-echo",
});
const data = yield* Railway.Volume("Data", {
project: site,
mountPath: "/data",
service: api,
});

Snapshot a mounted volume with VolumeBackup. Railway only backups attached volumes. Restore is destructive — see restoreVolumeBackup.

const snap = yield* Railway.VolumeBackup("Nightly", { volume: data });

Declare the Project once. Pass it into every child. Resource-valued props accept the resource or an Effect producing it.

src/data.ts
import * as Railway from "alchemy/Railway";
export const Site = Railway.Project("Site");
export const Data = Railway.Volume("Data", {
project: Site,
mountPath: "/data",
});

Source: src/Railway/VolumeBackup.ts

A Railway.VolumeBackup is a snapshot of a volume instance. Create it next to a Railway.Volume. Railway backups run as workflows — Alchemy waits for workflowStatus then reads the backup list.

Restore is destructive to the volume instance; use restoreVolumeBackup. Point-in-time restore forks a new Postgres service via restoreVolumePITR.

Pass the Volume. Alchemy generates a unique name. The volume should be attached to a Service — Railway only backups mounted volumes.

const site = yield* Railway.Project("Site");
const api = yield* Railway.Service("Api", {
project: site,
image: "hashicorp/http-echo",
});
const data = yield* Railway.Volume("Data", {
project: site,
mountPath: "/data",
service: api,
});
const snap = yield* Railway.VolumeBackup("Nightly", { volume: data });

Omit name for an ownership-stamped name. Pass one to label the snapshot in the dashboard.

const snap = yield* Railway.VolumeBackup("Nightly", {
volume: data,
name: "pre-migrate",
});

Lock to drop the expiration. One-way.

const snap = yield* Railway.VolumeBackup("Nightly", {
volume: data,
lock: true,
});

schedules is volume-instance state, not per-snapshot. Set it on one VolumeBackup that owns the instance.

const snap = yield* Railway.VolumeBackup("Nightly", {
volume: data,
schedules: ["DAILY", "WEEKLY"],
});

Restore is not a reconciler step. Call restoreVolumeBackup (destructive to the instance) or restoreVolumePITR (forks a new Postgres service).

yield* Railway.restoreVolumeBackup({
volumeInstanceId: snap.volumeInstanceId,
volumeInstanceBackupId: snap.volumeInstanceBackupId,
});

Resource-valued props accept the resource or an Effect producing it.

src/data.ts
import * as Railway from "alchemy/Railway";
export const Site = Railway.Project("Site");
export const Data = Railway.Volume("Data", {
project: Site,
mountPath: "/data",
});
export const Nightly = Railway.VolumeBackup("Nightly", {
volume: Data,
});