Railway app vs Granite.so: deploy speed, CLI, and free tier

By Mark Hayes
Railway app vs Granite.so: deploy speed, CLI, and free tier

Railway earned its reputation honestly. You link a repo, it figures out how to build it, and a few minutes later you have a URL. For a weekend project, that experience is hard to beat. The questions change once the project turns into a SaaS with paying users: how fast are deploys when you ship ten times a day, how much of your workflow lives in the CLI, what happens when the free credits run out, and what will the bill look like in month six?

This post compares the Railway app platform with Granite.so on those four questions, from the perspective of a solo founder running a typical stack: a web frontend, an API, a Postgres database, and file uploads. It aims to be fair. Railway is a good product, and for some projects it is the better choice.

Pricing on both platforms changes. Figures below are described at the time of writing (October 2026). Check each provider's pricing page before you decide.

The short version

RailwayGranite.so
Deploy sourcesGit repo, Dockerfile, Docker image, templatesGit repo (auto-build on push), Docker image
Build detectionAutomatic builder (Railpack), or your DockerfileYour Dockerfile or source build
CLIMature CLI (railway up, railway run, logs, variables)novps CLI: declarative novps.yaml manifest, apps apply --wait, logs, shell into containers, --json on every command
DatabasesPostgres, MySQL, Redis, MongoDB as services on the project canvasManaged databases with platform-handled backups
Object storageCheck current offering; many teams pair with an external S3 providerS3-compatible storage with CDN built in
Pricing modelSubscription plus metered usage (CPU, memory, egress, volumes)Hobby plan at $9/month including $15 of usage credits; outbound traffic not billed
Free optionTrial credits, then a limited free tierNo permanent free tier; Hobby is the entry point

If you want the most polished CLI and the fastest zero-to-URL for experiments, Railway is excellent. If you want app hosting, database, and CDN-backed storage in one bill that you can predict before you deploy, read on.

Deploy speed: what actually makes it fast

"Deploy speed" is usually measured as time from git push to the new version serving traffic. On any platform, that time breaks down into the same parts:

  1. Queue time before the build starts.

2. Build time: installing dependencies and compiling.

3. Image push and pull: moving the built artifact to where it runs.

4. Startup and health check: the new instance booting and passing its readiness check.

Railway's automatic builder is fast for common frameworks and caches layers between builds. Granite.so builds from your Dockerfile on every push to the branch you choose. In practice, for a small app, the platform difference is usually smaller than the difference your own setup makes. The biggest wins are in your repository, and they apply everywhere:

Order Dockerfile layers for caching

Copy dependency manifests before source code so a code-only change skips the install step:

FROM node:22-alpine
WORKDIR /app

# Changes rarely: cached between most builds
COPY package.json package-lock.json ./
RUN npm ci

# Changes on every commit
COPY . .
RUN npm run build

CMD ["npm", "start"]

Keep the build context small

A missing .dockerignore can turn a 5 MB upload into 500 MB:

# .dockerignore
node_modules
.next
.git
coverage
*.log
.env*

Make health checks honest and quick

A health endpoint that takes 20 seconds to respond adds 20 seconds to every deploy. Return quickly once the app can serve requests and reach the database. Nothing more.

Measure it yourself

Do not trust anyone's benchmark, including ours. Push the same commit to both platforms three times and time it:

git commit --allow-empty -m "deploy timing test"
START=$(date +%s)
git push origin main
# poll until the new commit hash appears at /version
until curl -fs https://your-app.example.com/version | grep -q "$(git rev-parse --short HEAD)"; do
  sleep 2
done
echo "Live after $(( $(date +%s) - START ))s"

This assumes your app exposes the current commit in a /version endpoint, which is worth having regardless of platform.

CLI: two different philosophies

Railway's CLI is genuinely good, and it is a fair reason to choose the platform. A typical workflow looks like this:

# install (Homebrew or npm)
brew install railway
# or: npm install -g @railway/cli

railway login
railway init            # create a new project
railway link            # or link the current directory to an existing one
railway up              # build and deploy the current directory
railway logs            # stream logs for the linked service
railway variables       # show environment variables
railway run npm test    # run a local command with the service's env vars injected

railway run is the standout. It lets you run migrations, scripts, or a local dev server with production-like environment variables without copying secrets into a .env file.

Granite.so has a CLI too, with a different emphasis. The binary is still called novps, after the platform's former name, and it is built around a declarative manifest: the whole app (web services, workers, cron jobs and environment) is described in a novps.yaml file and applied in one command, so the configuration lives in your repository rather than only in a dashboard.

curl https://cli.granite.so | sh
novps auth login
novps apps export my-api -o novps.yaml        # start from an app you already have
novps apps apply my-api -f novps.yaml --wait  # create or update it, wait for the URL
novps resources logs <resource-id> -f         # stream logs
novps resources connect <resource-id>         # open a shell in a running container

Every command accepts --json, and apps apply --wait exits non-zero when a deployment fails, which makes the CLI easy to script in CI or hand to a coding agent. Git push to your chosen branch still triggers an automatic build, and a non-engineer co-founder can see the same services, logs and settings in the dashboard without installing anything.

What Granite.so does not have is a direct equivalent of railway run. The closest thing is pulling connection details into a local, git-ignored file with novps databases get <id> --format env or novps secrets get, and then loading that file with a small script:

# scripts/with-env.sh
set -a
. ./.env.production.local
set +a
exec "$@"
./scripts/with-env.sh alembic upgrade head

Free tier: what "free" means for a real SaaS

Railway moved away from an always-on free tier some time ago. At the time of writing, new accounts get trial credits, and there is a limited free plan after that, with paid plans that combine a monthly subscription with metered usage. The details have changed several times, so check Railway's pricing page for current limits.

Here is the honest take for a SaaS founder: the free tier is not where you run production on any platform. Free tiers are for evaluation. They come with constraints such as resource caps, sleeping services, or credit limits that are fine for a demo and unacceptable when a beta user clicks a login link and waits 15 seconds for a cold start.

Granite.so does not offer a permanent free tier. The Hobby plan costs $9 per month and includes $15 of monthly usage credits. That is a deliberate trade: you pay a small fixed amount from day one, and in exchange you are on a plan meant for real traffic rather than a trial.

So the useful question is not "which is free?" but "which one gives me a predictable bill for my first 100 users?"

Pricing for a solo SaaS: estimate before you deploy

Railway bills for resources consumed: CPU time, memory over time, network egress, and volume storage, on top of a plan fee that includes some usage. That model is fair and efficient, since you pay for what you use. It also has a failure mode that catches solo founders: memory is billed for what your process holds, not what it needs. A Node process with a slow leak, a Python worker that loads a large model at import, or a database with a generous memory setting can quietly multiply a bill. For the full arithmetic, with worked costs for a web app, a worker and Postgres, see Railway pricing 2026.

If you stay on Railway, protect yourself:

  • Set a usage limit and an alert in your workspace settings. Railway supports both, and it is the single most important setting for avoiding a surprise invoice.
  • Watch the memory graph for each service after every release for the first week.
  • Put staging services to sleep when idle, and delete preview environments you are not using.

A worksheet for your stack

Whichever platform you evaluate, write the estimate down before you deploy. For a typical early SaaS:

ComponentWhat drives costQuestions to ask
Web frontendMemory and CPU over time, instance countDoes it scale to zero? Is there a minimum?
APIMemory and CPU, instance countWhat happens to cost during a traffic spike?
PostgresMemory, disk, backupsAre backups included? Is restore self-serve?
File storageGB stored, egress, CDNIs there a CDN? Is egress through it billed separately?
EgressGB served to usersWhich traffic counts as egress?

On Granite.so, resources are priced by size rather than by use. For this stack, the web frontend and the API are $5 each at 1 vCPU and 1 GB of RAM, the smallest managed Postgres (1 vCPU, 1 GB RAM, 15 GB NVMe) is $8, and storage is $0.035 per GB per month, so 10 GB of uploads adds $0.35. That is about $18 of usage. The Hobby plan includes $15, so you add $3 of extra credits at $1 each and the bill is about $12 a month. Outbound traffic is not billed, and the database's automatic system backups are free. Because the database, storage, and CDN are on the same platform, there is one bill instead of a hosting invoice, a database invoice, and an S3 invoice. For a founder who has just been surprised by a bill, the value is less about the absolute number and more about knowing it in advance.

The cheapest platform is the one whose bill you can predict. A $12 estimate that becomes $180 costs more than a $40 plan that stays $40.

Databases and storage: where the stacks diverge

On Railway, databases are services on your project canvas, backed by volumes. You get a lot of flexibility, including the option to run almost any database image. That flexibility means more of the operational judgment (backups, restore testing, sizing) sits with you. Review what backup options your plan includes and test a restore before launch.

Granite.so treats the database as a managed service: you attach it to your app, the connection string is injected as an environment variable, and backups are handled by the platform. The same goes for storage. S3-compatible buckets come with a CDN, so user uploads are served from cache without setting up a separate CDN provider. For a solo founder, that removes two vendors from the architecture diagram.

When Railway is the better choice

To be direct about it:

  • You want railway run-style local commands with production variables injected, and a CLI that has been polished for years.
  • You are prototyping many small projects and want the fastest path from idea to URL.
  • You need to run unusual services (a specific database, a queue, a self-hosted tool) from a template in seconds.
  • Your usage is small and spiky, and you are disciplined about usage limits.

When Granite.so is the better choice

  • You are running one SaaS product with real users and want app, database, and storage on one predictable bill.
  • You want the whole app (services, workers, cron jobs and environment) described in one manifest in your repository, deployed by Git push or novps apps apply.
  • You want managed database backups without owning a backup process.
  • You serve user-uploaded media and want a CDN without configuring one.

Moving from Railway if you decide to switch

Both platforms deploy containers, so migration is mostly mechanical:

1. Make sure you have a Dockerfile. If Railway was auto-detecting your build, write the Dockerfile explicitly and test it with docker build . locally.

2. Export environment variables. List them with railway variables and recreate them in the new service. Do not copy DATABASE_URL; it will change.

3. Dump the database using the Postgres service's public connection string from its variables:

pg_dump "$SOURCE_DATABASE_URL" --format=custom --no-owner --file=app.dump
pg_restore --dbname="$TARGET_DATABASE_URL" --no-owner --clean --if-exists app.dump

4. Copy object storage if you used an external bucket, with rclone sync or the AWS CLI pointed at the new S3-compatible endpoint.

5. Switch DNS after verifying the new deployment, and keep the old one running for a day in case you need to roll back.

Put the app in maintenance mode or stop writes during the database dump, or you will lose whatever changes arrive between the dump and the DNS switch.

The bottom line

Railway is a strong platform with a great CLI, and if that workflow is your priority, it is a sensible pick. If your priority as a solo founder is a production stack with a bill you can state before you deploy, Granite.so bundles app hosting, managed databases, and CDN-backed storage starting at $9 per month on the Hobby plan. Run the timing script and the cost worksheet against both, and let your own numbers decide.

Keep reading

More posts tagged “Community”.
Be first in line for updates
and special pricing
Get early access to new features and exclusive discounts delivered straight to your inbox