Railway Nixpacks Python Dependency Hell
Railway + nixpacks + Complex Python Dependencies = 6 Hours of Pain
What Happened

Deploying a FastAPI + MCP SDK Python server to Railway. What should have been a single deploy command turned into 12 consecutive fix commits across 6 hours (Jun 30, 14:27 - 21:05).
The Cascade
Each “fix” revealed the next problem:
- nixpacks didn’t detect Python -> added
providers = ["python"]to nixpacks.toml externally-managed-environment-> pip refuses to install into system Python on newer base images; needed virtualenv- anyio version conflict -> FastAPI wanted anyio<4, MCP SDK wanted anyio>=4.5; upgraded FastAPI to 0.115+
- pydantic vs pydantic-settings -> MCP requires pydantic-settings>=2.5.2 but pinned pydantic<2.10 was incompatible
- PyYAML build from source failed -> Railway’s build image lacked C compiler for PyYAML’s Cython extension; pinned binary wheel version
- pip resolver timeout -> with all the loose constraints, pip explored hundreds of candidates; added constraints file
- Constraints file not found -> nixpacks changed working directory during install; fixed path references
- Still timing out -> generated full lockfile (requirements.in -> requirements.txt with 905 pinned lines)
- pywin32 in lockfile -> generated on macOS, included Windows-only package; removed manually
- Start command crash -> Railway exec’d the command directly; needed shell wrapper
- Import errors -> wrong class names in mcp_http_server.py (
RedCorsairMCPServervs actual class) - Non-existent method call ->
get_request_count_totaldidn’t exist on the rate limiter
Root Cause
See definitions/root-cause-analysis for the analytical framework. Specific cause: nixpacks abstracts away the build environment but provides no escape hatch when pip resolution goes wrong. With a complex dependency tree (FastAPI + MCP SDK + Weaviate client + Google AI + Pydantic), the resolver had too many degrees of freedom.
How to Avoid
Delete nixpacks.toml and railway.json entirely. Write a Dockerfile with explicit control over Python version, pip install order, and working directory. Build time dropped from “timeout” to ~30 seconds.
nixpacks is fine for flask + gunicorn. For anything with >20 dependencies and version conflicts, start with a Dockerfile. The time “saved” by nixpacks auto-detection is negative when you spend 6 hours debugging its abstractions. See redcorsair-railway-dockerfile-over-nixpacks for the pattern.