Skip to departures
Adinalbani
AIM END

Declarative Deployment Configs: Railway, Render, Fly.io, Vercel and Netlify Compared

Gate
AI & ML
Departs
Crew
Michael Johnson
Passengers
457

Compare deployment configuration across five platforms, from full-stack resource definitions to frontend builds, and see where extra provisioning still matters.

Declarative Deployment Configs: Railway, Render, Fly.io, Vercel and Netlify Compared

Blog graphic: The Best Declarative Deploy Configs in 2026

The practical question when choosing a deployment config is how much of your application it can reproduce. A file that records build and start commands can make one service easier to deploy. A config that also declares databases, service connections, networking, and environment differences can help a team rebuild a larger part of its stack consistently.

Railway and Render are the strongest fits here for teams that want to define connected application resources. Railway uses a programmable definition and a plan-and-apply workflow; Render describes resources in YAML. Fly.io puts more runtime and network controls in the app config, while leaving some surrounding setup to separate workflows. Vercel and Netlify focus on web builds, routing, functions, and deployment contexts.

Those differences matter more than the file extension. A useful config should match the layer your team wants to manage, and make the remaining manual work visible. No deployment file automatically supplies initialized data, secret values, repository access, or every external resource an application needs.

What the deployment file is responsible for

Dashboard-based setup is quick when an application is small. As it grows, however, settings can drift: a worker command changes in one environment, a database is added through a separate screen, or staging gradually diverges from production. It can become difficult to tell what changed, who changed it, and whether another environment has the same setup.

Putting deployment settings in a version-controlled file gives those changes a reviewable history alongside application code. A proposed change can show that a service will gain a replica, a start command will change, or a database will be added. That makes configuration review part of the work before a deployment is applied.

Deployment tooling sits between two familiar layers. A Dockerfile defines how to build an image: its base image, dependencies, build steps, and default runtime command. At the other end, broad infrastructure tools can describe networks, compute, databases, and access settings across providers. Platform deployment configs occupy the application layer between them. They tell a hosting platform how to build and run services, connect related resources, and manage environments.

A Dockerfile is useful when a project needs custom system packages, a specific base operating system, a particular build process, or an image that can move between hosting providers. Platforms with automatic builders may make a custom Dockerfile unnecessary. In either case, an image definition usually doesn’t configure the database connection, public routing, persistent storage, or the platform’s deployment behavior.

Docker Compose describes a group of containers and their networks, volumes, and configuration. It works well for local setups such as an API, Postgres, and a worker running together. It can also support a small application on infrastructure your team operates. Compose describes the containers; you still need to manage the host and the surrounding production setup.

A platform config can go further. Depending on the provider, it may describe services, workers, scheduled jobs, databases, persistent storage, build and start commands, variables, health checks, scaling, domains, networking, and staging or preview environments. The key evaluation is how much of the actual stack can be reproduced from that artifact and what still needs a dashboard setting or separate provisioning step.

Five config models, five different scopes

PlatformConfig formatMain scopeGood fit
Railway.railway/railway.ts; Python and Go authoring in betaProject and environment resourcesMulti-service apps with reusable definitions
Renderrender.yamlConnected resources in a BlueprintStacks described as static YAML
Fly.iofly.tomlApp runtime and Machine settingsApps needing explicit runtime and network control
Vercelvercel.json, vercel.toml, or vercel.tsBuilds, routing, and functionsFramework-driven web applications
Netlifynetlify.tomlBuilds, routing, functions, and deploy contextsSites and apps with preview workflows

Railway and Render treat connected application infrastructure as the main unit. Railway evaluates a programmatic definition against a selected environment. Render uses a YAML Blueprint to list resources across projects and environments. Both can represent a broader stack than a service-level build file.

Fly.io centers its config on an app and the Machines that run its processes. Teams can specify settings such as ports, CPU, memory, health checks, regions, and volume mounts. A multi-part app generally has separate app configs, and database provisioning takes another step.

Vercel and Netlify configure a web deployment model. Their files handle builds and related behavior such as routes, headers, functions, and scheduled tasks. Netlify also uses deploy contexts for settings that differ between production, branch deployments, and previews. If an application needs independently provisioned databases or services, teams should account for how those resources are created and connected beyond the web config.

Railway: reusable definitions with a review step

Railway Infrastructure as Code replaces the older service-level railway.json and railway.toml approach. TypeScript authoring is generally available; Python and Go are in beta. The definition describes resources in a selected project environment, so teams can express environment-specific differences without copying an entire configuration for each environment.

The CLI workflow includes a plan step and an apply step:

railway config plan
railway config apply

railway config plan shows and validates proposed changes. Apply previews the changes and asks for confirmation before making them. Existing projects can be imported with railway config pull. That workflow is a useful match for teams that want to inspect infrastructure changes before applying them, and whose application resources need to evolve alongside the code.

There is a tradeoff: the configuration is programmatic. That creates room for reuse and environment logic, but a reader must understand the definition’s evaluation to know what it will create. Teams that prefer a static file may find Render or Fly’s config easier to inspect directly.

Railpack handles application builds. It detects supported languages and frameworks, installs dependencies, and produces a container image using detected or configured build and start commands. Railpack and Railway’s infrastructure config have separate jobs: one builds the application, while the other describes project resources and settings.

You can use Railway without a Dockerfile when Railpack supports the build. A Dockerfile remains useful when the app needs a custom image, system packages, a particular base image, or a build process that Railpack doesn’t cover.

Render: connected resources in YAML

Render’s render.yaml can describe web services, workers, scheduled jobs, Postgres databases, and environment groups. A service can refer to a database connection string as a resource property, rather than requiring someone to copy the connection value manually.

services:
- type: web
  name: api
  runtime: python
  buildCommand: pip install -r requirements.txt
  startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT
  healthCheckPath: /health
  envVars:
  - key: DATABASE_URL
    fromDatabase:
      name: postgres
      property: connectionString
databases:
- name: postgres

This format makes the resource list and relationships visible in a static file. Render Blueprints can also define projects and environments, so the structure needn’t stop at one flat set of services.

The cost of that clarity shows up when environments share most settings but differ in a few places. A YAML file can list staging and production explicitly, but repeated service definitions need to stay aligned. The example full-stack setup in the source declares a frontend, API, and database for each environment. Both frontends point to their matching API, and each API refers to its environment’s database. Render’s resource definitions and compute plans are covered in its Blueprint reference, with cross-service wiring handled in its connection guide.

Fly.io: direct control over app runtime

Fly.io exposes runtime settings in fly.toml, including process commands, ports, health checks, regions, and Machine resources. A config can make decisions about CPU, memory, HTTPS behavior, and service checks explicit.

app = "example-api"
primary_region = "ord"

[http_service]
internal_port = 8080
force_https = true

[[http_service.checks]]
path = "/health"
interval = "30s"
timeout = "5s"
grace_period = "10s"

[[vm]]
cpu_kind = "shared"
cpus = 1
memory = "512mb"

That control is valuable when the way an application runs is part of its design. But a fly.toml describes an app, not necessarily an entire application stack. For a frontend, API, and database across staging and production, teams will typically manage multiple app files and a separate database workflow.

The example architecture uses four app configs: frontend and API for each environment. The API has no public HTTP service; the frontend connects to it over a private network. Because Fly’s private networking uses IPv6, the API process listens on :: and the frontend uses the API app’s .internal hostname. App names must be available to the organization, and deployments need to run from the right service directories so Dockerfiles and build contexts resolve.

The configs describe runtime behavior, but they don’t provision the Managed Postgres databases, attach them, supply DATABASE_URL, or coordinate a release across the separate apps. Environment names alone also don’t establish private-network isolation. Those steps matter when estimating the operational work around Fly’s explicit runtime controls.

Vercel and Netlify: web deployment configs

Vercel’s project config covers builds, routing, headers, functions, and scheduled tasks. Teams can use vercel.json, TOML, or programmatic TypeScript config. That flexibility fits framework-based applications whose deployment needs center on web delivery. It doesn’t offer the same broad resource description as a full-stack Blueprint.

Netlify’s netlify.toml similarly covers builds, functions, redirects, headers, and deploy contexts. Context overrides can tailor behavior for production, branch deployments, and previews. For both platforms, the scope question is straightforward: if the app depends on an independently operated database or service, how will that resource be provisioned and connected?

Comparing the platforms with a full-stack example

Consider a Node frontend, Python API, and Postgres database, with staging and production environments. The frontend listens on 0.0.0.0:$PORT and proxies browser requests to the API using API_HOST and API_PORT. The API is a FastAPI application at main:app, has a /health endpoint, and reads a database URL. Each environment has its own database, and both deploy from main. This example doesn’t cover release promotion.

The frontend proxy lets a browser use one public origin while the API stays on the private network. The application must implement the proxy; deployment config supplies the destination. That division is easy to miss when comparing config files: the hosting platform can connect services, but it can’t add application behavior that the code doesn’t implement.

Railway’s version of the stack

A single .railway/railway.ts definition can describe Postgres, the API, and the frontend for the environment selected in the CLI. The same structure applies to staging or production, with the frontend domain changing according to the selected environment. The example connects the API to Postgres through DATABASE_URL and the frontend to the API through its private domain.

The Railway authoring API supplies the resource connections. The target environments must already exist; custom domains still need DNS configuration. Repository access, application code, and database schema initialization are also prerequisites. A reusable definition reduces duplicated configuration, but it doesn’t make those setup requirements disappear.

Render’s version of the stack

A Render Blueprint can define both environments and their connected resources in one file. In the example, the staging and production frontend, API, and database definitions are each listed directly. The API’s DATABASE_URL comes from its environment’s database, and each frontend gets its matching API host.

This makes the full resource inventory visible, at the price of repeated blocks. The example also leaves several tasks outside the file: authorizing repository access, setting up the Blueprint, configuring DNS, supplying third-party secrets, and running migrations or restoring data. Render’s config declares the services, databases, and connections, but the environment still needs its credentials and initialized data.

The difference between the Railway and Render examples is primarily how reuse is expressed. Railway evaluates one definition against the chosen environment. Render lists each environment’s resources explicitly. Teams should weigh the convenience of shared logic against the ease of reading a static resource list.

Fly.io’s version of the stack

Fly’s example uses separate frontend and API apps in each environment. The API listens privately, checks its health endpoint, and receives a database URL through a separate secret workflow. The frontend points to the corresponding API’s internal hostname. Both services have runtime settings in their own files.

That approach keeps Machine and app settings explicit, while leaving database provisioning and app coordination outside those files. It also means four app configs to manage for this small two-environment example. The source notes that Fly’s app reference covers runtime settings and Managed Postgres has its own provisioning workflow; the configs don’t declare the database or a coordinated release.

Moving from Render YAML to Railway

A migration is easier to reason about when it starts with resource relationships rather than a field-by-field translation. A Render web service maps to a Railway service(); a background worker can also use service() with an appropriate worker command. A Postgres resource maps to postgres(). Environment variables move to a service’s env settings, while database references need to be checked for the right connection format and application driver.

Render’s healthCheckPath has a Railway healthcheck counterpart. Confirm the endpoint’s behavior and the platform’s deployment checks using the health-check documentation. A preview environment also needs a deliberate mapping; Railway PR Environments can provide temporary infrastructure, but their provisioning rules and values require review.

For the example application, the key relationships are Postgres to API through DATABASE_URL, and API to frontend through the private connection. Check source, build, start, and public access settings for each service. Importantly, review the proposed infrastructure before applying it. The Railway IaC reference documents the relevant fields, while Render’s Blueprint reference describes its source fields.

Reproducibility includes more than resource definitions

Staging and production should share the same application structure wherever possible. Make intentional differences visible: domains, replica counts, external API credentials, deployment branches, and other environment-specific values. Replica settings can differ when the environments have different needs, but the reason should be clear.

Railway environments provide isolated service configurations, and PR Environments can add temporary infrastructure for reviewing changes. Similar environment features are useful only when the required settings and resources are defined consistently.

Secrets are a separate part of the setup. A config can refer to a secret, but that secret must exist in the target environment. Teams should account for the platform’s variable setup and the corresponding Render environment-variable and secret workflows.

Nor does defining a database reproduce its contents. A new environment may need an empty database, migrations, fixtures, or a controlled data copy. Decide what is appropriate before treating a configuration as a repeatable application setup. The resource can be declared while its schema and data still require separate work.

When the platform config isn’t enough

Some systems span more than application services and their adjacent resources. They may depend on cloud networking, specialized compute, or resources across multiple providers. That broader scope can call for a separate infrastructure tool alongside the deployment platform.

Terraform manages provider-defined resources through reusable modules and state, with a plan-and-apply workflow. It can fit an application hosted on a PaaS that also depends on cloud storage, DNS, or resources in another provider. The broader reach brings more work: provider configuration, state management, and ownership of the resources being provisioned.

Kubernetes manages container workloads through resources such as Deployments, Services, Jobs, and storage definitions. Helm packages and templates those resources. This layer fits teams that need direct control over container orchestration or already operate a shared cluster. It also brings decisions about cluster operations, networking, upgrades, and observability that a PaaS handles for its users.

CloudFormation describes AWS resources such as compute, networking, databases, and IAM settings. AWS SAM builds on it with a model for serverless applications. These tools fit architectures built around AWS services and permissions, and represent a wider infrastructure commitment than choosing where to run an API and its database.

NeedConfig layer
Define how one container image is builtDockerfile
Run a small multi-container applicationDocker Compose
Manage application resources through a platformRailway, Render, or Fly.io
Deploy a framework-driven web applicationVercel or Netlify
Manage infrastructure across providersTerraform
Control container orchestrationKubernetes
Manage AWS resources directlyCloudFormation or SAM

These layers can work together. A Dockerfile can build an image for a PaaS deployment, while Terraform manages other infrastructure the application depends on. Choose the layer that covers the resources your team needs to reproduce, then add broader tooling only where the platform stops covering them.

Choosing a config by the work you want it to capture

For a full-stack application, Railway and Render provide the clearest paths in this comparison to describing connected services and databases together. Railway suits teams that want reusable code and a plan-and-apply review workflow. Render suits teams that prefer a static YAML inventory and can keep repeated environment definitions aligned.

Fly.io makes sense when runtime, region, Machine, and network decisions deserve direct expression and the team is comfortable provisioning databases and coordinating multiple apps. Vercel and Netlify fit applications centered on their web build, routing, function, and preview workflows. Their config files shouldn’t be mistaken for a complete description of separate infrastructure.

A Dockerfile defines an image; a deployment config tells a platform how to deploy an application and may also describe surrounding resources. Whether one config can be reused across staging and production depends on the platform and the definition. Railway can evaluate the same definition against existing target environments, with environment-specific domains or values. Each environment still needs its own secrets and database initialization.

Before committing to a platform, try reproducing a small version of the application from its configuration artifact. Track what the file creates, what it connects, and which setup steps remain. That exercise gives a concrete measure of how much consistency the config provides—and where the team will need a separate process.

Source: Michael Johnson · blog.railway.com
Arrived · Adinalbani

Also departing from this gate