Railway deployment fails with ModuleNotFoundError despite dependencies in pyproject.toml
MCP server (FastMCP 3.0.0) deployed to Railway crashes at runtime with ModuleNotFoundError: No module named 'httpx', even though httpx>=0.27 is listed under [project].dependencies in pyproject.toml. The Dockerfile runs pip install . which completes without error, but none of the declared dependencies are actually installed. A secondary issue compounds the problem: Railway's health check is configured to hit / which returns 404 because FastMCP serves its endpoint at /mcp, so Railway marks the deployment as FAILED even if the server manages to start.
Two root causes, both in project configuration:
1. Missing [build-system] table in pyproject.toml
Without a [build-system] table, pip install . uses a legacy fallback that silently skips dependency installation. Add the table:
[build-system]
requires = ["setuptools>=75.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"
Or, for belt-and-suspenders safety, also pre-install critical deps explicitly in the Dockerfile before pip install .:
RUN pip install httpx>=0.27 fastmcp>=2.0
RUN pip install .
2. Health check path mismatch
FastMCP serves at /mcp, not /. Configure Railway's health check to match:
- Set health check path to
/mcpin Railway service settings - Or add a root health endpoint in your server code:
from starlette.responses import JSONResponse
from starlette.routing import Route
async def health(request):
return JSONResponse({"status": "ok"})
# Add to your ASGI app's routes
Key points:
- pip install . without [build-system] is a silent failure — the build succeeds but deps are missing
- Always verify installed packages with pip list in a Dockerfile RUN step during debugging
- Railway health checks default to / — confirm the actual serve path of your framework