Self-Hosting FFmpeg on Railway: Ephemeral Disk, Exit 137, and the 15-Minute Cap

You added ffmpeg to your Railway service, transcoded a test clip, and shipped it. Then a real user uploaded a 200 MB video and the container died with exit code 137, or the finished file vanished after your next deploy. Self-hosting FFmpeg on Railway starts easy and then breaks in ways that have nothing to do with FFmpeg itself.
Quick answer: You can run FFmpeg on Railway by adding it through anixpacks.toml(nixPkgs = ["...", "ffmpeg"]) or a Dockerfile that runsapt-get install -y ffmpeg. Installing the binary is the easy part. What breaks production is Railway's ephemeral filesystem (outputs disappear on redeploy), memory limits that OOM-kill large transcodes with exit code 137, and the 15-minute ceiling on a single HTTP request. Offloading the job to a hosted FFmpeg API removes all three.
Getting FFmpeg installed on Railway (and the config that silently fails)
Railway builds with Nixpacks by default, and Nixpacks does not include FFmpeg. You add it one of two ways.
The Nixpacks route is a nixpacks.toml at the repo root:
[phases.setup]
nixPkgs = ["...", "ffmpeg"]
The "..." is the part people miss. It tells Nixpacks to extend the auto-detected packages (your Node or Python toolchain) instead of replacing them. Drop it and Nixpacks installs FFmpeg but strips the runtime it detected, so the build passes and the app won't boot. This is the single most common "Nixpacks not installing FFMPEG" thread on the Railway help station.
The Dockerfile route is more predictable, which is why most people end up here:
FROM node:20-slim
RUN apt-get update && apt-get install -y ffmpeg && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
RUN npm ci
CMD ["node", "server.js"]
Commit the Dockerfile, connect the repo, and Railway detects it and builds your container. Either path gets you a working ffmpeg -version in about ten minutes.
Installing FFmpeg is the easy part. What actually breaks.
Conventional wisdom says Railway is a real container, so of course it can run FFmpeg. That's true. It will run FFmpeg all day on small clips. The wall isn't the binary. It's the three assumptions FFmpeg makes about the box it runs on, none of which hold on a PaaS.
The ephemeral filesystem eats your output
FFmpeg writes to local disk. Railway's local disk is ephemeral: every redeploy, crash, or restart gives you a fresh container and wipes anything you wrote to the working directory or /tmp. So the transcode finishes, you return a path like /app/output.mp4, and by the time the user clicks download the file is gone.
Railway Volumes fix persistence, but they add work rather than removing it. You get one volume per service, mounted at a path you manage, and you now own cleanup, disk-full errors, and the fact that a volume pins the service to a single instance. This is the same trap Heroku dynos have, covered in Running FFmpeg on Heroku: the ephemeral-filesystem trap. The real fix is to not treat a PaaS container as durable storage.
Exit code 137: OOM kills on real files
Exit code 137 means the kernel sent SIGKILL, and on a container platform that almost always means out of memory. FFmpeg's memory use scales with resolution and filter complexity, not file size on disk, so a 4K source or a heavy scale/overlay filtergraph can spike well past what a small Railway service is allotted. Railway's old free tier capped at 512 MB RAM, and even on paid plans you're sizing (and paying for) a container big enough for your worst-case input that sits idle the rest of the time.
You can't catch a SIGKILL and retry gracefully. The process is simply gone, mid-job.
Long transcodes outlive the request window
If you kick off FFmpeg inside an HTTP handler, you're racing a clock. Railway caps a single HTTP request at 15 minutes. A one-minute social clip transcodes in seconds, but a 30-minute webinar re-encode, or a batch, or a slow libx264 -preset slow pass can blow past that. The connection closes, the user sees an error, and the transcode may still be burning CPU in the background. AWS Lambda users hit the same wall from the other direction, described in FFmpeg on AWS Lambda: the layer-size and timeout problem.
One FFmpeg job pegs the box (concurrency and cost)
FFmpeg is CPU-bound and will happily use every core it's given. On a single Railway service, one active transcode starves your web requests, so the fix is more always-on containers. You're now paying 24/7 for compute you use in bursts, plus a queue system to stop jobs from stampeding the box. This is the same class of problem that pushes people off Firebase Cloud Functions, walked through in FFmpeg on Firebase Cloud Functions: why it breaks.
FFmpeg on Railway vs. one API call
The mechanism that removes the four walls isn't a bigger Railway plan. It's not running FFmpeg on Railway at all. Send the job to a managed FFmpeg API, get a URL back.
Here's a 720p compress both ways. The FFmpeg command:
ffmpeg -i input.mp4 -vf "scale=-2:720" -c:v libx264 -crf 23 -c:a aac -b:a 128k output.mp4
The same job as one call to the FFmpeg Micro API. The shape is a job you submit, then poll or receive a webhook, then download the output. Exact endpoints and parameters are in the docs; this is the request shape:
curl https://api.ffmpeg-micro.com/v1/jobs \
-H "Authorization: Bearer $FFMPEG_MICRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "https://your-bucket/input.mp4",
"operations": [
{ "type": "transcode", "scale": "-2:720", "video_codec": "libx264", "crf": 23 }
]
}'
| Self-host on Railway | FFmpeg Micro API | |
|---|---|---|
| Install FFmpeg | `nixpacks.toml` or Dockerfile | Nothing to install |
| Output storage | Ephemeral disk or a volume you manage | Returned as a URL |
| Large-file memory | You size the container, risk exit 137 | Handled server-side |
| Jobs over 15 min | Cut off by the request cap | Async job, poll or webhook |
| Concurrency | Pay for always-on containers | Scales per job |
| Wiring | Custom queue and storage code | One call from code, n8n, Make, Zapier, or an AI agent |
There's a free tier to start, and you can run a job with no signup on the /try page.
When self-hosting FFmpeg on Railway is fine
Self-hosting on Railway is the right call in a few honest cases. If your inputs are small and predictable (short clips, known resolution), a single job finishes in seconds well under the 15-minute cap, and volume is low, a Railway container plus a Dockerfile is cheap and simple. It's also fine for internal tools where an occasional failed transcode isn't a production incident, or when you have a hard requirement to keep media inside your own infrastructure and are willing to own the volume, queue, and memory sizing.
If any of those flip (public users, unpredictable file sizes, jobs that must not silently die, bursty concurrency), the operational cost outruns the API price fast.
Common pitfalls
- Dropping the
"..."innixpacks.toml. It replaces your language runtime instead of adding FFmpeg. Keep it, or switch to a Dockerfile. - Returning a local file path. The container the user downloads from may not be the one that wrote the file. Upload to object storage or use an API that returns a URL.
- Running FFmpeg in the request handler. Anything that might exceed 15 minutes needs a background worker, or it dies with the connection open.
- Sizing for your average input. OOM (exit 137) is triggered by your worst input, not your typical one. A single 4K upload takes down a service tuned for 720p.
- Missing codecs. Some Debian FFmpeg builds ship without certain encoders. Verify with
ffmpeg -encodersbefore assuminglibx264oraacare present.
FAQ
Does Railway come with FFmpeg installed?
No. Railway's default Nixpacks build image does not include FFmpeg. You add it with a nixpacks.toml (nixPkgs = ["...", "ffmpeg"]) or a Dockerfile that runs apt-get install -y ffmpeg.
Why does my FFmpeg process exit with code 137 on Railway?
Exit code 137 is a SIGKILL from the kernel, and on Railway it almost always means the container ran out of memory. FFmpeg's memory use scales with resolution and filtergraph complexity, so a high-resolution source or heavy filters can exceed your service's RAM and get killed mid-transcode.
Can Railway volumes fix the ephemeral filesystem problem for FFmpeg?
Volumes make output persist across redeploys, but you get one volume per service, you manage cleanup and disk limits yourself, and the volume pins the service to a single instance. It solves persistence, not the memory, timeout, or concurrency walls.
How long can an FFmpeg job run on Railway?
A single HTTP request is capped at 15 minutes on Railway. FFmpeg jobs that might run longer (long sources, slow presets, batches) must run in a background worker, not inside the request, or they'll be cut off with the connection still open.
Should I use Railway or a hosted FFmpeg API?
Use Railway if your inputs are small and predictable and low-volume. Use a hosted API like FFmpeg Micro when files are large or unpredictable, jobs can exceed 15 minutes, or you don't want to run a queue, a volume, and always-on containers just to process video.
Wire the job into your existing stack and skip the Railway container entirely: sign up free and send your first transcode as one API call.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

Running FFmpeg on Heroku: The Ephemeral-Filesystem Trap (and the Fix)
Heroku kills FFmpeg jobs three ways: ephemeral filesystem, slug-size limits, and dyno timeouts. Learn the failure modes and the fix.

How to Add AI Voiceover to Video with ElevenLabs and FFmpeg Micro
Generate AI voiceover with ElevenLabs TTS and merge it into video using FFmpeg Micro API. Replace or mix audio with no FFmpeg binary needed.

How to Use FFmpeg with Next.js (No Binary Required)
Use FFmpeg Micro cloud API to process video in Next.js Route Handlers and Server Actions. No binary installation, no serverless timeouts.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free