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

  1. In the Railway dashboard, select your service
  2. Click the three-dot menu > Add Volume
  3. Set the mount path — this is where your app reads/writes persistent data
  4. Set the volume size (start with 1–5 GB; you can resize later)
  5. 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 Q4

Recipe: 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 Volume

Recipe: 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 filepath

Volume 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