Railway services are ephemeral by default — every redeploy starts from a clean slate. Anything your app writes to the local filesystem disappears on the next deploy. For most web apps this is fine, but for AI applications that cache model weights, store uploaded files, or maintain a local vector database, you need persistent storage. That's what Railway Volumes provide.
What Gets Lost Without a Volume
- Files uploaded by users and stored on the local filesystem
- Downloaded model weights (Ollama models, embedding model caches)
- SQLite databases written to the local disk
- Generated files, exports, or cached embeddings
- Anything written to /tmp or any local path
Railway Volumes are not backups. They persist data across deploys but are not replicated. For production databases, use Railway's managed Postgres which has automated backups.Attaching a Volume to a Service
- In the Railway dashboard, select your service
- Click the three-dot menu > Add Volume
- Set the mount path — this is where your app reads/writes persistent data
- Set the volume size (start with 1–5 GB; you can resize later)
- Redeploy the service — the volume is now mounted at that path
# railway.toml — you can also define volumes as code
[volumes]
[[volumes.mounts]]
mountPath = "/data"
size = "5GB"Recipe: Persistent Ollama Model Cache
Ollama downloads model weights on first use. Without a volume, every redeploy re-downloads 4–40 GB of weights. Mount a volume at the Ollama models directory to persist them.
FROM ollama/ollama
# Railway will mount the volume at /root/.ollama
# Model weights are stored here and persist across deploys
ENV OLLAMA_MODELS=/root/.ollama/models
CMD ["ollama", "serve"]# railway.toml
[deploy]
startCommand = "ollama serve"
[volumes]
[[volumes.mounts]]
mountPath = "/root/.ollama"
size = "20GB" # enough for a 7B model in Q4Recipe: Persistent ChromaDB or FAISS Index
Local vector stores write their index to disk. Mount a volume at the data directory so your index survives redeployments.
import os
import chromadb
# Use the mounted volume path for persistence
DATA_DIR = os.environ.get('DATA_DIR', '/data/chroma')
os.makedirs(DATA_DIR, exist_ok=True)
client = chromadb.PersistentClient(path=DATA_DIR)
collection = client.get_or_create_collection('documents')
# Index persists across deploys because /data is a Railway VolumeRecipe: User File Uploads
For applications where users upload files (PDFs, images, CSVs for RAG ingestion), store them on the volume rather than shipping them to S3 — simpler for low-to-mid volume applications.
import os
from pathlib import Path
UPLOAD_DIR = Path(os.environ.get('UPLOAD_DIR', '/data/uploads'))
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
def save_upload(filename: str, content: bytes) -> Path:
filepath = UPLOAD_DIR / filename
filepath.write_bytes(content)
return filepathVolume Limitations
- Volumes are attached to a single service — they cannot be shared between services in the same project
- A service can have at most one volume
- Volumes are regional — if you change your service region, volume data is not migrated automatically
- No built-in versioning or point-in-time restore (unlike managed databases)
For shared file storage across multiple services, use an S3-compatible object store (Cloudflare R2, AWS S3, or Backblaze B2) instead of a Railway Volume.| Metadata | Value |
|---|---|
| Title | Railway Persistent Volumes: How Storage Actually Works Across Deploys |
| Tool | Railway |
| Primary SEO keyword | railway persistent volumes |
| Secondary keywords | railway storage, railway volume mount, railway file persistence, railway ollama |
| Estimated read time | 7 minutes |
| Research date | 2026-04-14 |