<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sarah Bedell</title>
    <description>The latest articles on DEV Community by Sarah Bedell (@sarah-railway).</description>
    <link>https://dev.to/sarah-railway</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3397372%2Fe9dbafac-a8ab-4fe1-a23a-e8e6fca177df.jpg</url>
      <title>DEV Community: Sarah Bedell</title>
      <link>https://dev.to/sarah-railway</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarah-railway"/>
    <language>en</language>
    <item>
      <title>Monitoring &amp; Observability: Using Logs, Metrics, Traces, and Alerts to Understand System Failures</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/monitoring-observability-using-logs-metrics-traces-and-alerts-to-understand-system-failures-31m6</link>
      <guid>https://dev.to/sarah-railway/monitoring-observability-using-logs-metrics-traces-and-alerts-to-understand-system-failures-31m6</guid>
      <description>&lt;p&gt;Author: Mahmoud Abdelwahab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmf6etsa64mp9ur147o4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmf6etsa64mp9ur147o4e.png" alt="Monitoring &amp;amp; Observability: Using Logs, Metrics, Traces, and Alerts to Understand System Failures" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your application ships to production, it becomes partly opaque. You own the code, but the runtime, network, and platform behaviors often fall outside your direct line of sight. That’s where Monitoring and Observability come in.&lt;/p&gt;

&lt;p&gt;Monitoring warns you when predefined thresholds break. Observability lets you explore unknowns, asking new questions in real time and getting meaningful answers without redeploying.&lt;/p&gt;

&lt;p&gt;For engineers running software in production, observability rests on three pillars: logs, metrics, and traces. Each offers a different lens into system behavior. Understanding where each excels and where it doesn’t is essential for building a practical, scalable visibility strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are metrics, logs, traces, and alerts?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Logs: the detailed narrative and audit trail. Use structured logs, centralize them, tag each request with a correlation or trace ID, and avoid logging sensitive data.&lt;/li&gt;
&lt;li&gt;Metrics: the fast, aggregated signal. Great for dashboards, trends, SLOs, and real-time alerting. Easy to query, but light on context.&lt;/li&gt;
&lt;li&gt;Traces: track a request as it flows through your distributed system. A trace is a collection of spans: each span represents a single operation within a service. Together, spans form a tree structure showing the complete path a request took through your system. Ideal for pinpointing bottlenecks and mapping dependencies. &lt;/li&gt;
&lt;li&gt;Alerts: the early warning system. Alert on user-impacting symptoms aligned to SLOs (Service Level Objectives), route by severity, and attach runbooks to reduce mean time to recover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use them together: an alert points to a metric spike, a trace isolates the slow hop, and logs reveal the root cause and exact error payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------+ +-----------+ +---------+ +------+
| ALERT | ---&amp;gt; | METRIC | ---&amp;gt; | TRACE | ---&amp;gt; | LOG |
+---------+ +-----------+ +---------+ +------+
     | | | |
     | | | |
     v v v v
  SLO breach → metric spike → bottleneck found → root cause confirmed

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Observability Pillars at a Glance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;What It Captures&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Limitations&lt;/th&gt;
&lt;th&gt;Primary Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Discrete events with full context&lt;/td&gt;
&lt;td&gt;Detailed debugging, audits, forensics&lt;/td&gt;
&lt;td&gt;Weak for real-time or cross-service insight&lt;/td&gt;
&lt;td&gt;Root cause analysis and compliance records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aggregated numeric signals over time&lt;/td&gt;
&lt;td&gt;Fast detection, trend analysis, SLO tracking&lt;/td&gt;
&lt;td&gt;Lacks context and per-user granularity&lt;/td&gt;
&lt;td&gt;System health, capacity planning, alerting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Request paths across services&lt;/td&gt;
&lt;td&gt;Dependency mapping, latency analysis, bottleneck isolation&lt;/td&gt;
&lt;td&gt;Gaps without full instrumentation, limited trend visibility&lt;/td&gt;
&lt;td&gt;Distributed performance and latency debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alerts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Threshold-based notifications&lt;/td&gt;
&lt;td&gt;Proactive incident response, SLO enforcement&lt;/td&gt;
&lt;td&gt;Noise, false positives, tuning overhead&lt;/td&gt;
&lt;td&gt;On-call operations and early warning signals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Logs: The Detailed Record
&lt;/h2&gt;

&lt;p&gt;Logs are the most familiar pillar of observability. They're discrete records of events that happened in your system, typically written as text lines with timestamps. When something goes wrong, logs are often your first stop for understanding what happened.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-11-10T14:23:47.612Z INFO [auth-service] User login succeeded user_id=4821 ip=192.168.10.45 duration=142ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Where Logs Shine
&lt;/h3&gt;

&lt;p&gt;Logs excel at providing detailed context. When you need to understand exactly what happened during a specific request or transaction. They capture the sequence of events, error messages, stack traces, and any contextual data your application chose to record. This makes them indispensable for debugging: you can trace through the exact execution path that led to a problem.&lt;/p&gt;

&lt;p&gt;For compliance and audit requirements, logs are essential. They provide an immutable record of what actions were taken, by whom, and when. This is particularly important for applications handling sensitive data or operating under regulatory frameworks like GDPR. When you need to demonstrate that certain data was accessed or modified, logs provide that proof.&lt;/p&gt;

&lt;p&gt;Logs also shine when you need to understand user behavior or business events. If you want to know why a specific user encountered an error, or track a particular transaction through your system, logs are your tool.&lt;/p&gt;

&lt;p&gt;Setting up centralized logging is one of the first observability tasks engineers tackle when deploying to production. Your application instances generate logs locally, but you need them aggregated in one place where you can search, filter, and analyze them. This becomes especially important in containerized environments where containers can be ephemeral: if a container crashes, its local logs disappear with it.&lt;/p&gt;

&lt;p&gt;Structured logging makes logs even more useful. Instead of free-form text, structured logs use a consistent format (often JSON) that makes them machine-readable, enabling advanced querying and filtering capabilities. You can quickly find all logs related to a specific user ID, transaction ID, or error type, and the structure makes it easier to build dashboards and visualizations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "timestamp": "2025-11-10T14:23:47.612Z",
  "level": "INFO",
  "service": "auth-service",
  "event": "user_login_succeeded",
  "user_id": 4821,
  "ip": "192.168.10.45",
  "duration_ms": 142
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing logs in production
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Stream to stdout and stderr, not local files&lt;/li&gt;
&lt;li&gt;Attach a correlation or trace ID to every request and include it in each log line&lt;/li&gt;
&lt;li&gt;Use structured JSON with consistent keys; capture multi-line errors as single JSON lines&lt;/li&gt;
&lt;li&gt;Sanitize at the source: avoid secrets and PII, mask sensitive fields, and add automated redaction&lt;/li&gt;
&lt;li&gt;Make CI/CD and deployment logs searchable with application logs to connect releases to behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where Logs Don't Shine
&lt;/h3&gt;

&lt;p&gt;Logs have a major weakness: real-time analysis at scale. Searching millions of log lines for trends or anomalies is slow. Logs are not built to answer time-sensitive questions like "What’s the current error rate?" or "Is latency rising right now?" For that kind of visibility, metrics are the better fit.&lt;/p&gt;

&lt;p&gt;Logs also fall short when requests span multiple services. Each service writes its own logs, so reconstructing a full request path means stitching together records from different sources and aligning them by timestamps or IDs. Distributed tracing solves this problem by following a request end to end across every service it touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metrics: The Aggregated View
&lt;/h2&gt;

&lt;p&gt;Metrics are numerical measurements collected over time. Unlike logs, which preserve individual events, metrics aggregate data into time-series measurements. Think of metrics as the dashboard of your car: they give you a high-level view of how your system is performing right now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4nd1e5w07thhhsiyrpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4nd1e5w07thhhsiyrpz.png" alt="Example Grafana dashboard" width="800" height="728"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Example Grafana dashboard&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Where Metrics Shine
&lt;/h3&gt;

&lt;p&gt;Metrics excel at providing real-time visibility into system health. You can answer questions like "What's the current request rate?" or "What's the 95th percentile response time?" instantly, without searching through logs. This makes metrics ideal for dashboards that give you an at-a-glance view of your system's state.&lt;/p&gt;

&lt;p&gt;Metrics are perfect for trend analysis, capacity planning, and alerting. By tracking metrics over time, you can identify patterns and predict future needs.&lt;/p&gt;

&lt;p&gt;You can set thresholds on metrics and get notified when values exceed acceptable ranges.&lt;/p&gt;
&lt;h3&gt;
  
  
  What to measure and how to use it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;System metrics: CPU, memory, disk I/O, network throughput and errors&lt;/li&gt;
&lt;li&gt;Application metrics: request rate, error rate, latency percentiles, queue depth, cache hit rates&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Practical guidance for metrics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prefer percentiles (p95, p99) over averages to reflect real user experience&lt;/li&gt;
&lt;li&gt;Avoid unbounded label cardinality; do not label by user ID or raw URL when it explodes series count&lt;/li&gt;
&lt;li&gt;Define SLIs and SLOs, then alert on burn rate across short and long windows&lt;/li&gt;
&lt;li&gt;Pair metrics with deploy markers so regressions are visible at the moment of change&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Where Metrics Don't Shine
&lt;/h3&gt;

&lt;p&gt;The aggregation that makes metrics efficient is also their weakness. Metrics lose the detail that logs preserve. If your error rate metric shows a spike, you know something is wrong, but you can't see the individual errors that caused it. You'll need to dive into logs to understand what actually happened.&lt;/p&gt;

&lt;p&gt;Metrics also don't help much with debugging specific issues. If a user reports a problem, metrics won't tell you what happened to that particular user's request. You need logs or traces for that level of detail.&lt;/p&gt;

&lt;p&gt;Understanding user behavior is another area where metrics fall short. While metrics can tell you how many users are active or what the average session duration is, they can't tell you what a specific user did or why they took a particular action. For that, you need logs or specialized analytics tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  Traces: Following the Request
&lt;/h2&gt;

&lt;p&gt;Distributed tracing, or simply traces, track a request as it flows through your distributed system. A trace is a collection of spans: each span represents a single operation within a service. Together, spans form a tree structure showing the complete path a request took through your system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trace ID: 9f1c7a32b4f94c89a7e6c2d01b8b1234

gateway: ───────────────────────────────────────────── 520ms
              │
auth-service: ──────────────────────────────────────── 380ms
                   │
db: ─────────────── 120ms
                           (SELECT * FROM users WHERE id=4821)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Where Traces Shine
&lt;/h3&gt;

&lt;p&gt;Traces are invaluable for understanding request flow in distributed systems. When a request touches multiple services (which is common in microservices architectures, serverless platforms, or any distributed setup), traces show you the complete journey. You can see which services were involved, how long each operation took, and where bottlenecks occurred.&lt;/p&gt;

&lt;p&gt;Traces pinpoint which service or operation causes latency when users report slowness. This level of visibility is difficult to achieve with logs alone, especially when requests span multiple services.&lt;/p&gt;

&lt;p&gt;Tagging log entries with trace IDs is a common pattern that combines the strengths of logs and traces. When you include a trace ID in your logs, you can start with a trace to see the high-level flow, then use the trace ID to find all related logs for detailed context. Traces also help identify dependencies and understand system architecture by showing which services call which other services, how frequently, and with what latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sampling and propagation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Head-based sampling: decide at the start of a trace whether to record it; cheap, but can miss rare failures&lt;/li&gt;
&lt;li&gt;Tail-based sampling: keep traces that exhibit errors or high latency; better for incident analysis, higher cost&lt;/li&gt;
&lt;li&gt;Environment-aware sampling: higher sampling in staging or canary, lower in steady-state production&lt;/li&gt;
&lt;li&gt;Propagate context: pass trace and span IDs across services, threads, and async boundaries&lt;/li&gt;
&lt;li&gt;Instrument dependencies: external APIs, databases, queues, and caches, so spans cover the full path&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where Traces Don't Shine
&lt;/h3&gt;

&lt;p&gt;Traces introduce storage and processing overhead, especially in high-traffic systems. Every request creates a trace with multiple spans, and storing all of this data can be expensive. Many teams sample traces, only storing a percentage of requests, to manage costs while still maintaining visibility.&lt;/p&gt;

&lt;p&gt;Traces also add some overhead to your application. The instrumentation required to create traces adds latency, though modern tracing libraries keep this overhead minimal. Still, in extremely latency-sensitive applications, even small overheads matter.&lt;/p&gt;

&lt;p&gt;For simple monolithic applications, traces provide less value. If your entire application runs in a single process and you don't have distributed components, logs and metrics might be sufficient. Traces become more valuable as your system becomes more distributed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alerts: The Early Warning System
&lt;/h2&gt;

&lt;p&gt;Alerts are notifications triggered when specific conditions are met. They're your system's way of telling you that something needs attention, ideally before users notice a problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where Alerts Shine
&lt;/h3&gt;

&lt;p&gt;Alerts detect issues proactively by notifying you when metrics breach thresholds or critical services fail. They also catch gradual degradations such as slow memory leaks.&lt;/p&gt;

&lt;p&gt;For on-call engineers, well-configured alerts are essential. They reduce the time between when a problem occurs and when someone starts investigating it. Alerts are also crucial for SLA monitoring, helping you track compliance and respond quickly when you're at risk of violating commitments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing alerts that humans respect
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Alert on user-impacting symptoms aligned to SLOs, not only on low-level causes&lt;/li&gt;
&lt;li&gt;Use multi-window thresholds to catch fast regressions and slow burns without noise&lt;/li&gt;
&lt;li&gt;Route by severity and ownership; page for critical issues, create tickets for non-urgent work&lt;/li&gt;
&lt;li&gt;Attach runbooks that name the likely cause, the first commands to run, and rollback steps&lt;/li&gt;
&lt;li&gt;Deduplicate and group related alerts to reduce noise during incidents&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where Alerts Don't Shine
&lt;/h3&gt;

&lt;p&gt;The biggest challenge with alerts is alert fatigue. When alerts fire too frequently, especially for non-critical issues, engineers start ignoring them. False positives are another problem: if alerts fire for conditions that aren't actually problems, engineers lose trust in the alerting system. This often happens when thresholds are set too aggressively or when alerts don't account for normal variations in system behavior.&lt;/p&gt;

&lt;p&gt;Alerts require proper threshold configuration and are only as good as the data they're based on. Set thresholds too tight, and you'll be overwhelmed with noise. Set them too loose, and real problems will go undetected. Finding the right balance takes time and iteration, and it changes as your system evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability on Railway
&lt;/h2&gt;

&lt;p&gt;Understanding the concepts behind logs, metrics, traces, and alerts is essential, but putting them into practice requires the right tools. Railway provides built-in observability features that address many of the challenges engineers face when deploying to production, integrating all four pillars into a unified platform.&lt;/p&gt;

&lt;p&gt;You can try these observability tools in your own environment — &lt;a href="https://railway.com/?referralCode=thisismahmoud" rel="noopener noreferrer"&gt;deploy a service on Railway&lt;/a&gt; and inspect logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Centralized Logging
&lt;/h3&gt;

&lt;p&gt;Railway automatically captures all logs emitted to standard output or standard error from your applications. Any &lt;code&gt;console.log()&lt;/code&gt; statements, error messages, or application output are immediately available for viewing and searching without additional configuration.&lt;/p&gt;

&lt;p&gt;You can access logs in different ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service logs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Drill into a single deployment's build, deployment and runtime logs&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95fxuzin81fqgd8ohvaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95fxuzin81fqgd8ohvaf.png" alt="Service-level build, deployment and HTTP logs on Railway" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Service-level build, deployment and HTTP logs on Railway&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Log Explorer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Log Explorer enables environment-wide search across all services. It also supports advanced filtering syntax: search for partial text matches, filter by service or replica, or use structured log attributes like &lt;code&gt;@level:error&lt;/code&gt; to find all error-level logs. Railway's environment logs let you query logs from all services simultaneously, addressing the challenge of correlating events across services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yo9gvadsksdcsouqwvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yo9gvadsksdcsouqwvn.png" alt="Railway log explorer" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway log explorer&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Structured logging is fully supported. When you emit JSON-formatted logs with fields like &lt;code&gt;level&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, and custom attributes, Railway automatically parses and indexes them. You can filter by custom attributes using &lt;code&gt;@attributeName:value&lt;/code&gt;, making it easy to find logs related to a specific user ID, transaction, or any metadata you include.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueewx75wc6xtis2gvxq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueewx75wc6xtis2gvxq0.png" alt="Railway Log explorer filtering" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway Log explorer filtering&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Filtering examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;request&lt;/code&gt;: find logs that contain the word request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"POST /api"&lt;/code&gt;: find logs that contain the substring POST /api&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@level:error&lt;/code&gt;: filter by error level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The CLI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can use the &lt;a href="https://docs.railway.com/reference/cli-api" rel="noopener noreferrer"&gt;Railway CLI&lt;/a&gt; for quick checks from the terminal by running &lt;code&gt;railway logs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ railway logs --help
View the most-recent deploy's logs

Usage: railway logs [OPTIONS]

Options:
  -d, --deployment Show deployment logs
  -b, --build Show build logs
      --json Output in JSON format
  -h, --help Print help
  -V, --version Print version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see how structured logs behave in production, &lt;a href="https://railway.com/?referralCode=thisismahmoud" rel="noopener noreferrer"&gt;deploy a small service on Railway&lt;/a&gt; and stream its output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metrics and Performance Monitoring
&lt;/h3&gt;

&lt;p&gt;Railway provides real-time metrics for CPU, memory, disk usage, and network traffic for each service, available directly in the service dashboard with up to 30 days of historical data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjw8ckr7iobkaqwf2ox6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjw8ckr7iobkaqwf2ox6.png" alt="Service-level metrics on Railway which include CPU/Memory utilization, Number of Requests broken down by status code and egress" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Service-level metrics on Railway which include CPU/Memory utilization, Number of Requests broken down by status code and egress&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If a service has multiple replicas, you can view metrics as a combined sum or per replica.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Observability Dashboard
&lt;/h3&gt;

&lt;p&gt;Railway's Observability Dashboard brings logs, metrics, and project usage together in a single customizable view. It is scoped per environment, and you can create widgets that display specific metrics, filtered logs, or project spend data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkjcf78lz7i2cam8fwij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkjcf78lz7i2cam8fwij.png" alt="Railway Observability" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway Observability&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Alerts and Notifications
&lt;/h3&gt;

&lt;p&gt;Railway provides two complementary approaches to alerting: monitors for metric-based alerts and webhooks for deployment notifications.&lt;/p&gt;

&lt;p&gt;Monitors allow you to configure email alerts/notification when metrics exceed thresholds for CPU, RAM, disk usage, or network egress. This addresses proactive issue detection: instead of waiting for users to report problems, you're notified when resource usage indicates potential issues. Monitors are configured directly on dashboard widgets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oamrx61n9pl5wkrlbgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oamrx61n9pl5wkrlbgg.png" alt="Set up monitoring on Railway" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Set up monitoring on Railway&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Webhooks provide a flexible notification mechanism for deployment state changes and custom events. Railway automatically transforms payloads for popular destinations like Discord and Slack, so you can integrate notifications into your existing team communication channels.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1vy0tjshrm4gt5ge4t1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1vy0tjshrm4gt5ge4t1.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Whether you're deploying a side project or running a production SaaS, Railway’s observability features give you full visibility into your system. Logs are centralized automatically, metrics are collected with no setup, and alerts are easy to configure. Request tracing support is coming soon. Railway handles the infrastructure so you can focus on your application, not the tooling. &lt;a href="https://railway.com/?referralCode=thisismahmoud" rel="noopener noreferrer"&gt;Start a project and see it in action.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Server rendering benchmarks: Railway vs Cloudflare vs Vercel</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/server-rendering-benchmarks-railway-vs-cloudflare-vs-vercel-371k</link>
      <guid>https://dev.to/sarah-railway/server-rendering-benchmarks-railway-vs-cloudflare-vs-vercel-371k</guid>
      <description>&lt;p&gt;Author: Mahmoud Abdelwahab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhl2usopswjiz5o0d78j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhl2usopswjiz5o0d78j.png" alt="Server rendering benchmarks: Railway vs Cloudflare vs Vercel" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A couple of weeks ago, independent developer &lt;a href="https://x.com/theo" rel="noopener noreferrer"&gt;Theo Browne&lt;/a&gt; published &lt;a href="https://github.com/t3dotgg/cf-vs-vercel-bench" rel="noopener noreferrer"&gt;a set of benchmarks&lt;/a&gt; comparing server-side rendering (SSR) performance between Cloudflare Workers and Vercel Functions.&lt;/p&gt;

&lt;p&gt;At first, the tests showed Cloudflare lagging behind — about 3–5× slower than Vercel Functions. But then Cloudflare &lt;a href="https://blog.cloudflare.com/unpacking-cloudflare-workers-cpu-performance-benchmarks/" rel="noopener noreferrer"&gt;rolled out a wave of performance improvements&lt;/a&gt;, flipping the script and, in some cases, pulling ahead.&lt;/p&gt;

&lt;p&gt;We were intrigued by this whole situation and wanted to see how Railway stacks up when running the same benchmarks. These were the results we got (lower is better):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjc4doz0b3pog9xt9266v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjc4doz0b3pog9xt9266v.png" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;Railway - (Bun)&lt;/th&gt;
&lt;th&gt;Railway - (Node)&lt;/th&gt;
&lt;th&gt;Cloudflare&lt;/th&gt;
&lt;th&gt;Vercel&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;td&gt;1826ms&lt;/td&gt;
&lt;td&gt;2397ms&lt;/td&gt;
&lt;td&gt;1274ms&lt;/td&gt;
&lt;td&gt;1089ms&lt;/td&gt;
&lt;td&gt;Vercel wins — 1.17× faster than Cloudflare, 1.68× faster than Railway (Bun), 2.20× faster than Railway (Node)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React SSR&lt;/td&gt;
&lt;td&gt;177ms&lt;/td&gt;
&lt;td&gt;244ms&lt;/td&gt;
&lt;td&gt;163ms&lt;/td&gt;
&lt;td&gt;168ms&lt;/td&gt;
&lt;td&gt;Cloudflare wins — 1.03× faster than Vercel, 1.09× faster than Railway (Bun), 1.50× faster than Railway (Node)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sveltekit&lt;/td&gt;
&lt;td&gt;102ms&lt;/td&gt;
&lt;td&gt;134ms&lt;/td&gt;
&lt;td&gt;314ms&lt;/td&gt;
&lt;td&gt;367ms&lt;/td&gt;
&lt;td&gt;Railway (Bun) wins — 1.31× faster than Railway (Node), 3.08× faster than Cloudflare, 3.60× faster than Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Math&lt;/td&gt;
&lt;td&gt;1487ms&lt;/td&gt;
&lt;td&gt;1151ms&lt;/td&gt;
&lt;td&gt;550ms&lt;/td&gt;
&lt;td&gt;685ms&lt;/td&gt;
&lt;td&gt;Cloudflare wins — 1.25× faster than Vercel, 2.09× faster than Railway (Node), 2.70× faster than Railway (Bun)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vanilla JS&lt;/td&gt;
&lt;td&gt;534ms&lt;/td&gt;
&lt;td&gt;479ms&lt;/td&gt;
&lt;td&gt;1809ms&lt;/td&gt;
&lt;td&gt;1865ms&lt;/td&gt;
&lt;td&gt;Railway (Node) wins — 1.11× faster than Railway (Bun), 3.78× faster than Cloudflare, 3.89× faster than Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Railway performed strongly in two of the five benchmarks — performing the best in the SvelteKit and Vanilla JS tests, where it ran roughly 3–4× faster than both Cloudflare and Vercel. The React SSR results were close, while the Next.js and Math benchmarks highlight areas for further optimization, which we’ll cover in this post.&lt;/p&gt;

&lt;p&gt;We’ll start by looking at what the benchmarks actually test, how we added Railway into the mix, and how we ran our setup. After that, we’ll break down each platform’s deployment model and scaling approach before diving into the benchmark results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark overview
&lt;/h2&gt;

&lt;p&gt;The benchmarks test SSR performance by dynamically rendering a data-heavy page that performs thousands of mathematical calculations — primes, Fibonacci numbers, factorials, and nested data structures — creating a computation-intensive workload that pushes both CPU and rendering limits.&lt;/p&gt;

&lt;p&gt;Each framework implements the same workload, but in its own way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js: React component using JSX&lt;/li&gt;
&lt;li&gt;React SSR: Identical logic as the Next.js benchmark but implemented via &lt;code&gt;React.createElement()&lt;/code&gt; without JSX&lt;/li&gt;
&lt;li&gt;SvelteKit: Logic runs in &lt;code&gt;+page.server.ts&lt;/code&gt;, rendered using Svelte templates&lt;/li&gt;
&lt;li&gt;Vanilla JS: HTML built manually via string concatenation. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Vanilla JS implementation also includes two heavier variants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/slower-bench&lt;/code&gt; — ~3× heavier workload (150 sections vs. 50, 60 items each vs. 20, and a prime limit of 500,000 instead of 100,000)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/realistic-math-bench&lt;/code&gt; — focuses on integer arithmetic, array sorting, string hashing, and prime counting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benchmarking process is orchestrated by a script that executes 400 HTTP requests per endpoint with 20 concurrent connections, measuring full round-trip response times — from request start to fully rendered response.&lt;/p&gt;

&lt;p&gt;For each test, it records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average latency&lt;/li&gt;
&lt;li&gt;Fastest response&lt;/li&gt;
&lt;li&gt;Slowest response&lt;/li&gt;
&lt;li&gt;Variability (difference between fastest and slowest results)&lt;/li&gt;
&lt;li&gt;Success rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once all measurements are collected, the script ranks the platforms for each workload and generates comparison reports summarizing relative speed and consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Railway to the mix
&lt;/h2&gt;

&lt;p&gt;We &lt;a href="https://github.com/m-abdelwahab/cf-vs-vercel-vs-railway-bench" rel="noopener noreferrer"&gt;forked Theo’s benchmark repository&lt;/a&gt; and created dedicated Railway Edition benchmarks: one set running on Node.js and another deployed on Bun (&lt;code&gt;railway-edition-bun&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Code changes were minimal — just defining entry points and adding start commands. We also updated the benchmarking script to include Railway and Railway (Bun) in the result.&lt;/p&gt;

&lt;p&gt;Finally, each variant was deployed as its own service and scaled to 10 replicas (more on that later).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyz29oe92eeru37iz562a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyz29oe92eeru37iz562a.png" alt="Railway edition benchmarks on Railway" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway edition benchmarks on Railway&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the benchmark
&lt;/h2&gt;

&lt;p&gt;To keep results easy to reproduce, we ran the test client directly from an &lt;code&gt;m7i-flex.large&lt;/code&gt; EC2 instance in AWS’s &lt;code&gt;us-west-1&lt;/code&gt; region (California)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqzb5ya3lt9txq0o4tza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqzb5ya3lt9txq0o4tza.png" alt="Running benchmarks from an EC2 instance from AWS’ CloudShell" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Running benchmarks from an EC2 instance from AWS’ CloudShell&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each benchmark on Vercel was deployed to the &lt;code&gt;us-west-1&lt;/code&gt; region as its own project using default compute settings — 1 vCPU and 2 GB memory — since all benchmarks were single-threaded.&lt;/p&gt;

&lt;p&gt;Each benchmark on Vercel was deployed as an independent project in the &lt;code&gt;us-west-1&lt;/code&gt; region, using the default compute configuration of 1 vCPU and 2 GB of memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkng0mpdqyxz3za0m0wz2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkng0mpdqyxz3za0m0wz2.png" alt="Region configuration for Vercel functions" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Region configuration for Vercel functions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For testing Cloudflare, we used Theo’s test account: &lt;code&gt;theo-s-cool-new-test-account-10-3.workers.dev&lt;/code&gt;. Cloudflare Workers automatically run code in the region closest to the request and are well-connected to all AWS regions, so no region configuration was required.&lt;/p&gt;

&lt;p&gt;Finally, all Railway services were deployed in our US West region. Our infrastructure runs on globally distributed hardware that is fully owned and managed by Railway across data centers worldwide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faib1dhnqf2fyj9m0e78v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faib1dhnqf2fyj9m0e78v.png" alt="Region configuration for Railway services" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Region configuration for Railway services&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This setup minimizes network latency as much as possible. That said, because Vercel’s infrastructure also runs on AWS — and our EC2 test client was in the same region as its functions — this gives Vercel a slight edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling and deployment models: Serverless vs. long-running servers
&lt;/h2&gt;

&lt;p&gt;Before diving deeper into the benchmark results, it’s important to understand how each platform deployment and scaling models.&lt;/p&gt;

&lt;h3&gt;
  
  
  The serverless model
&lt;/h3&gt;

&lt;p&gt;Cloudflare and Vercel both follow a serverless model — you write your code, deploy it, and the infrastructure is abstracted away from you. Under the hood, though, the two platforms work quite differently.&lt;/p&gt;

&lt;p&gt;When you deploy on Cloudflare, your code runs on their &lt;a href="https://www.cloudflare.com/network" rel="noopener noreferrer"&gt;global network&lt;/a&gt;, which spans thousands of machines across hundreds of locations. Each machine runs &lt;code&gt;workerd&lt;/code&gt;, a custom runtime built on the &lt;a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-chrome-v8/" rel="noopener noreferrer"&gt;V8 engine&lt;/a&gt;. One of Cloudflare’s value propositions is you don’t need to choose a region — the platform routes each request to the nearest data center, running your code as close as possible to the user for optimal latency.&lt;/p&gt;

&lt;p&gt;As your app scales and handles more requests, Cloudflare automatically distributes the workload across its network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvvfrw2jrmjfkdlzsj84.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvvfrw2jrmjfkdlzsj84.png" alt="Cloudflare locations" width="800" height="420"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Cloudflare locations&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vercel, on the other hand, runs on AWS and takes a slightly different approach to deployment and scaling. Server-side logic is deployed as AWS Lambda functions under the hood. Vercel automatically creates new function instances to handle incoming requests, allowing multiple concurrent executions within each instance. As traffic increases, it scales by spinning up additional instances to meet demand. (see &lt;a href="https://vercel.com/docs/fluid-compute" rel="noopener noreferrer"&gt;&lt;strong&gt;Fluid Compute&lt;/strong&gt;&lt;/a&gt; for details).&lt;/p&gt;

&lt;p&gt;Over time, idle functions automatically scale down to zero, reducing unnecessary compute usage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zszao3caqwpvugxta0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zszao3caqwpvugxta0m.png" alt="Source: vercel.com/docs/fundamentals/what-is-compute" width="800" height="220"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Source: vercel.com/docs/fundamentals/what-is-compute&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both platforms charge only for active CPU time — you’re not billed for I/O waits, network latency, or external API calls.&lt;/p&gt;

&lt;p&gt;While the serverless model makes deployment simple by abstracting away infrastructure, that simplicity comes with trade-offs. There are limits on memory, execution time, and function size. You can’t run long-running workloads or those requiring a persistent connection, and you give up control over the execution environment, relying entirely on the platform’s predefined runtimes.&lt;/p&gt;

&lt;p&gt;Serverless emerged as a response to the friction of managing servers. You need to choose instance sizes, CPU, and memory before knowing what your app actually needs. This guesswork often leads to one of two outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under-provisioning: not enough compute, causing slow or failed requests.&lt;/li&gt;
&lt;li&gt;Over-provisioning: too many idle resources, which you pay for regardless of your usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Railway, we believe the answer isn’t to hide the server — it’s to make the server experience better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better servers on Railway
&lt;/h3&gt;

&lt;p&gt;Services you deploy on Railway run on long-running servers. You can import your code and we’ll build and deploy it for you, or you can deploy directly from an &lt;a href="https://opencontainers.org/" rel="noopener noreferrer"&gt;OCI-compliant&lt;/a&gt; image registry (e.g DockerHub, GitHub image registry, etc.). You have full control over the runtime and language your service uses.&lt;/p&gt;



&lt;p&gt;&lt;em&gt;Creating a new project on Railway&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Deployed services automatically scale up or down based on workload — no need to pick instance sizes or tune scaling thresholds.&lt;/p&gt;

&lt;p&gt;You can also scale horizontally by adding replicas. Railway automatically balances public traffic across replicas within each region.&lt;/p&gt;

&lt;p&gt;Additionally, replicas can be deployed in multiple regions. Railway routes incoming traffic to the nearest region and evenly distributes requests among the replicas there — all without any manual scaling configuration.&lt;/p&gt;

&lt;p&gt;Each replica runs with the full compute limits of your plan. For example, on the Pro plan, services can use up to 32 vCPUs and 32 GB RAM. Deploying three replicas gives your service a combined capacity of 96 vCPUs and 96 GB RAM.&lt;/p&gt;



&lt;p&gt;&lt;em&gt;Deploy replicas both within the same region and across different regions to scale&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pricing follows the same principle as Cloudflare and Vercel: you pay only for active CPU and memory usage, not idle time. When running multiple replicas, you’re billed only for the compute time actively consumed by each one.&lt;/p&gt;

&lt;p&gt;This means you can get a similar experience to serverless without the constraints around memory, file sizes, or execution limits.&lt;/p&gt;

&lt;p&gt;Now that you have a an understanding of the deployment and scaling models of each platform, let’s do a deeper dive into the benchmark results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of the benchmark results
&lt;/h2&gt;

&lt;p&gt;Previously, Railway was running on Google Cloud, and &lt;a href="https://dev.to/sarah-railway/railway-v3-faster-and-cheaper-1ii1-temp-slug-8087263"&gt;we recently migrated to our own baremetal servers earlier this year&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ve built our entire platform from the ground up, giving us full control over every layer of the stack — hardware, networking, software, runtime, and orchestration.&lt;/p&gt;

&lt;p&gt;We’re now deploying the next generation of our hardware, unlocking even better performance across our infrastructure. You can learn more about our data center buildout in our blog posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.railway.com/p/data-center-build-part-one" rel="noopener noreferrer"&gt;So You Want to Build Your Own Data Center&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.railway.com/p/data-center-build-part-two" rel="noopener noreferrer"&gt;Zero-Touch Bare Metal at Scale&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bun vs. Node
&lt;/h3&gt;

&lt;p&gt;Railway is language, framework, and runtime agnostic — which gives us the flexibility to experiment with different configurations and identify the most optimal setup.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://bun.com/" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; as the runtime gave us better numbers overall and it made a significant difference for Next.js. However, for the Maths benchmark, Node outperformed Bun. We’ll collaborate with the Bun team to dig deeper on why this is the case.&lt;/p&gt;

&lt;p&gt;If you want to try out Bun on Railway, all you need to do is use it as your package manager and use it in your project’s &lt;code&gt;start&lt;/code&gt; script. &lt;a href="https://railpack.com/" rel="noopener noreferrer"&gt;Railpack&lt;/a&gt;, our zero-config application builder, supports Bun out-of-the-box.&lt;/p&gt;

&lt;h3&gt;
  
  
  More replicas, better performance
&lt;/h3&gt;

&lt;p&gt;Unsurprisingly, deploying more replicas on Railway led to better overall performance across all benchmarks. It even led to Railway being the fastest in three out of five benchmarks. This makes sense since the workload is now split across more instances. Here are the results of the benchmark when running at 10 replicas vs 20:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feuykc4xiksp34n9hty40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feuykc4xiksp34n9hty40.png" alt="10 replicas for each Railway service" width="800" height="552"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;10 replicas for each Railway service&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhscscmjgn0zb97nq6qic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhscscmjgn0zb97nq6qic.png" alt="20 replicas for each Railway service" width="800" height="552"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;20 replicas for each Railway service&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Of course, there are diminishing returns. It’s better to understand the workload you’re running and to optimize something else instead of throwing more compute at the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing Next.js
&lt;/h3&gt;

&lt;p&gt;Unfortunately, Next.js doesn’t fully utilize the compute resources available when deployed on Railway. The framework is currently limited to using a single CPU core — a&lt;a href="https://x.com/cramforce/status/1975656443954274780" rel="noopener noreferrer"&gt;detail confirmed by Vercel’s CTO&lt;/a&gt; and verified in our own testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fln1xuxj341rub9i7qv2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fln1xuxj341rub9i7qv2i.png" alt="https://x.com/cramforce/status/1975656443954274780" width="800" height="371"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://x.com/cramforce/status/1975656443954274780" rel="noopener noreferrer"&gt;https://x.com/cramforce/status/1975656443954274780&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As a result, the only effective way to scale a Next.js app is through horizontal scaling, by adding more replicas rather than relying on additional CPU cores.&lt;/p&gt;

&lt;p&gt;Additionally, while Next.js is one of the most popular frameworks for building web applications, it’s been historically difficult to deploy it effectively outside of Vercel. This led to the community to create &lt;a href="https://opennext.js.org/" rel="noopener noreferrer"&gt;OpenNext&lt;/a&gt;, a shared effort to make the open-source framework work reliably everywhere. Both Cloudflare and Netlify now maintain their own adapters built on top of this work.&lt;/p&gt;

&lt;p&gt;Fortunately, the Next.js team is now formalizing this with official Deployment Adapters — a standardized API that makes it easier for platforms to integrate Next.js without hacks or reverse engineering. Vercel will use the same adapter API as everyone else, ensuring true parity across environments. You can &lt;a href="https://github.com/vercel/next.js/discussions/77740" rel="noopener noreferrer"&gt;follow the proposal in this RFC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With these updates, deploying Next.js on Railway should become much smoother and more performant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vanilla JS and Maths benchmarks
&lt;/h3&gt;

&lt;p&gt;The Cloudflare team found something interesting: Node.js isn’t currently using the fastest available implementation for certain trigonometric functions. Since Node supports a wide range of systems and architectures, it’s compiled with more conservative defaults. V8, the JavaScript engine it runs on, includes a compile-time flag that enables a faster path for these math operations. In Cloudflare Workers, that flag happens to be on by default — in Node.js, it’s not. The Cloudflare team has opened a pull request to enable it, which should make math-heavy workloads faster for everyone once it lands. You can &lt;a href="https://blog.cloudflare.com/unpacking-cloudflare-workers-cpu-performance-benchmarks/#node-jss-trigonometry-problem" rel="noopener noreferrer"&gt;learn more in their blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we found that container runtimes introduce a noticeable performance penalty — roughly 40% compared to running natively. This lines up with the weaker math benchmark results we observed earlier.&lt;/p&gt;

&lt;p&gt;We’re already working on a new VM runtime designed to provide stronger isolation and significantly better performance. As it matures, we expect to close much of this gap — and we’ll share more about it in a future post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;We believe that having open benchmarks are valuable for everyone. They help platform builders like us identify where we can improve, and they give developers a clearer sense of what to expect when deploying their workloads.&lt;/p&gt;

&lt;p&gt;That said, benchmarks are tricky. Designing tests that truly reflect real-world performance is harder than it looks. The numbers you see often represent only a small slice of the full picture — in this case, CPU performance. In practice, application performance is influenced by a lot more: database speed, network latency, API calls, and even end-user connection quality.&lt;/p&gt;

&lt;p&gt;We’re investing in creating more open benchmarks to better capture these real-world conditions. And if you’ve run your own tests where Railway performs differently, we’d love to see them — we’ll dig in, learn from them, and keep improving.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, you can tag us on &lt;a href="https://twitter.com/railway" rel="noopener noreferrer"&gt;X&lt;/a&gt;, reach out in &lt;a href="https://station.railway.com/" rel="noopener noreferrer"&gt;Help Station&lt;/a&gt; or &lt;a href="https://discord.com/invite/railway" rel="noopener noreferrer"&gt;ping us on Discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Top five Heroku alternatives</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Wed, 15 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/top-five-heroku-alternatives-1agc</link>
      <guid>https://dev.to/sarah-railway/top-five-heroku-alternatives-1agc</guid>
      <description>&lt;p&gt;Author: Mahmoud Abdelwahab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mrfux8ckknw78bbz4f8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mrfux8ckknw78bbz4f8.png" alt="Top five Heroku alternatives" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Heroku pioneered the Platform-as-a-Service (PaaS) model, making it simple for developers to deploy and manage applications without worrying about infrastructure. However, as applications grow and requirements evolve, many teams find themselves seeking alternatives that offer better pricing models, more flexibility, or modern features.&lt;/p&gt;

&lt;p&gt;This guide explores five compelling alternatives to Heroku, each offering distinct approaches to deployment, resource management, scaling, and pricing. Whether you're looking for usage-based pricing, better performance, or more control over your infrastructure, this comparison will help you find the right platform for your needs.&lt;/p&gt;

&lt;p&gt;The platforms covered in this guide are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://railway.com/" rel="noopener noreferrer"&gt;Railway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://render.com/" rel="noopener noreferrer"&gt;Render&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;Fly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/products/app-platform" rel="noopener noreferrer"&gt;DigitalOcean App Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Look for Heroku Alternatives?
&lt;/h2&gt;

&lt;p&gt;Heroku’s pricing has become prohibitively expensive for many production workloads, and its underlying architecture imposes several limitations compared to more modern platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No persistent storage: Services deployed to Heroku do not offer persistent data storage via volumes. Any data written to the local filesystem is ephemeral and will be lost upon redeployment&lt;/li&gt;
&lt;li&gt;No native multi-region support: Requires separate instances and external load balancers to achieve global distribution&lt;/li&gt;
&lt;li&gt;Limited organizational structure: Each app is deployed independently with no top-level "project" object that groups related apps&lt;/li&gt;
&lt;li&gt;No shared environment variables: Each deployed app has its own isolated set of variables, making it harder to manage secrets across multiple services&lt;/li&gt;
&lt;li&gt;No built-in health checks for zero downtime deployments: Zero-downtime deployments on Heroku typically rely on enabling Preboot so new dynos start serving traffic before old ones stop, using a &lt;code&gt;release&lt;/code&gt; phase for backward-compatible migrations, and handling graceful shutdowns via SIGTERM. While Heroku offers metrics and logging, it lacks built-in HTTP health checks — you’ll need to add your own health-check endpoint and external monitoring to catch deployment issues.&lt;/li&gt;
&lt;li&gt;Private networking is a paid add-on: Available only on enterprise plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, since Heroku runs on AWS, additional costs are passed down for resources like bandwidth, memory, CPU, and storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Heroku Alternatives Comparison
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Legend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Full support&lt;/li&gt;
&lt;li&gt;⚠️ Partial support or requires workarounds&lt;/li&gt;
&lt;li&gt;❌ Not supported&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Railway&lt;/th&gt;
&lt;th&gt;Render&lt;/th&gt;
&lt;th&gt;Fly&lt;/th&gt;
&lt;th&gt;Vercel&lt;/th&gt;
&lt;th&gt;DigitalOcean&lt;/th&gt;
&lt;th&gt;Heroku&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DEPLOYMENT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment Model&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;td&gt;Lightweight VMs&lt;/td&gt;
&lt;td&gt;Serverless functions&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Support&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source Code Deploy&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Service Projects&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No (one-to-one)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INFRASTRUCTURE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs On&lt;/td&gt;
&lt;td&gt;Own hardware&lt;/td&gt;
&lt;td&gt;AWS/GCP&lt;/td&gt;
&lt;td&gt;Own hardware&lt;/td&gt;
&lt;td&gt;AWS (serverless)&lt;/td&gt;
&lt;td&gt;Own hardware&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max Memory&lt;/td&gt;
&lt;td&gt;Plan-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;4GB&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution Limits&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;13.3 min max&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold Starts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (however several optimizations exist to reduce them)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent Storage via volumes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DATABASES &amp;amp; STORAGE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database Support&lt;/td&gt;
&lt;td&gt;✅ One-click deploy any open-source database&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;Via marketplace&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ Native (via add-ons)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCALING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vertical AutoScaling&lt;/td&gt;
&lt;td&gt;✅ Automatic&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;td&gt;✅ Automatic&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal Scaling&lt;/td&gt;
&lt;td&gt;✅ Yes (By deploying replicas)&lt;/td&gt;
&lt;td&gt;✅ Yes (Configure min and max number of concurrent instances)&lt;/td&gt;
&lt;td&gt;✅ Yes (By deploying fly-autoscaler)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes (Configure min and max number of concurrent instances)&lt;/td&gt;
&lt;td&gt;✅ Yes (Configure min and max number of concurrent instances)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Region Support&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;❌ No (requires manual setup)&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No (requires manual setup)&lt;/td&gt;
&lt;td&gt;❌ No (requires manual setup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PRICING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing Model&lt;/td&gt;
&lt;td&gt;Usage-based (Active compute time + resources used)&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Machine state-based&lt;/td&gt;
&lt;td&gt;Usage-based (Active compute time + resources used)&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing Factors&lt;/td&gt;
&lt;td&gt;Active compute time × size&lt;/td&gt;
&lt;td&gt;Fixed monthly per instance. When scaling horizontally it's instance size x total running time&lt;/td&gt;
&lt;td&gt;Running time + CPU type&lt;/td&gt;
&lt;td&gt;CPU time + memory + invocations&lt;/td&gt;
&lt;td&gt;Fixed monthly per instance&lt;/td&gt;
&lt;td&gt;Fixed monthly per instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scales to Zero&lt;/td&gt;
&lt;td&gt;✅ Supported via app sleeping&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Supported via autostop&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD &amp;amp; ENVIRONMENTS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Integration&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PR Preview Environments&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Not supported out of the box. Requires setting up a CI/CD pipeline&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment Support&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;⚠️ Separate orgs&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;⚠️ Separate projects&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instant Rollbacks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-Deploy Commands&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Manual when setting up a deployment pipeline&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OBSERVABILITY&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built-in Monitoring&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes (Prometheus)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated Logs&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DEVELOPER TOOLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure as Code&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI Support&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH Access&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NETWORKING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Domains&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed TLS&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Networking&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Paid add-on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health Checks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ADDITIONAL FEATURES&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native Support for Cron Jobs&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared Variables&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;td&gt;⚠️ Within project&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Railway
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Filoqo7oa4vlskiyuf3v9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Filoqo7oa4vlskiyuf3v9.png" alt="railway.com" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;railway.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At a high level, both Railway and Heroku can be used to deploy your app. Both platforms share many similarities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can deploy your app from a Docker image or by importing your app’s source code from GitHub.&lt;/li&gt;
&lt;li&gt;Services are deployed to a long-running server.&lt;/li&gt;
&lt;li&gt;Connect your GitHub repository for automatic builds and deployments on code pushes.&lt;/li&gt;
&lt;li&gt;Create isolated preview environments for every pull request for every app&lt;/li&gt;
&lt;li&gt;Support for instant rollbacks.&lt;/li&gt;
&lt;li&gt;Integrated metrics and logs.&lt;/li&gt;
&lt;li&gt;Command-line-interface (CLI) to manage resources.&lt;/li&gt;
&lt;li&gt;Integrated build pipeline with the ability to define pre-deploy command.&lt;/li&gt;
&lt;li&gt;Custom domains with fully managed TLS.&lt;/li&gt;
&lt;li&gt;Run arbitrary commands against deployed services (SSH).&lt;/li&gt;
&lt;li&gt;Webhooks: Build integrations with external services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, there are some differences between the platforms that might make Railway a better fit for you.&lt;/p&gt;
&lt;h3&gt;
  
  
  Automatic Scaling and Resource Management
&lt;/h3&gt;

&lt;p&gt;Unlike Heroku's manual scaling approach, Railway automatically scales compute resources based on workload without manual threshold configuration. Each plan has defined CPU and memory limits, and the platform adjusts resources dynamically.&lt;/p&gt;

&lt;p&gt;For horizontal scaling, you can deploy multiple replicas of your service. Railway automatically distributes public traffic randomly across replicas within each region. Each replica runs with the full resource limits of your plan.&lt;/p&gt;

&lt;p&gt;Replicas can be placed in different geographical locations, with automatic routing to the nearest region. The platform then randomly distributes requests among available replicas within that region, a capability Heroku lacks without external load balancers.&lt;/p&gt;

&lt;p&gt;Finally, if you want to save on compute resources, you can enable &lt;a href="https://docs.railway.com/reference/app-sleeping" rel="noopener noreferrer"&gt;app sleeping&lt;/a&gt;, which suspends a running service after 10 minutes of inactivity. Services become active again on incoming requests.&lt;/p&gt;
&lt;h3&gt;
  
  
  Usage-Based Pricing Model
&lt;/h3&gt;

&lt;p&gt;Railway's pricing model is fundamentally different from Heroku's instance-based approach. Instead of paying a fixed monthly price for instances that may be under or over-utilized, Railway uses usage-based pricing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active compute time x compute size (memory and CPU)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk86d062ygt8cuo61u6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk86d062ygt8cuo61u6n.png" alt="Railway autoscaling" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway autoscaling&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This means you only pay for what you actually use. If you spin up multiple replicas for a given service, you'll only be charged for the active compute time for each replica.&lt;/p&gt;

&lt;p&gt;Railway's underlying infrastructure runs on hardware that's owned and operated in data centers across the globe. By controlling the hardware, software, and networking stack end to end, the platform delivers best-in-class performance, reliability, and powerful features, all while keeping costs in check.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboard and deployment experience
&lt;/h3&gt;

&lt;p&gt;Railway's dashboard offers a real-time collaborative canvas where you can view all of your running services and databases at a glance. Projects contain multiple services and databases, and you can group different infrastructure components and visualize how they're related to one another.&lt;/p&gt;

&lt;p&gt;You can also spin up isolated environments in one click or by setting up &lt;a href="https://docs.railway.com/guides/environments#enable-pr-environments" rel="noopener noreferrer"&gt;automatic PR environments&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observability
&lt;/h3&gt;

&lt;p&gt;Railway includes integrated metrics and logs to help you track application performance, giving you visibility into your deployments without needing external tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.railway.com%2F_next%2Fimage%3Furl%3Dhttps%253A%252F%252Fres.cloudinary.com%252Frailway%252Fimage%252Fupload%252Fv1717179720%252FWholescreenshot_vc5l5e.png%26w%3D3840%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.railway.com%2F_next%2Fimage%3Furl%3Dhttps%253A%252F%252Fres.cloudinary.com%252Frailway%252Fimage%252Fupload%252Fv1717179720%252FWholescreenshot_vc5l5e.png%26w%3D3840%26q%3D80" alt="Observability Dashboard" width="2223" height="1298"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Observability Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Support
&lt;/h3&gt;

&lt;p&gt;Railway has first-class support for databases with one-click deployment of any open-source database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relational: Postgres, MySQL&lt;/li&gt;
&lt;li&gt;Analytical: ClickHouse, Timescale&lt;/li&gt;
&lt;li&gt;Key-value: Redis, Dragonfly&lt;/li&gt;
&lt;li&gt;Vector: Chroma, Weaviate&lt;/li&gt;
&lt;li&gt;Document: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out all of the &lt;a href="https://railway.com/deploy?category=Storage" rel="noopener noreferrer"&gt;different storage solutions&lt;/a&gt; you can deploy.&lt;/p&gt;

&lt;p&gt;This is a significant improvement over Heroku's add-on marketplace, where managing services requires switching between different dashboards and providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Features
&lt;/h3&gt;

&lt;p&gt;Railway includes several features that improve on Heroku's offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persistent storage via volumes: You can attach a volume to deployed services. Any data you write to the volume will persist across deployments&lt;/li&gt;
&lt;li&gt;Shared environment variables: Unlike Heroku's isolated per-app variables, Railway allows you to share variables across services&lt;/li&gt;
&lt;li&gt;Native cron job support: Schedule recurring tasks without external add-ons. Heroku's native scheduler only supports three recurring frequencies: once every 10 minutes, once an hour, and once a day.&lt;/li&gt;
&lt;li&gt;Infrastructure as Code: Programmatic control over your resources through IaC definitions&lt;/li&gt;
&lt;li&gt;Healthchecks: define a healthcheck path to guarrentee zero downtime deployments&lt;/li&gt;
&lt;li&gt;Public and private networking: Built-in support without additional costs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Render
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwiezf5ahmbzeugm7m0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwiezf5ahmbzeugm7m0g.png" alt="Render.com" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Render.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Render is another modern alternative to Heroku that addresses several of its limitations. Like Railway, Render supports multi-service architectures where you can deploy different services under one project (e.g., a frontend, APIs, databases).&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure and Scaling Model
&lt;/h3&gt;

&lt;p&gt;Render follows a traditional, instance-based model similar to Heroku. Each instance has a set of allocated compute resources (memory and CPU).&lt;/p&gt;

&lt;p&gt;When your deployed service needs more resources, you can scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vertically&lt;/strong&gt; : Manually upgrade to a larger instance size to unlock more compute resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontally&lt;/strong&gt; : Distribute your workload across multiple running instances by either:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this approach covers scaling within a single region, Render does not offer native multi-region support. To achieve a globally distributed deployment, you must provision separate instances in different regions and set up an external load balancer to route traffic between them, the same limitation Heroku has.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Model
&lt;/h3&gt;

&lt;p&gt;Render follows traditional instance-based pricing similar to Heroku. You select the amount of compute resources you need from a list of instance sizes, each with a fixed monthly price.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xcif0eoh5a49svzrkvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xcif0eoh5a49svzrkvi.png" alt="Render Instances" width="800" height="356"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Render Instances&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Similar to Heroku, Render runs on AWS and GCP, so the unit economics need to be high to offset the cost of the underlying infrastructure. These extra costs are passed down to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlocking additional features (e.g., horizontal autoscaling and environments are only available on paid plans)&lt;/li&gt;
&lt;li&gt;Paying extra for resources (e.g., bandwidth, memory, CPU, and storage)&lt;/li&gt;
&lt;li&gt;Paying for seats where each team member you invite adds a fixed monthly fee regardless of usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Additional Features
&lt;/h3&gt;

&lt;p&gt;Render includes several features that improve on Heroku's offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persistent storage via volumes: You can attach a volume to deployed services. Any data you write to the volume will persist across deployments&lt;/li&gt;
&lt;li&gt;Shared environment variables: Unlike Heroku's isolated per-app variables, Render applications can use secret files and shared environment groups&lt;/li&gt;
&lt;li&gt;Native cron job support: Schedule recurring tasks without external add-ons. Heroku's native scheduler only supports three recurring frequencies: once every 10 minutes, once an hour, and once a day.&lt;/li&gt;
&lt;li&gt;Global CDN: Render offers native support for static sites, a feature missing from Heroku&lt;/li&gt;
&lt;li&gt;Healthchecks: define a healthcheck path to guarrentee zero downtime deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbv400osks80wue782zej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbv400osks80wue782zej.png" alt="Fly" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fly&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://fly.io/" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt; offers a different approach to deploying applications compared to Heroku. While both platforms support long-running applications, Fly uses lightweight Virtual Machines (VMs) called Fly Machines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure and Deployment Model
&lt;/h3&gt;

&lt;p&gt;When you deploy your app to Fly, your code runs on Fly Machines. Each machine needs a defined amount of CPU and memory. You can either choose from preset sizes or configure them separately, depending on your app's needs.&lt;/p&gt;

&lt;p&gt;Machines come with two CPU types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared CPUs&lt;/strong&gt; : 6% guaranteed CPU time with bursting capability. Subject to throttling under heavy usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance CPUs&lt;/strong&gt; : Dedicated CPU access without throttling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fly machines run on hardware that's owned and operated in data centers across the globe, with native support for multi-region deployments—something Heroku doesn't offer without additional setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Your Application
&lt;/h3&gt;

&lt;p&gt;When scaling your app on Fly, you have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale a machine's CPU and RAM&lt;/strong&gt; : Manually pick a larger instance using the Fly CLI or API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increase the number of running machines&lt;/strong&gt; :&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jvwqdbzij0sexm9u8ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jvwqdbzij0sexm9u8ik.png" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scaling on Fly&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Model
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmma47sttmjkfuvr3sqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmma47sttmjkfuvr3sqr.png" alt="Fly Pricing" width="800" height="1723"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fly Pricing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fly charges for compute based on two primary factors: machine state and CPU type (shared vs. performance).&lt;/p&gt;

&lt;p&gt;Machine state determines the base charge structure. Started machines incur full compute charges, while stopped machines are only charged for root file system (rootfs) storage. The rootfs size depends on your OCI image plus &lt;a href="https://containerd.io/" rel="noopener noreferrer"&gt;containerd&lt;/a&gt; optimizations applied to the underlying file system.&lt;/p&gt;

&lt;p&gt;Reserved compute blocks require annual upfront payment with monthly non-rolling credits.&lt;/p&gt;

&lt;p&gt;Fly Machines charge based on running time regardless of utilization. Stopped machines only incur storage charges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Workflow and CI/CD
&lt;/h3&gt;

&lt;p&gt;Fly provides a CLI-first experience through &lt;code&gt;flyctl&lt;/code&gt;, allowing you to create and deploy apps, manage Machines and volumes, configure networking, and perform other infrastructure tasks directly from the command line.&lt;/p&gt;

&lt;p&gt;However, Fly lacks built-in CI/CD capabilities that Heroku offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No native preview environments&lt;/strong&gt; : You can't create isolated preview environments for every pull request out-of-the-box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No instant rollbacks&lt;/strong&gt; : Unlike Heroku's built-in rollback feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To access these features, you'll need to integrate third-party CI/CD tools like &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Similarly, Fly doesn't include native environment support for development, staging, and production workflows. To achieve proper environment isolation, you must create separate organizations for each environment and link them to a parent organization for centralized billing management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Metrics
&lt;/h3&gt;

&lt;p&gt;For monitoring, Fly automatically collects metrics from every application using a fully-managed Prometheus service based on VictoriaMetrics. The system scrapes metrics from all application instances and provides data on HTTP responses, TCP connections, memory usage, CPU performance, disk I/O, network traffic, and filesystem utilization.&lt;/p&gt;

&lt;p&gt;The Fly dashboard includes a basic Metrics tab displaying this automatically collected data. Beyond the basic dashboard, Fly offers a managed Grafana instance at &lt;a href="http://fly-metrics.net/" rel="noopener noreferrer"&gt;fly-metrics.net&lt;/a&gt; with detailed dashboards and query capabilities using MetricsQL as the querying language. You can also connect external tools through the Prometheus API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6xc32wj38fmjn6wkfp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6xc32wj38fmjn6wkfp0.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://fly-metrics.net/" rel="noopener noreferrer"&gt;fly-metrics.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alerting and custom dashboards require multiple tools and query languages. Additionally, Fly doesn't support webhooks (which Heroku does), making it more difficult to build integrations with external services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Features
&lt;/h3&gt;

&lt;p&gt;Fly includes several features that improve on Heroku's offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persistent storage via volumes: You can attach a volume to deployed services. Any data you write to the volume will persist across deployments&lt;/li&gt;
&lt;li&gt;Healthchecks: define a healthcheck path to guarrentee zero downtime deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vercel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyn8jvkvjall7cxw8ktm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyn8jvkvjall7cxw8ktm.png" alt="vercel.com" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;vercel.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vercel takes a fundamentally different approach from Heroku. While Heroku deploys applications to long-running servers, Vercel uses a serverless deployment model ideal for web applications and static sites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure and Deployment Model
&lt;/h3&gt;

&lt;p&gt;Vercel has developed a proprietary deployment model where infrastructure components are derived from the application code through a concept called &lt;a href="https://vercel.com/blog/framework-defined-infrastructure" rel="noopener noreferrer"&gt;Framework-defined infrastructure&lt;/a&gt;. At build time, application code is parsed and translated into the necessary infrastructure components. Server-side code is then deployed as serverless functions.&lt;/p&gt;

&lt;p&gt;Note that Vercel does not support the deployment of Docker images or containers—a significant difference from Heroku.&lt;/p&gt;

&lt;p&gt;To handle scaling, Vercel creates a new function instance for each incoming request with support for concurrent execution within the same instance through their &lt;a href="https://vercel.com/docs/fluid-compute" rel="noopener noreferrer"&gt;Fluid compute&lt;/a&gt; system. Over time, functions scale down to zero to save on compute resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb88whciwyl55d8msmpfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb88whciwyl55d8msmpfj.png" alt="Vercel Fluid Compute" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel Fluid Compute&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Model
&lt;/h3&gt;

&lt;p&gt;Vercel uses usage-based pricing similar to Railway, but with different billing factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active CPU&lt;/strong&gt; : Time your code actively runs in milliseconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned memory&lt;/strong&gt; : Memory held by the function instance for the full lifetime of the instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invocations&lt;/strong&gt; : Number of function requests, where you're billed per request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each pricing plan includes a certain allocation of these metrics, making it possible to pay for what you use. However, since Vercel runs on AWS, the unit economics need to be high to offset the cost of the underlying infrastructure. Those extra costs are passed down to you, so you end up paying extra for resources such as bandwidth, memory, CPU, and storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Management and Developer Experience
&lt;/h3&gt;

&lt;p&gt;In Vercel, a project maps to a deployed application. If you would like to deploy multiple apps, you'll do it by creating multiple projects. This one-to-one mapping can complicate architectures with multiple services—similar to Heroku's limitation.&lt;/p&gt;

&lt;p&gt;Vercel includes several modern features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Built-in observability and monitoring&lt;/strong&gt; : Track application performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated preview environments&lt;/strong&gt; : For every pull request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant rollbacks&lt;/strong&gt; : Revert to previous versions when needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; : Programmatic control over resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI support&lt;/strong&gt; : Command-line interface for deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iru6zxr55gnian9z9ct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iru6zxr55gnian9z9ct.png" alt="Vercel Dashboard" width="800" height="443"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5db4l99aj9mkmqydy7ze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5db4l99aj9mkmqydy7ze.png" alt="observability" width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;observability&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3ika8w0f93k9gcgit5j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3ika8w0f93k9gcgit5j.png" alt="Vercel PR bot" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel PR bot&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  External Service Integration
&lt;/h3&gt;

&lt;p&gt;If you would like to integrate your app with other infrastructure primitives (e.g., storage solutions for your application's database, caching, analytical storage), you can do it through the Vercel marketplace. This gives you an integrated billing experience, similar to Heroku's add-on system. However, managing services is still done by accessing the original service provider, making it necessary to switch back and forth between different dashboards when you're building your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cvtoiwumgbcq2ze6tyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cvtoiwumgbcq2ze6tyt.png" alt="Vercel Marketplace" width="800" height="462"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel Marketplace&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations and Constraints
&lt;/h3&gt;

&lt;p&gt;The serverless deployment model abstracts away infrastructure but introduces significant limitations compared to Heroku's long-running server model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory limits&lt;/strong&gt; : The maximum amount of memory per function is 4GB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution time limit&lt;/strong&gt; : The maximum amount of time a function can run is 800 seconds (~13.3 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size (after gzip compression)&lt;/strong&gt;: The maximum is 250 MB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold starts&lt;/strong&gt; : When a function instance is created for the first time, there's added latency. Vercel includes &lt;a href="https://vercel.com/docs/fluid-compute#bytecode-caching" rel="noopener noreferrer"&gt;several optimizations&lt;/a&gt; including bytecode caching, which reduces cold start frequency but won't completely eliminate them&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unsuitable Workloads
&lt;/h3&gt;

&lt;p&gt;If you're currently running the following workloads on Heroku, Vercel functions will not be a suitable replacement:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long-running workloads:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Processing: ETL jobs, large file imports/exports, analytics aggregation&lt;/li&gt;
&lt;li&gt;Media Processing: Video/audio transcoding, image resizing, thumbnail generation&lt;/li&gt;
&lt;li&gt;Report Generation: Creating large PDFs, financial reports, user summaries&lt;/li&gt;
&lt;li&gt;DevOps/Infrastructure: Backups, CI/CD tasks, server provisioning&lt;/li&gt;
&lt;li&gt;Billing &amp;amp; Finance: Usage calculation, invoice generation, payment retries&lt;/li&gt;
&lt;li&gt;User Operations: Account deletion, data merging, stat recalculations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Workloads requiring persistent connections:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chat messaging: Live chats, typing indicators&lt;/li&gt;
&lt;li&gt;Live dashboards: Metrics, analytics, stock tickers&lt;/li&gt;
&lt;li&gt;Collaboration: Document editing, presence&lt;/li&gt;
&lt;li&gt;Live tracking: Delivery location updates&lt;/li&gt;
&lt;li&gt;Push notifications: Instant alerts&lt;/li&gt;
&lt;li&gt;Voice/video calls: Signaling, status updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  DigitalOcean App Platform
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5n9m1yx90z5j5jkw0yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5n9m1yx90z5j5jkw0yd.png" alt="DigitalOcean App platform" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DigitalOcean App platform&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;DigitalOcean App Platform is similar to Heroku in many ways, offering a traditional PaaS experience with some modern improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;DigitalOcean App Platform shares many features with Heroku:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker and source code deployment&lt;/strong&gt; : Deploy from a Docker image or import your source code from GitHub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-running servers&lt;/strong&gt; : Services are deployed to servers that stay running&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public and private networking&lt;/strong&gt; : Included out-of-the-box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub integration&lt;/strong&gt; : Automatic builds and deployments on code pushes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant rollbacks&lt;/strong&gt; : Revert to previous versions when issues arise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated monitoring&lt;/strong&gt; : Built-in metrics and logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI support&lt;/strong&gt; : Command-line interface to manage resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-deploy commands&lt;/strong&gt; : Integrated build pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed TLS and Wildcard domains&lt;/strong&gt; : Custom domains with fully managed TLS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH access&lt;/strong&gt; : Run arbitrary commands against deployed services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infrastructure and Scaling Model
&lt;/h3&gt;

&lt;p&gt;Similar to Heroku, DigitalOcean App Platform follows a traditional, instance-based model. Each instance has a set of allocated compute resources (memory and CPU) and runs on hardware that's owned and operated in data centers across the globe.&lt;/p&gt;

&lt;p&gt;When your deployed service needs more resources, you can scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vertically&lt;/strong&gt; : Manually upgrade to a larger instance size to unlock more compute resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontally&lt;/strong&gt; : Distribute your workload across multiple running instances by either:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this approach covers scaling within a single region, DigitalOcean App Platform does not offer native multi-region support. To achieve a globally distributed deployment, you must provision separate instances in different regions and set up an external load balancer to route traffic between them—the same limitation as Heroku.&lt;/p&gt;

&lt;p&gt;Furthermore, similar to Heroku, services deployed to the platform do not offer persistent data storage. Any data written to the local filesystem is ephemeral and will be lost upon redeployment, meaning you'll need to integrate with external storage solutions if your application requires data durability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Model
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqk6l4h8k7c9wg3apgjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqk6l4h8k7c9wg3apgjr.png" alt="DigitalOcean Instances" width="800" height="409"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DigitalOcean Instances&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;DigitalOcean App Platform follows traditional instance-based pricing like Heroku. You select the amount of compute resources you need from a list of instance sizes, each with a fixed monthly price.&lt;/p&gt;

&lt;p&gt;Fixed pricing results in the same challenges as Heroku:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Under-provisioning&lt;/strong&gt; : Your deployed service doesn't have enough compute resources, leading to failed requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-provisioning&lt;/strong&gt; : Your deployed service has extra unused resources that you're overpaying for every month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Horizontal autoscaling requires threshold tuning, which can be difficult to optimize.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Workflow and CI/CD
&lt;/h3&gt;

&lt;p&gt;DigitalOcean App Platform's dashboard offers a traditional dashboard where you can view all of your project's resources. You can have multi-service architecture where you Deploy multiple services under one project (e.g., a frontend, APIs, databases)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejcezf1ab2eel3gh4eqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejcezf1ab2eel3gh4eqk.png" alt="DigitalOcean Dashboard" width="800" height="610"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DigitalOcean Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Additionally, you can also set up shared environment variables between services using &lt;code&gt;Bindable Variables&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, you can set up health checks to guarantee zero-downtime deployments, a feature that Heroku doesn’t include out-of-the-box.&lt;/p&gt;

&lt;p&gt;However, DigitalOcean App Platform lacks some built-in CI/CD capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No concept of "environments"&lt;/strong&gt; : Unlike Heroku, which has built-in environment support, you must create separate projects for each environment (development, staging, production)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No native preview environments&lt;/strong&gt; : You can't automatically create isolated preview environments for every pull request. To achieve this, you'll need to integrate third-party CI/CD tools like &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, DigitalOcean App Platform doesn't support webhooks (which Heroku does), making it more difficult to build integrations with external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Railway as a Heroku Alternative: Migrate your app
&lt;/h2&gt;

&lt;p&gt;Ready to make the switch? Railway offers the smoothest migration path from Heroku, with similar concepts but better pricing and features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://railway.com/new" rel="noopener noreferrer"&gt;Create an account on Railway&lt;/a&gt;. You can sign up for free and receive $5 in credits to try out the platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying Your Application
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Choose "Deploy from GitHub repo", connect your GitHub account, and select the repository you would like to deploy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdd94jgrp1jejdwlpb0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdd94jgrp1jejdwlpb0a.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Railway onboarding new project&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If your project is using any environment variables or secrets:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc459tdjbdpxx5newr8in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc459tdjbdpxx5newr8in.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Railway environment variables&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To make your project accessible over the internet, configure a domain:&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Considerations When Choosing an Alternative
&lt;/h2&gt;

&lt;p&gt;When evaluating alternatives to Heroku, consider the following factors:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usage-based (Railway, Vercel)&lt;/strong&gt;: Pay only for what you use. Best for variable workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance-based (Render, DigitalOcean, Heroku)&lt;/strong&gt;: Fixed monthly costs. Predictable but can lead to over or under-provisioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine state-based (Fly)&lt;/strong&gt;: Charges based on running time and CPU type&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scaling Approach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic (Railway)&lt;/strong&gt;: Platform automatically scales resources without manual intervention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual/threshold-based (Render, DigitalOcean, Heroku, Fly)&lt;/strong&gt;: Requires manual configuration or threshold tuning&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-Service Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native (Railway, Render, DigitalOcean)&lt;/strong&gt;: Deploy and manage multiple related services in one project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-to-one (Heroku, Vercel, Fly)&lt;/strong&gt;: Each app/service is deployed independently&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Persistent Storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supported (Railway, Render, Fly)&lt;/strong&gt;: Data persists across deployments via volumes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not supported (Heroku, DigitalOcean, Vercel)&lt;/strong&gt;: Requires external storage solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-Region Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native (Railway, Fly)&lt;/strong&gt;: Built-in support for global distribution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual setup (Heroku, Render, DigitalOcean)&lt;/strong&gt;: Requires separate instances and external load balancers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic (Vercel)&lt;/strong&gt;: Serverless functions deploy globally by default&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Developer Experience
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Built-in environments (Railway, Render, Vercel, Heroku)&lt;/strong&gt;: Native support for dev/staging/prod workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires separate orgs/projects (Fly, DigitalOcean)&lt;/strong&gt;: More complex environment management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infrastructure Control
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Own hardware (Railway, Fly, DigitalOcean)&lt;/strong&gt;: Better performance and cost control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs on cloud providers (Heroku, Render, Vercel)&lt;/strong&gt;: Additional costs passed down to users&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While Heroku pioneered the PaaS model, modern alternatives offer compelling improvements in pricing, features, and developer experience. Your choice depends on your specific needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Railway&lt;/strong&gt; is the most comprehensive alternative, offering usage-based pricing, automatic scaling, native multi-region support, persistent storage, and a superior developer experience with multi-service projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; provides a similar feature set to Heroku with some improvements, but maintains traditional instance-based pricing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fly&lt;/strong&gt; offers excellent multi-region support with lightweight VMs, ideal for globally distributed applications that need low latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; is purpose-built for web applications and static sites with serverless functions, but has execution time limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DigitalOcean App Platform&lt;/strong&gt; offers a familiar experience similar to Heroku but lacks some modern features like environment support and preview deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most teams migrating from Heroku, Railway offers the smoothest transition path with the most significant improvements in pricing, features, and developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Need Help or Have Questions?
&lt;/h2&gt;

&lt;p&gt;If you need help along the way, the &lt;a href="http://discord.gg/railway" rel="noopener noreferrer"&gt;Railway Discord&lt;/a&gt; and &lt;a href="https://station.railway.com/" rel="noopener noreferrer"&gt;Help Station&lt;/a&gt; are great resources to get support from the team and community.&lt;/p&gt;

&lt;p&gt;For larger workloads or specific requirements: &lt;a href="https://cal.com/team/railway/work-with-railway" rel="noopener noreferrer"&gt;book a call with the Railway team&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Comparing top PaaS and deployment providers</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Wed, 01 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/comparing-top-paas-and-deployment-providers-3i5m</link>
      <guid>https://dev.to/sarah-railway/comparing-top-paas-and-deployment-providers-3i5m</guid>
      <description>&lt;p&gt;Author: Mahmoud Abdelwahab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr55qcrz286itw77nas8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr55qcrz286itw77nas8b.png" alt="Comparing top PaaS and deployment providers" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many solutions today let developers deploy and manage applications while abstracting away the complexities of infrastructure management.&lt;/p&gt;

&lt;p&gt;That said, each platform offers distinct approaches to deployment, resource management, scaling, and pricing, which will shape your workflow and operational costs.&lt;/p&gt;

&lt;p&gt;This guide compares the following providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/" rel="noopener noreferrer"&gt;Railway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://render.com/" rel="noopener noreferrer"&gt;Render&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;Fly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/products/app-platform" rel="noopener noreferrer"&gt;DigitalOcean App platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this comparison at hand, you’ll be able to make an informed decision on which platform best suits your needs. Here's a high-level summary comparing the platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  PaaS Cloud Deployment Provider Comparison
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Legend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Full support&lt;/li&gt;
&lt;li&gt;⚠️ Partial support or requires workarounds&lt;/li&gt;
&lt;li&gt;❌ Not supported&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Vercel&lt;/th&gt;
&lt;th&gt;Railway&lt;/th&gt;
&lt;th&gt;Render&lt;/th&gt;
&lt;th&gt;Fly&lt;/th&gt;
&lt;th&gt;DigitalOcean&lt;/th&gt;
&lt;th&gt;Heroku&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DEPLOYMENT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment Model&lt;/td&gt;
&lt;td&gt;Serverless functions&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;td&gt;Lightweight VMs&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;td&gt;Long-running servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Support&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source Code Deploy&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Service Projects&lt;/td&gt;
&lt;td&gt;❌ No (one-to-one)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INFRASTRUCTURE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs On&lt;/td&gt;
&lt;td&gt;AWS (serverless)&lt;/td&gt;
&lt;td&gt;Own hardware&lt;/td&gt;
&lt;td&gt;AWS/GCP&lt;/td&gt;
&lt;td&gt;Own hardware&lt;/td&gt;
&lt;td&gt;Own hardware&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max Memory&lt;/td&gt;
&lt;td&gt;4GB&lt;/td&gt;
&lt;td&gt;Plan-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution Limits&lt;/td&gt;
&lt;td&gt;13.3 min max&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold Starts&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent Storage via volumes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DATABASES &amp;amp; STORAGE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database Support&lt;/td&gt;
&lt;td&gt;Via marketplace&lt;/td&gt;
&lt;td&gt;✅ One-click deploy any open-source database&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ Native (via add-ons)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCALING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vertical AutoScaling&lt;/td&gt;
&lt;td&gt;✅ Automatic&lt;/td&gt;
&lt;td&gt;✅ Automatic&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;td&gt;⚠️ Manual/threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal Scaling&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes - - By deploying replicas&lt;/td&gt;
&lt;td&gt;✅ Yes - - Configure min and max number of concurrent instances&lt;/td&gt;
&lt;td&gt;✅ Yes - - By deploying fly-autoscaler&lt;/td&gt;
&lt;td&gt;✅ Yes - - Configure min and max number of concurrent instances&lt;/td&gt;
&lt;td&gt;✅ Yes - - Configure min and max number of concurrent instances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Region Support&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;❌ No (requires manual setup)&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;❌ No (requires manual setup)&lt;/td&gt;
&lt;td&gt;❌ No (requires manual setup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PRICING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing Model&lt;/td&gt;
&lt;td&gt;Usage-based - - Active compute time + resources used&lt;/td&gt;
&lt;td&gt;Usage-based - - Active compute time + resources used&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Machine state-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;td&gt;Instance-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing Factors&lt;/td&gt;
&lt;td&gt;CPU time + memory + invocations&lt;/td&gt;
&lt;td&gt;Active compute time × size&lt;/td&gt;
&lt;td&gt;Fixed monthly per instance. When scaling horizontally it’s instance size x total running time&lt;/td&gt;
&lt;td&gt;Running time + CPU type&lt;/td&gt;
&lt;td&gt;Fixed monthly per instance&lt;/td&gt;
&lt;td&gt;Fixed monthly per instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scales to Zero&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Supported via app sleeping&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Supported via autostop&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD &amp;amp; ENVIRONMENTS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Integration&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PR Preview Environments&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Not supported out of the box. Requires setting up a CI/CD pipeline&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment Support&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;⚠️ Separate orgs&lt;/td&gt;
&lt;td&gt;⚠️ Separate projects&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instant Rollbacks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-Deploy Commands&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Manual when setting up a deployment pipeline&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OBSERVABILITY&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built-in Monitoring&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes (Prometheus)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated Logs&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DEVELOPER TOOLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure as Code&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI Support&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH Access&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NETWORKING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Domains&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wildcard Domains&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed TLS&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Networking&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Paid add-on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health Checks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ADDITIONAL FEATURES&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native Support for Cron Jobs&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared Variables&lt;/td&gt;
&lt;td&gt;⚠️ Within project&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Vercel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtw5pi4g0ulsncvwaud2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtw5pi4g0ulsncvwaud2.png" alt="vercel.com" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;vercel.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vercel makes it possible to deploy web applications and static sites while abstracting away infrastructure management and scaling.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Infrastructure and Deployment Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Vercel has developed a proprietary deployment model where infrastructure components are derived from the application code through a concept called &lt;a href="https://vercel.com/blog/framework-defined-infrastructure" rel="noopener noreferrer"&gt;Framework-defined infrastructure&lt;/a&gt;. At build time, application code is parsed and translated into the necessary infrastructure components. Server-side code is then deployed as serverless functions. Note that Vercel does not support the deployment of Docker images or containers.&lt;/p&gt;

&lt;p&gt;To handle scaling, Vercel creates a new function instance for each incoming request with support for concurrent execution within the same instance through their &lt;a href="https://vercel.com/docs/fluid-compute" rel="noopener noreferrer"&gt;Fluid compute&lt;/a&gt; system. Over time, functions scale down to zero to save on compute resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb88whciwyl55d8msmpfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb88whciwyl55d8msmpfj.png" alt="Vercel Fluid Compute" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel Fluid Compute&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Vercel functions are billed based on three primary factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active CPU: Time your code actively runs in milliseconds&lt;/li&gt;
&lt;li&gt;Provisioned memory: Memory held by the function instance, for the full lifetime of the instance&lt;/li&gt;
&lt;li&gt;Invocations: number of function requests, where you’re billed per request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each pricing plan includes a certain allocation of these metrics. This makes it possible for you to pay for what you use. However, since Vercel runs on AWS, the unit economics of the business need to be high to offset the cost of the underlying infrastructure. Those extra costs are then passed down to you as the user, so you end up paying extra for resources such as bandwidth, memory, CPU and storage.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Project Management and Developer Experience&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In Vercel, a project maps to a deployed application. If you would like to deploy multiple apps, you’ll do it by creating multiple projects. This one-to-one mapping can complicate architectures with multiple services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iru6zxr55gnian9z9ct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iru6zxr55gnian9z9ct.png" alt="Vercel Dashboard" width="800" height="443"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vercel also includes support for built-in observability and monitoring&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cnh1loheu286x82xk4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cnh1loheu286x82xk4c.png" alt="observability" width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;observability&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There’s also support for automated preview environments for every pull request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqj74ia8gzd858g21eu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqj74ia8gzd858g21eu3.png" alt="Vercel PR bot" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel PR bot&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;External Service Integration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you would like to integrate your app with other infrastructure primitives (e.g storage solutions for your application’s database, caching, analytical storage, etc.), you can do it through the Vercel marketplace. This gives you an integrated billing experience, however managing services is still done by accessing the original service provider. Making it necessary to switch back and forth between different dashboards when you’re building your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cvtoiwumgbcq2ze6tyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cvtoiwumgbcq2ze6tyt.png" alt="Vercel Marketplace" width="800" height="462"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vercel Marketplace&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Limitations and Constraints&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This serverless deployment model abstracts away infrastructure, but introduces significant limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory limits: the maximum amount of memory per function is 4GB&lt;/li&gt;
&lt;li&gt;Execution time limit: the maximum amount of time a function can run is 800 seconds (~13.3 minutes)&lt;/li&gt;
&lt;li&gt;Size (after gzip compression): the maximum is 250 MB&lt;/li&gt;
&lt;li&gt;Cold starts: when a function instance is created for the first time, there’s an amount of added latency. Vercel includes &lt;a href="https://vercel.com/docs/fluid-compute#bytecode-caching" rel="noopener noreferrer"&gt;several optimizations&lt;/a&gt; including bytecode caching, which reduces cold start frequency but won’t completely eliminate them&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Unsuitable Workloads&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you plan on running long-running workloads, Vercel functions will not be the right fit. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Processing: ETL jobs, large file imports/exports, analytics aggregation&lt;/li&gt;
&lt;li&gt;Media Processing: Video/audio transcoding, image resizing, thumbnail generation&lt;/li&gt;
&lt;li&gt;Report Generation: Creating large PDFs, financial reports, user summaries&lt;/li&gt;
&lt;li&gt;DevOps/Infrastructure: Backups, CI/CD tasks, server provisioning&lt;/li&gt;
&lt;li&gt;Billing &amp;amp; Finance: Usage calculation, invoice generation, payment retries&lt;/li&gt;
&lt;li&gt;User Operations: Account deletion, data merging, stat recalculations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, workloads that require a persistent connection are incompatible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chat messaging: Live chats, typing indicators&lt;/li&gt;
&lt;li&gt;Live dashboards: Metrics, analytics, stock tickers&lt;/li&gt;
&lt;li&gt;Collaboration: Document editing, presence&lt;/li&gt;
&lt;li&gt;Live tracking: Delivery location updates&lt;/li&gt;
&lt;li&gt;Push notifications: Instant alerts&lt;/li&gt;
&lt;li&gt;Voice/video calls: Signaling, status updates&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Railway
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff41tllk4anbwpyqcqqo0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff41tllk4anbwpyqcqqo0.png" alt="railway.com" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;railway.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Railway enables you to deploy applications to long-running servers, making it ideal for applications that need to stay running or maintain a persistent connection. You can deploy your apps as services from a Docker image or by importing your source code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dashboard
&lt;/h3&gt;

&lt;p&gt;Railway’s dashboard offers a real-time collaborative canvas where you can view all of your running services and databases at a glance. Projects contain multiple services and databases (frontend, APIs, workers, databases, queues). You can group the different infrastructure components and visualize how they’re related to one another.&lt;/p&gt;
&lt;h3&gt;
  
  
  Deployment experience
&lt;/h3&gt;

&lt;p&gt;You also have programmatic control over your resources through Infrastructure-as-Code (IaC) definitions and a command-line interface.&lt;/p&gt;

&lt;p&gt;You can connect your GitHub repository to enable automatic builds and deployments whenever you push code, and create &lt;a href="https://docs.railway.com/guides/environments#enable-pr-environments" rel="noopener noreferrer"&gt;isolated preview environments for every pull request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If issues arise, you can revert your app to previous versions. Railway’s integrated build pipeline supports pre-deploy commands, and you can run arbitrary commands against deployed services via SSH.&lt;/p&gt;

&lt;p&gt;When it comes to observability, Railway has integrated metrics and logs help you track application performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.railway.com%2F_next%2Fimage%3Furl%3Dhttps%253A%252F%252Fres.cloudinary.com%252Frailway%252Fimage%252Fupload%252Fv1717179720%252FWholescreenshot_vc5l5e.png%26w%3D3840%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.railway.com%2F_next%2Fimage%3Furl%3Dhttps%253A%252F%252Fres.cloudinary.com%252Frailway%252Fimage%252Fupload%252Fv1717179720%252FWholescreenshot_vc5l5e.png%26w%3D3840%26q%3D80" alt="Observability Dashboard" width="2223" height="1298"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Observability Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, Railway supports networking features like public and private networking, custom domains with managed TLS as well as wildcard domains.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Database Support&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Railway has first-class support for Databases. You can one-click deploy any open-source database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relational: Postgres, MySQL&lt;/li&gt;
&lt;li&gt;Analytical: ClickHouse, Timescale&lt;/li&gt;
&lt;li&gt;Key-value: Redis, Dragonfly&lt;/li&gt;
&lt;li&gt;Vector: Chroma, Weaviate&lt;/li&gt;
&lt;li&gt;Document: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out all of the &lt;a href="https://railway.com/deploy?category=Storage" rel="noopener noreferrer"&gt;different storage solutions&lt;/a&gt; you can deploy.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Automatic Scaling and Resource Management&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Railway automatically scales compute resources based on workload without manual threshold configuration. Each plan has defined CPU and memory limits.&lt;/p&gt;

&lt;p&gt;You can scale horizontally by deploying multiple replicas of your service. Railway automatically distributes public traffic randomly across replicas within each region. Each replica runs with the full resource limits of your plan.&lt;/p&gt;

&lt;p&gt;Replicas can be placed in different geographical locations. The platform automatically routes public traffic to the nearest region, then randomly distributes requests among the available replicas within that region.&lt;/p&gt;

&lt;p&gt;Finally, if you would to save on compute resources, you can enable &lt;a href="https://docs.railway.com/reference/app-sleeping" rel="noopener noreferrer"&gt;app sleeping&lt;/a&gt;, which suspends a running service after 10 mins of inactivity. Services will then become active on incoming requests.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Usage-Based Pricing Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Railway follows a usage-based pricing model that depends on how long your service runs and the amount of resources it consumes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active compute time x compute size (memory and CPU)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk86d062ygt8cuo61u6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk86d062ygt8cuo61u6n.png" alt="Railway autoscaling" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway autoscaling&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you spin up multiple replicas for a given service, you’ll only be charged for the active compute time for each replica.&lt;/p&gt;

&lt;p&gt;Railway's underlying infrastructure runs on hardware that’s owned and operated in data centers across the globe. By controlling the hardware, software, and networking stack end to end, the platform delivers best-in-class performance, reliability, and powerful features, all while keeping costs in check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Render
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn40vnivf9x2boxx06yof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn40vnivf9x2boxx06yof.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Render is similar to Railway in the following aspects&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can deploy your app from a Docker image or by importing your app’s source code from GitHub.&lt;/li&gt;
&lt;li&gt;Multi-service architecture where you can deploy different services under one project (e.g. a frontend, APIs, databases, etc.).&lt;/li&gt;
&lt;li&gt;Services are deployed to a long-running server.&lt;/li&gt;
&lt;li&gt;Services can have persistent storage via volumes.&lt;/li&gt;
&lt;li&gt;Public and private networking are included out-of-the-box.&lt;/li&gt;
&lt;li&gt;Healthchecks are available to guarantee zero-downtime deployments.&lt;/li&gt;
&lt;li&gt;Connect your GitHub repository for automatic builds and deployments on code pushes.&lt;/li&gt;
&lt;li&gt;Create isolated preview environments for every pull request.&lt;/li&gt;
&lt;li&gt;Support for instant rollbacks.&lt;/li&gt;
&lt;li&gt;Integrated metrics and logs.&lt;/li&gt;
&lt;li&gt;Define Infrastructure-as-Code (IaC).&lt;/li&gt;
&lt;li&gt;Command-line-interface (CLI) to manage resources.&lt;/li&gt;
&lt;li&gt;Integrated build pipeline with the ability to define pre-deploy command.&lt;/li&gt;
&lt;li&gt;Support for wildcard domains.&lt;/li&gt;
&lt;li&gt;Custom domains with fully managed TLS.&lt;/li&gt;
&lt;li&gt;Schedule tasks with cron jobs.&lt;/li&gt;
&lt;li&gt;Run arbitrary commands against deployed services (SSH).&lt;/li&gt;
&lt;li&gt;Shared environment variables across services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, there are some differences between the platforms that might make Railway a better fit for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Infrastructure and Scaling Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Render follows a traditional, instance-based model. Each instance has a set of allocated compute resources (memory and CPU).&lt;/p&gt;

&lt;p&gt;In the scenario where your deployed service needs more resources, you can either scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vertically: you will need to manually upgrade to a large instance size to unlock more compute resources.&lt;/li&gt;
&lt;li&gt;Horizontally: your workload will be distributed across multiple running instances. You can either:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this approach covers scaling within a single region, Render does not offer native multi-region support. To achieve a globally distributed deployment, you must provision separate instances in different regions and set up an external load balancer to route traffic between them.&lt;/p&gt;

&lt;p&gt;The main drawback of this setup is that it requires manual developer intervention. Either by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually changing instance sizes/running instance count.&lt;/li&gt;
&lt;li&gt;Manually adjusting thresholds because you can get into situations where your service scales up for spikes but doesn’t scale down quickly enough, leaving you paying for unused resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Render follows a traditional, instance-based pricing. You select the amount of compute resources you need from a list of instance sizes where each one has a fixed monthly price.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xcif0eoh5a49svzrkvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xcif0eoh5a49svzrkvi.png" alt="Render Instances" width="800" height="356"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Render Instances&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While this model gives you predictable pricing, the main drawback is you end up in one of two situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under-provisioning: your deployed service doesn’t have enough compute resources which will lead to failed requests.&lt;/li&gt;
&lt;li&gt;Over-provisioning: your deployed service will have extra unused resources that you’re overpaying for every month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enabling horizontal autoscaling can help with optimizing costs, but the trade-off will be needing to figure out the right amount of thresholds instead.&lt;/p&gt;

&lt;p&gt;Additionally, Render runs on AWS and GCP, so the unit economics of the business need to be high to offset the cost of the underlying infrastructure. Those extra costs are then passed down to you as the user, so you end up paying extra for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlocking additional features (e.g. horizontal autoscaling and environments are only available on paid plans).&lt;/li&gt;
&lt;li&gt;Pay extra for resources (e.g., bandwidth, memory, CPU and storage).&lt;/li&gt;
&lt;li&gt;Pay for seats where each team member you invite adds a fixed monthly fee regardless of your usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu865r51vlhv3fh7mxso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu865r51vlhv3fh7mxso.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At a high level, &lt;a href="http://fly.io/" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt; is similar to Render and Railway in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can deploy your app from a Docker image or by importing your app’s source code from GitHub.&lt;/li&gt;
&lt;li&gt;Apps are deployed to a long-running server.&lt;/li&gt;
&lt;li&gt;Apps can have persistent storage through volumes.&lt;/li&gt;
&lt;li&gt;Public and private networking are included out-of-the-box.&lt;/li&gt;
&lt;li&gt;Healthchecks to guarantee zero-downtime deployments.&lt;/li&gt;
&lt;li&gt;Connect your GitHub repository for automatic builds and deployments on code pushes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, there are differences when it comes to the overall developer experience&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure and deployment model
&lt;/h3&gt;

&lt;p&gt;When you deploy your app to Fly, your code runs on lightweight Virtual Machines (VMs) called Fly Machines. Each machine needs a defined amount of CPU and memory. You can either choose from preset sizes or configure them separately, depending on your app’s needs.&lt;/p&gt;

&lt;p&gt;Machines come with two CPU types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared CPUs&lt;/strong&gt; : 6% guaranteed CPU time with bursting capability. Subject to throttling under heavy usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance CPUs&lt;/strong&gt; : Dedicated CPU access without throttling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fly machines run on hardware that’s owned and operated in data centers across the globe, with support for multi-region deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scaling your application&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When scaling your app, you have one of two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale a machine’s CPU and RAM&lt;/strong&gt; : you will need to manually pick a larger instance. You can do this using the Fly CLI or API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increase the number of running machines&lt;/strong&gt;. There are two options:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jvwqdbzij0sexm9u8ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jvwqdbzij0sexm9u8ik.png" alt="Scaling on Fly" width="800" height="317"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Scaling on Fly&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmma47sttmjkfuvr3sqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmma47sttmjkfuvr3sqr.png" alt="Fly Pricing" width="800" height="1723"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fly Pricing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fly charges for compute based on two primary factors: machine state and CPU type (shared vs. performance).&lt;/p&gt;

&lt;p&gt;Machine state determines the base charge structure. Started machines incur full compute charges, while stopped machines are only charged for root file system (rootfs) storage. The rootfs size depends on your OCI image plus &lt;a href="https://containerd.io/" rel="noopener noreferrer"&gt;containerd&lt;/a&gt; optimizations applied to the underlying file system.&lt;/p&gt;

&lt;p&gt;Reserved compute blocks require annual upfront payment with monthly non-rolling credits.&lt;/p&gt;

&lt;p&gt;Fly Machines charge based on running time regardless of utilization. Stopped machines only incur storage charges.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Developer Workflow and CI/CD&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Fly provides a CLI-first experience through &lt;code&gt;flyctl&lt;/code&gt;, allowing you to create and deploy apps, manage Machines and volumes, configure networking, and perform other infrastructure tasks directly from the command line.&lt;/p&gt;

&lt;p&gt;However, Fly lacks built-in CI/CD capabilities. This means you can’t:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create isolated preview environments for every pull request&lt;/li&gt;
&lt;li&gt;Perform instant rollbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To access these features, you’ll need to integrate third-party CI/CD tools like &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Similarly, Fly doesn’t include native environment support for development, staging, and production workflows. To achieve proper environment isolation, you must create separate organizations for each environment and link them to a parent organization for centralized billing management.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monitoring and Metrics&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For monitoring, Fly automatically collects metrics from every application using a fully-managed Prometheus service based on VictoriaMetrics. The system scrapes metrics from all application instances and provides data on HTTP responses, TCP connections, memory usage, CPU performance, disk I/O, network traffic, and filesystem utilization.&lt;/p&gt;

&lt;p&gt;The Fly dashboard includes a basic Metrics tab displaying this automatically collected data. Beyond the basic dashboard, Fly offers a managed Grafana instance at &lt;a href="http://fly-metrics.net/" rel="noopener noreferrer"&gt;fly-metrics.net&lt;/a&gt; with detailed dashboards and query capabilities using MetricsQL as the querying language. You can also connect external tools through the Prometheus API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6xc32wj38fmjn6wkfp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6xc32wj38fmjn6wkfp0.png" alt="fly-metrics.net" width="800" height="412"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;fly-metrics.net&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Alerting and custom dashboards require multiple tools and query languages. Additionally, Fly doesn’t support webhooks, making it more difficult to build integrations with external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  DigitalOcean App Platform
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4qz0t7muk18olit88vh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4qz0t7muk18olit88vh.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At a high level, DigitalOcean App Platform is similar to Railway and Render in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can deploy your app from a Docker image or by importing your app’s source code from GitHub.&lt;/li&gt;
&lt;li&gt;Multi-service architecture where you can deploy different services under one project (e.g. a frontend, APIs, databases, etc.).&lt;/li&gt;
&lt;li&gt;Services are deployed to a long-running server.&lt;/li&gt;
&lt;li&gt;Public and private networking are included out-of-the-box.&lt;/li&gt;
&lt;li&gt;Healthchecks are available to guarantee zero-downtime deployments.&lt;/li&gt;
&lt;li&gt;Connect your GitHub repository for automatic builds and deployments on code pushes.&lt;/li&gt;
&lt;li&gt;Support for instant rollbacks.&lt;/li&gt;
&lt;li&gt;Integrated metrics and logs.&lt;/li&gt;
&lt;li&gt;Define Infrastructure-as-Code (IaC).&lt;/li&gt;
&lt;li&gt;Command-line-interface (CLI) to manage resources.&lt;/li&gt;
&lt;li&gt;Integrated build pipeline with the ability to define pre-deploy command.&lt;/li&gt;
&lt;li&gt;Support for wildcard domains.&lt;/li&gt;
&lt;li&gt;Custom domains with fully managed TLS.&lt;/li&gt;
&lt;li&gt;Run arbitrary commands against deployed services (SSH).&lt;/li&gt;
&lt;li&gt;Shared environment variables across services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Infrastructure and Scaling Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Similar to Render, DigitalOcean App Platform follows a traditional, instance-based model.&lt;/p&gt;

&lt;p&gt;Each instance has a set of allocated compute resources (memory and CPU) and runs on on hardware that’s owned and operated in data centers across the globe.&lt;/p&gt;

&lt;p&gt;In the scenario where your deployed service needs more resources, you can either scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vertically: you will need to manually upgrade to a large instance size to unlock more compute resources.&lt;/li&gt;
&lt;li&gt;Horizontally: your workload will be distributed across multiple running instances. You can either:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this approach covers scaling within a single region, DigitalOcean App Platform does not offer native multi-region support. To achieve a globally distributed deployment, you must provision separate instances in different regions and set up an external load balancer to route traffic between them.&lt;/p&gt;

&lt;p&gt;Furthermore, services deployed to the platform do not offer persistent data storage. Any data written to the local filesystem is ephemeral and will be lost upon redeployment, meaning you’ll need to integrate with external storage solutions if your application requires data durability.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqk6l4h8k7c9wg3apgjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqk6l4h8k7c9wg3apgjr.png" alt="DigitalOcean Instances" width="800" height="409"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DigitalOcean Instances&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;DigitalOcean App Platform follows a traditional, instance-based pricing. You select the amount of compute resources you need from a list of instance sizes where each one has a fixed monthly price.&lt;/p&gt;

&lt;p&gt;Fixed pricing results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under-provisioning: your deployed service doesn’t have enough compute resources which will lead to failed requests&lt;/li&gt;
&lt;li&gt;Over-provisioning: your deployed service will have extra unused resources that you’re overpaying for every month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Horizontal autoscaling requires threshold tuning.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Developer Workflow and CI/CD&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DigitalOcean App Platform’s dashboard offers a traditional dashboard where you can view all of your project’s resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejcezf1ab2eel3gh4eqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejcezf1ab2eel3gh4eqk.png" alt="DigitalOcean Dashboard" width="800" height="610"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DigitalOcean Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, DigitalOcean App Platform lacks built-in CI/CD capabilities around environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No concept of “environments” (e.g., development, staging, and production). To achieve proper environment isolation, you must create separate projects for each environment.&lt;/li&gt;
&lt;li&gt;No native support for automatically creating isolated preview environments for every pull request. To achieve this, you’ll need to integrate third-party CI/CD tools like &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, DigitalOcean App Platform doesn’t support webhooks, making it more difficult to build integrations with external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Heroku
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylrorijb5oqb4p5syn0f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylrorijb5oqb4p5syn0f.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Heroku is similar to Railway, Render, DigitalOcean App Platform in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can deploy your app from a Docker image or by importing your app’s source code from GitHub.&lt;/li&gt;
&lt;li&gt;Services are deployed to a long-running server.&lt;/li&gt;
&lt;li&gt;Connect your GitHub repository for automatic builds and deployments on code pushes.&lt;/li&gt;
&lt;li&gt;Create isolated preview environments for every pull request.&lt;/li&gt;
&lt;li&gt;Support for instant rollbacks.&lt;/li&gt;
&lt;li&gt;Integrated metrics and logs.&lt;/li&gt;
&lt;li&gt;Define Infrastructure-as-Code (IaC).&lt;/li&gt;
&lt;li&gt;Command-line-interface (CLI) to manage resources.&lt;/li&gt;
&lt;li&gt;Integrated build pipeline with the ability to define pre-deploy command.&lt;/li&gt;
&lt;li&gt;Custom domains with fully managed TLS.&lt;/li&gt;
&lt;li&gt;Run arbitrary commands against deployed services (SSH).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, there are some differences&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Infrastructure and Scaling Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Heroku follows a traditional, instance-based model. Each instance has a set of allocated compute resources (memory and CPU).&lt;/p&gt;

&lt;p&gt;In the scenario where your deployed service needs more resources, you can either scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vertically&lt;/strong&gt; : you will need to manually upgrade to a large instance size to unlock more compute resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontally&lt;/strong&gt; : your workload will be distributed across multiple running instances. You can either:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This requires manual intervention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually changing instance sizes/running instance count&lt;/li&gt;
&lt;li&gt;Manually adjusting thresholds because you can get into situations where your service scales up for spikes but doesn’t scale down quickly enough, leaving you paying for unused resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Heroku lacks native multi-region support. Requires separate instances and external load balancers.&lt;/p&gt;

&lt;p&gt;Furthermore, services deployed to the platform do not offer persistent data storage. Any data written to the local filesystem is ephemeral and will be lost upon redeployment, meaning you’ll need to integrate with external storage solutions if your application requires data durability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2g8korrfzzat1n6are9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2g8korrfzzat1n6are9.png" alt="Heroku Instances" width="800" height="572"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Heroku Instances&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Similar to Render and DigitalOcean app platform, Heroku follows a traditional, instance-based pricing. You select the amount of compute resources you need from a list of instance sizes where each one has a fixed monthly price.&lt;/p&gt;

&lt;p&gt;Fixed pricing results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under-provisioning: your deployed service doesn’t have enough compute resources which will lead to failed requests&lt;/li&gt;
&lt;li&gt;Over-provisioning: your deployed service will have extra unused resources that you’re overpaying for every month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Horizontal autoscaling requires threshold tuning.&lt;/p&gt;

&lt;p&gt;Since Heroku runs on AWS. Additional costs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlocking additional features (e.g. private networking is a paid enterprise add-on)&lt;/li&gt;
&lt;li&gt;Pay extra for resources (e.g., bandwidth, memory, CPU and storage)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dashboard and Organizational Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Heroku’s unit of deployment is the app, and each app is deployed independently. If you have different infrastructure components (e.g. API, frontend, background workers, etc.) they will be treated as independent entities. There is no top‑level “project” object that groups related apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1wqvutxpa456ihf1yp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1wqvutxpa456ihf1yp1.png" alt="Heroku Dashboard" width="800" height="452"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Heroku Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Additionally, Heroku does not support shared environment variables across apps. Each deployed app has its own isolated set of variables, making it harder to manage secrets or config values shared across multiple services. Finally, Heroku doesn’t support wildcard domains. Each subdomain requires manual configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate your application to Railway
&lt;/h2&gt;

&lt;p&gt;To get started, &lt;a href="https://railway.com/new" rel="noopener noreferrer"&gt;create an account on Railway&lt;/a&gt;. You can sign up for free and receive $5 in credits to try out the platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying your application
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;“Choose Deploy from GitHub repo”, connect your GitHub account, and select the repo you would like to deploy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdd94jgrp1jejdwlpb0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdd94jgrp1jejdwlpb0a.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Railway onboarding new project&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If your project is using any environment variables or secrets:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc459tdjbdpxx5newr8in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc459tdjbdpxx5newr8in.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Railway environment variables&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To make your project accessible over the internet, you will need to configure a domain:&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Need help or have questions?
&lt;/h2&gt;

&lt;p&gt;If you need help along the way, the &lt;a href="http://discord.gg/railway" rel="noopener noreferrer"&gt;Railway Discord&lt;/a&gt; and &lt;a href="https://station.railway.com/" rel="noopener noreferrer"&gt;Help Station&lt;/a&gt; are great resources to get support from the team and community.&lt;/p&gt;

&lt;p&gt;For larger workloads or specific requirements: &lt;a href="https://cal.com/team/railway/work-with-railway" rel="noopener noreferrer"&gt;book a call with the Railway team&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>The F in SOC2 stands for functional</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Tue, 16 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/the-f-in-soc2-stands-for-functional-39jl</link>
      <guid>https://dev.to/sarah-railway/the-f-in-soc2-stands-for-functional-39jl</guid>
      <description>&lt;p&gt;Author: Angelo Saraceno&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwipvsygthgnzuc5pu1f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwipvsygthgnzuc5pu1f5.png" alt="The F in SOC2 stands for functional" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At Railway we believe that everyone should be able to deploy software, instantly.&lt;/p&gt;

&lt;p&gt;No certs, no training. Code from laptop to live as quickly as you can commit code.&lt;/p&gt;

&lt;p&gt;So naturally I tend to be the type of person that is skeptical of credentialing regimes.&lt;/p&gt;

&lt;p&gt;Before anyone sends us hate mail saying that we hate security. Absolutely not, we spend a lot of our time fighting fraudulent workloads, dealing with spammers and abusers, and patching up our systems up to snuff. This is work that historically takes thousands of engineering hours that we’ve taken on behalf of our customers.&lt;/p&gt;

&lt;p&gt;Some of the said work we can’t talk about.&lt;/p&gt;

&lt;p&gt;I also hear you typing: “Well, you just don’t like compliance.”&lt;/p&gt;

&lt;p&gt;Personally, I find the screenshots fun. Working at a startup is akin to founding a new country, and to me, there is nothing more exciting than contributing to the legitimacy of an organization like mature processes.&lt;/p&gt;

&lt;p&gt;At my last employer, I spent a considerable amount of time working with FedRAMP auditors making sure that the last product I managed was fully compliant with the security standards, export controls, and documentation that one needs whenever you sell to the U.S. Federal Government. Before that, when I was a entrepreneur, I tried my hand at writing various SBIRs/STTRs and selling to government. I think the controls there for those organizations make sense. (I have also contributed to writing successful NSF grant proposals in a former life.)&lt;/p&gt;

&lt;p&gt;There is no bureaucracy that I can’t navigate.&lt;/p&gt;

&lt;p&gt;But for a company, that is just starting, that is very much likely to pivot, or to get their first design partner or proof of concept- SOC2 is a strangle hold of a tax.&lt;/p&gt;

&lt;p&gt;Where in my opinion, adds undue stress for founders that otherwise would have been more logical to push the process until later. Those founders can’t complain about it because doing so would ruin their legitimacy.&lt;/p&gt;

&lt;p&gt;With the way SOC2 is currently used, especially enforced by SOC2 software vendors has created a SOC2 pyramid scheme. Where in order to be (quickly) SOC2 compliant, it’s highly suggested that your vendors are also certified, forcing the entire stack to have the “certification” sticker- or be removed from an organization’s footprint.&lt;/p&gt;

&lt;p&gt;To make matters worse, the now canonical way it’s implemented and audited flies in the face on how it was originally supposed to be implemented at these organizations.&lt;/p&gt;

&lt;p&gt;We now require every startup to have screenshots and auditors &lt;em&gt;before&lt;/em&gt; they have customers. We've added a $40,000 tax on any entrepreneur trying to sell to enterprises—another headwind founders don't need.&lt;/p&gt;

&lt;p&gt;At Railway, we back entrepreneurs and host their applications. I, however, worry that building the next Zoom (2011), Slack (2013), or Superhuman (2014) wouldn't be possible today. Not impossible, but significantly harder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We've created a credentialing cartel.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hear the keyboard already! You must be typing out “No way this is true, the process can’t be that bad!”&lt;/p&gt;

&lt;p&gt;And I agree, it’s not bad, for us, a VC funded company with experienced operations staff- but if you are a 2-3 person company, I think it’s fatal. Lemme explain to you how.&lt;/p&gt;

&lt;h1&gt;
  
  
  How SOC2 Works
&lt;/h1&gt;

&lt;p&gt;Tiny sidebar on what SOC2 is:&lt;/p&gt;

&lt;p&gt;SOC 2 is an independent audit (ran by a licensed CPA firm) that produces an attestation report, not a certification, about how a company’s security controls are designed and operate against the &lt;a href="https://www.sikich.com/insight/how-tsc-trust-services-criteria-can-improve-your-business/" rel="noopener noreferrer"&gt;AICPA’s Trust Services Criteria&lt;/a&gt; (security, availability, confidentiality, processing integrity, and privacy).&lt;/p&gt;

&lt;p&gt;What this looks like is a number of controls that are listed out on a matrix, and then a company spends their time writing documentation to show that they meet these controls.&lt;/p&gt;

&lt;p&gt;A Type I opines on whether controls are designed and implemented correctly at a point in time.&lt;/p&gt;

&lt;p&gt;A Type II checks that those controls actually operated over a observation window (commonly 6–12 months).&lt;/p&gt;

&lt;p&gt;Note, that there is not a legal requirement for SOC2, although GDPR and California’s data privacy framework are law, this gave rise to a number of privacy and compliance management vendors who now occupy the space.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we did in the times of yore
&lt;/h2&gt;

&lt;p&gt;SOC2 has been around for quite sometime, since the 1970s in fact. However, it’s rise was in part due to a few factors.&lt;/p&gt;

&lt;p&gt;As the demand from the public geared lawmakers in the U.S. and Europe to craft greater accountability to large tech companies, greater government regulation about data sovereignty arose. Example on how it applies to Railway. For any customer who decides to host their workload in the EU, we CANNOT move that workload to a different country and we have spent a good amount of effort to prove it via our EU representative that this is the case.&lt;/p&gt;

&lt;p&gt;The increase in government oversight led to the rise of SOC2 security vendors that have compelled companies that they need SOC2 on top of the existing compliance frameworks.&lt;/p&gt;

&lt;p&gt;In the past before this regime, you would sell to a business, and then at the mid-market level, they would send you to the IT where you would do a security review in the form for a questionnaire. It was long, but it was around 30 or so questions. Provided you had a sufficiently motivated buyer, it was not a major blocker after they signed an NDA.&lt;/p&gt;

&lt;h2&gt;
  
  
  However… enter screenshot mania
&lt;/h2&gt;

&lt;p&gt;As the rise of these security vendors popped up, AICPA released SSAE 18 which updated the matrix and had a greater focus on cloud presence and data governance.&lt;/p&gt;

&lt;p&gt;What security documentation vendors do then is take the list of controls, and then attempt to model a questionnaire off of the SSAE 18 standards that an auditor would expect when reviewing your packet.&lt;/p&gt;

&lt;p&gt;When you look at the controls, they seem reasonable, take this one for instance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The entity demonstrates a commitment to attract, develop, and retain competent individuals in alignment with objectives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;—CC1.4, SOC2 SSAE 18&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s walk through the mindset of someone in operations should have when working with this document. Cool, I would provide proof of our hiring process, our recruiting process, and our code of conduct.&lt;/p&gt;

&lt;p&gt;Usually, a security vendor would then prompt you to provide the following documentation and then you are good to go.&lt;/p&gt;

&lt;p&gt;Except, there are 250 of these questions.&lt;/p&gt;

&lt;p&gt;I will admit, most of them, are easy, and for a mature organization would have no problem spending the month or so going through the list at their own leisure as they beef up their organizational maturity addressing any gaps that the controls suggest.&lt;/p&gt;

&lt;p&gt;But if you are a seed stage start up trying to account for this one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The entity authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures to meet its objectives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;—CC8.1, SOC2 SSAE 18&lt;/p&gt;

&lt;p&gt;There is a David Graber-esque fake work PDF that you would have to spit out to satisfy the request of the control. Let’s assume that you are using a new upstart dev-tool, unless you feel strongly about it, you would just rip the tool out.&lt;/p&gt;

&lt;p&gt;Since every prospect that you consider you as a vendor requires SOC2 to maintain their own SOC2 (or at least make it easier for their attestation report) we’re in a world where not companies are doing the questionnaire in bad faith, getting an attestation for a product that doesn’t exist, attesting for a process that doesn’t exist- watering down the attestation for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Auditor is biased, and works for you.
&lt;/h2&gt;

&lt;p&gt;You might be wondering, but they are accountants, that can’t happen. &lt;a href="https://www.readmargins.com/p/the-soc2-kabuki-theater" rel="noopener noreferrer"&gt;Can Duruk (good guy) who writes at Read Margins wrote about this part of the auditing process.&lt;/a&gt; But I am happy to add some color on how this may happen.&lt;/p&gt;

&lt;p&gt;First off, I want to say that Railway, myself, and our operations team takes the attestation process seriously. We take our customer workloads seriously.&lt;/p&gt;

&lt;p&gt;When we picked an auditor, we wanted the audit to be relatively adversarial. Meaning, we wanted to avoid a situation where the auditor is coaching us through the test. It’s common for an auditor to point out discrepancies in your reports and evidence, but we never wanted to pre-prep the packet and water down our footprint to make the firm happy.&lt;/p&gt;

&lt;p&gt;However, with the rise of this belief that now everyone needs SOC2, auditing firms have started pre-screening the packets, even sometimes offering the attestation report that you fill out FOR THEM, that they just hand waive.&lt;/p&gt;

&lt;p&gt;It’s an insult to the compliance process.&lt;/p&gt;

&lt;p&gt;For some perspective, FedRAMP is a true adversarial auditing process where you hire two auditing firms. One that works with you to prep the packet to certify that you are at the FedRAMP level you attest to, and then one that you can’t have any communication with until after the interview. &lt;em&gt;(I, for one, can’t wait when we begin this process.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don’t want the controls to get stricter, however, I don’t want what was previously a serious process to be taken less seriously all because three or four security vendors are making a business hoodwinking companies to hand over $12,000 a year plus a $12,000 for the audit, plus $20,000 for an external security review, on top of the amount of wasted engineering cycles spent taking screenshots and writing unread prose.&lt;/p&gt;

&lt;h1&gt;
  
  
  Protect Little Tech
&lt;/h1&gt;

&lt;p&gt;Look, I understand I know that there isn’t great optics being someone at Infrastructure company complaining about the SOC2 process. Nonetheless, I am more passionate about the journey that our customers take when they decide to leave a familiar job and create something new that needs to be sold.&lt;/p&gt;

&lt;p&gt;Many of those founders have enough to worry about, let alone the FUD that we have allowed this industry to push on them.&lt;/p&gt;

&lt;p&gt;My fear is that BigCompliance will then convince a new generation of buyers that they will need FedRAMP Level 5 to be a good vendor and we will erect moats and choke out innovation like never before.&lt;/p&gt;

&lt;p&gt;With that said, I do think we still should have a way to prove a software’s security and organizational footprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the bar, change the ramp
&lt;/h2&gt;

&lt;p&gt;The big challenge that I have with SOC2 is that it’s seen as a all or nothing gate. I strongly think that we need something between nothing and SOC2. If someone from the AICPA is listening- adopting a level system on SOC2 would be much appreciated where companies can start by building an attestation packet much sooner without the headache.&lt;/p&gt;

&lt;p&gt;Most companies, those who use Railway, or using a traditional cloud provider are likely covered by a significant amount of controls that their cloud host offers. Example, Railway offers built in DB backups and one click restore, that massively helps with explaining a company’s disaster recovery story to an attestation report. I feel like we can do much better by having a sharable “Trust Kit” that has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System diagram &amp;amp; data flows&lt;/li&gt;
&lt;li&gt;Sub-processors &amp;amp; shared‑responsibility mapping to cloud attestations&lt;/li&gt;
&lt;li&gt;Access control inventory (who has access to what infrastructure, when)&lt;/li&gt;
&lt;li&gt;Backup/Disaster Recovery summary with reproduction and fire-drills and test logs&lt;/li&gt;
&lt;li&gt;Vulnerability management&lt;/li&gt;
&lt;li&gt;Incident response run book &amp;amp; on‑call policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And- not that I can change the buyer I really wish that we move away from a world where we don’t require every sub‑vendor to be SOC 2 if their blast radius is small.&lt;/p&gt;

&lt;p&gt;However, any advice, as the compliance environment shifts, be mindful about how your company presents it’s self and always think about how you can mature the organization you are building.&lt;/p&gt;

&lt;p&gt;We’ll keep our own bar high (SOC 2 Type II done!; ISO 27001 on deck and FedRAMP… eventually), and we’ll champion the staged‑trust model with our vendors and customers.&lt;/p&gt;

&lt;p&gt;Security should be earned and demonstrated continuously, not purchased as a one‑time sticker.&lt;/p&gt;

&lt;p&gt;…and we’ll always have the back of the entrepreneur.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How We Oops-Proofed Infrastructure Deletion on Railway</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Thu, 28 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/how-we-oops-proofed-infrastructure-deletion-on-railway-14j7</link>
      <guid>https://dev.to/sarah-railway/how-we-oops-proofed-infrastructure-deletion-on-railway-14j7</guid>
      <description>&lt;p&gt;Author: Mahmoud Abdelwahab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2omuoj1p7upju7gx3i6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2omuoj1p7upju7gx3i6q.png" alt="How We Oops-Proofed Infrastructure Deletion on Railway" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve ever accidentally applied a Terraform or Kubernetes config that nuked production, you probably don’t even want to remember what it felt like. That split second when your terminal hangs, Slack blows up, automated alerts are triggered, and you realize you have just pulled the plug on your entire system is the kind of mistake that makes you double check every command for weeks afterward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83mlr65jjgrbhpeake0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83mlr65jjgrbhpeake0d.png" alt="Accidentally deleting production resources" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Accidentally deleting production resources&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The truth is, this isn’t a skill issue. It's the tools you use to interface with infrastructure that are to blame.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://railway.com/" rel="noopener noreferrer"&gt;Railway&lt;/a&gt;, rather than nuking all your resources right away, you get a 48-hour grace period where you can undo deletions. We shipped this behavior for project deletions and now they’re available for persistent volumes.&lt;/p&gt;

&lt;p&gt;You might just shrug and think, &lt;em&gt;“_nice.&lt;/em&gt;”_ But what looks like a simple feature on the surface actually hides a lot of complexity under the hood, especially when it involves actions connected to &lt;a href="https://blog.railway.com/p/data-center-build-part-two" rel="noopener noreferrer"&gt;real machines in a datacenter you operate&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Temporal and durable execution
&lt;/h3&gt;

&lt;p&gt;We use &lt;a href="https://temporal.io/" rel="noopener noreferrer"&gt;Temporal&lt;/a&gt; as our workflow engine, which allows us to build reliable and stateful background processes. It maintains a complete event history for each workflow, and makes it possible for business logic to be replayed, recovered, or paused at any point in time.&lt;/p&gt;

&lt;p&gt;If you’re new to Temporal, there are a few foundational concepts worth knowing: Workflows, Activities, and Signals.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://docs.temporal.io/workflows?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Temporal Workflow&lt;/a&gt; defines the orchestration logic of your application. It is composed of &lt;a href="https://docs.temporal.io/activities?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Activities&lt;/a&gt;, which are independent functions that typically perform side-effecting operations such as API calls, database writes, or long-running tasks. Because these Activities are prone to failure, Temporal provides built-in reliability features, such as automatic retries and the ability to run Activities for arbitrary durations without concern for process crashes or restarts.&lt;/p&gt;

&lt;p&gt;In addition to Workflows and Activities, Signals provide a way to send external input to a running Workflow. This makes it possible to adjust behavior or provide new data at runtime without restarting the Workflow. Signals are especially useful for scenarios like updating job parameters, canceling a task, or notifying the Workflow of an external event.&lt;/p&gt;

&lt;p&gt;Finally, Temporal ships with &lt;a href="https://docs.temporal.io/web-ui" rel="noopener noreferrer"&gt;a built-in web UI&lt;/a&gt; that allows you to inspect details of past and present Workflow Executions, which is useful for debugging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful9o0012npr7yecjcy71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful9o0012npr7yecjcy71.png" alt="Temporal web UI" width="800" height="690"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Temporal web UI&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Patching an environment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Processing changes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you deploy a &lt;a href="https://docs.railway.com/guides/staged-changes" rel="noopener noreferrer"&gt;staged change&lt;/a&gt; on Railway, the dashboard’s frontend sends a request to commit it as a patch. Patches applied to an environment can modify &lt;a href="https://docs.railway.com/reference/services" rel="noopener noreferrer"&gt;services&lt;/a&gt;, &lt;a href="https://docs.railway.com/reference/volumes" rel="noopener noreferrer"&gt;volumes&lt;/a&gt;, and &lt;a href="https://docs.railway.com/reference/variables" rel="noopener noreferrer"&gt;variables&lt;/a&gt;. In the case of volumes, several types of changes may occur, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resizing&lt;/li&gt;
&lt;li&gt;Mounting / unmounting&lt;/li&gt;
&lt;li&gt;Configuring usage alerts&lt;/li&gt;
&lt;li&gt;Deleting a volume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the server, the handler first performs authorization and safety checks. After loading the currently staged patch for the target environment and fetching the environment’s current configuration, it verifies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user is allowed to access the environment&lt;/li&gt;
&lt;li&gt;If the change is destructive, the user must be an admin and complete 2FA (if configured) before proceeding&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If those checks pass, the handler invokes a &lt;code&gt;commitPatch&lt;/code&gt; backend controller to finalize the operation. Here’s what it looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const commitPatch = async (
  ctx: RailwayContext,
  {
    patch,            
    skipDeploys,      
    commitMessage,    
    appliedByUser,    
  }: {
    patch: EnvironmentPatch &amp;amp; {
      environment: Environment; 
      project: Project;         
    };
    skipDeploys?: boolean | null; 
    commitMessage?: string;       
    appliedByUser?: User | null;  
  },
) =&amp;gt; {
  const temporalClient = await getTemporalClient();

  // Start a workflow with a signal (commit patch to environment workflow)
  const handle = await temporalClient.signalWithStart(
    commitPatchToEnvironment,
    {
      signal: stagedChangesSignal, // Signal to apply staged changes
      args: [
        {
          environment: patch.environment,       
          patchId: patch.id,                   
          user: appliedByUser ?? ctx.user,     
          commitMessage,                       
          skipAllDeploys: skipDeploys ?? false,
        },
      ],
      taskQueue: TASK_QUEUES.backboardEnvironments,
      workflowId: commitPatchToEnvironmentWorkflowId({
        environmentId: patch.environment.id, 
        patchId: patch.id,                  
      }),
      workflowExecutionTimeout: "2h", 
      searchAttributes: customSearchAttributes({
        projectIds: patch.projectId,                   
        userIds: appliedByUser?.id ?? ctx.user?.id,
      }),
    },
  );

  // Trigger event firing for this patch
  await fireEventsForPatch(ctx, { patch });

  // Return workflow info (useful for tracking workflow state externally)
  return { workflowId: handle.workflowId, handle };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This controller starts a new &lt;code&gt;commitPatchToEnvironment&lt;/code&gt; workflow and sends an initial signal to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Committing a patch to an environment workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;commitPatchToEnvironment&lt;/code&gt; workflow includes several Temporal Activities, one of which is responsible for triggering a delayed volume deletion workflow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const triggerDeleteVolumeInstances = async (ctx, { volumeId, environmentId, user, patchId, tombstone, delayDeletion }) =&amp;gt; {
  // Immediate deletion if delay not requested or info missing
  if (!delayDeletion || !user || !patchId) {
    return await executeDeleteVolumeInstances({ volumeId, environmentId, tombstone });
  }

  // Lookup active volume instance
  const volumeInstance = await ctx.db.volumeInstance.findFirst({
    where: { volumeId, environmentId, deletedAt: null },
  });

  if (!volumeInstance) throw new NotFoundError("VolumeInstance");

  // Start delayed deletion workflow
  const temporal = await getTemporalClient();
  const workflowId = delayedDeleteVolumeInstanceWorkflowId(volumeInstance.id);
  await temporal.signalWithStart(delayedDeleteVolumeInstanceWorkflow, {
    signal: delayedDeleteVolumeInstanceSignal,
    signalArgs: [{ action: "DELAYED_DELETION", userId: user.id }],
    workflowId,
    args: [{ volumeInstanceId: volumeInstance.id, tombstone, patchId, initialUserId: user.id }],
    taskQueue: TASK_QUEUES.backboardEnvironments,
  });

  return workflowId;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;triggerDeleteVolumeInstances&lt;/code&gt; function deletes a volume instance either immediately or in a delayed manner depending on the input arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the &lt;code&gt;delayDeletion&lt;/code&gt; flag is false (or if required fields like &lt;code&gt;user&lt;/code&gt; or &lt;code&gt;patchId&lt;/code&gt; are missing), it performs an immediate deletion&lt;/li&gt;
&lt;li&gt;Otherwise, it fetches the target volume instance from the database and uses Temporal to start or signal the &lt;code&gt;delayedDeleteVolumeInstanceWorkflow&lt;/code&gt; (via a unique workflow ID) that schedules the deletion for later, recording the initiating user and patch information—this allows the system to support both direct cleanup and orchestrated, trackable delayed deletions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scheduling volume deletion
&lt;/h3&gt;

&lt;p&gt;Here’s a high-level overview of what the &lt;code&gt;delayedDeleteVolumeInstanceWorkflow&lt;/code&gt; workflow does&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flk2f4nk57oxna76lqjtp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flk2f4nk57oxna76lqjtp.png" alt="Delay volume deletion workflow" width="800" height="998"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Delay volume deletion workflow&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a simplified example of what the &lt;code&gt;delayedDeleteVolumeInstanceWorkflow&lt;/code&gt; looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// simplified example
export async function delayedDeleteVolumeInstanceWorkflow({
  volumeInstanceId,
  tombstone,
  patchId,
  initialUserId,
}: {
  volumeInstanceId: string
  tombstone?: boolean
  patchId: string
  initialUserId: string
}) {
  // Default to delayed deletion
  let action = "DELAYED_DELETION";
  let userId = initialUserId;

  // Make volume searchable by attributes
  await upsertVolumeSearchAttributes({ volumeId: volumeInstanceId, userId });

  // Compute when deletion should occur
  const deleteAt = new Date(Date.now() + VOLUME_DELETE_DELAY_MS);

  try {
    // Mark the volume instance with scheduled deletedAt timestamp
    const volumeInstance = await updateDeletedAt({ volumeInstanceId, deletedAt: deleteAt });

    // Notify admins about the scheduled deletion
    await notifyScheduledDeletion({ volumeInstanceId, patchId });

    // Allow external signals to cancel or override the deletion
    wf.setHandler(delayedDeleteVolumeInstanceSignal, (s) =&amp;gt; {
      action = s.action;
      userId = s.userId;
    });

    // Wait until cancellation/override OR until the grace delay expires
    await wf.condition(() =&amp;gt; action !== "DELAYED_DELETION", VOLUME_DELETE_DELAY_MS);

    // If deletion is canceled: restore the volume and exit early
    if (action === "CANCEL_DELETION") {
      await updateDeletedAt({ volumeInstanceId, deletedAt: null });
      await restoreVolumeInstance({ volumeId: vi.volumeId, environmentId: vi.environmentId, userId });
      return;
    }

    // Otherwise, proceed with permanent deletion via child workflow
    await wf.executeChild(deleteVolumeInstances, {
      args: [{ volumeId: vi.volumeId, environmentId: vi.environmentId, tombstone }],
      workflowExecutionTimeout: "1h", // safeguard timeout
    });
  } catch (err) {
    // On failure: reset state and report error
    await wf.CancellationScope.nonCancellable(async () =&amp;gt; {
      await updateDeletedAt({ volumeInstanceId, deletedAt: null });
      await reportFailure({ volumeInstanceId, error: err, ...wf.workflowInfo() });
    });
    throw err;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initialize State&lt;/strong&gt; – Default the action to &lt;code&gt;DELAYED_DELETION&lt;/code&gt;, and record the &lt;code&gt;initialUserId&lt;/code&gt; for attribution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attach Metadata&lt;/strong&gt; – Record searchable workflow attributes (&lt;code&gt;volumeId&lt;/code&gt;, &lt;code&gt;userId&lt;/code&gt;) so the deletion can be tracked and queried later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Deletion&lt;/strong&gt; – Calculate a future timestamp (&lt;code&gt;deleteAt&lt;/code&gt;) when the volume will be eligible for deletion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mark for Deletion&lt;/strong&gt; – Update the database record with the &lt;code&gt;deletedAt&lt;/code&gt; value, signaling that the volume is pending deletion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notify Admins&lt;/strong&gt; – Send an email alert so administrators are aware of the scheduled deletion and can intervene if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Register Signal Handler&lt;/strong&gt; – Listen for external signals that may cancel or override the deletion request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait for Condition or Timeout&lt;/strong&gt; – Pause until either a cancellation signal arrives or the delay window expires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle Cancellation&lt;/strong&gt; – If deletion is canceled, clear the &lt;code&gt;deletedAt&lt;/code&gt; field, create a restore patch, and exit the workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proceed with Deletion&lt;/strong&gt; – If no cancellation occurs, launch a child workflow to perform the permanent deletion under a strict timeout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt; – On failure, reset the deletion state, send a failure notification with workflow details, and propagate the error.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What happens at the infrastructure level
&lt;/h3&gt;

&lt;p&gt;Once the 48-hour grace period expires, the system moves from orchestration to the actual teardown of infrastructure. This process happens in two main phases: Infrastructure Cleanup and Final Cleanup.&lt;/p&gt;

&lt;p&gt;Both are driven by Temporal workflows that coordinate database state, routers, and compute hosts, ensuring that deletions are safe, observable, and consistent across all layers of the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Infrastructure Cleanup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the grace window ends, the &lt;code&gt;delayedDeleteVolumeInstanceWorkflow&lt;/code&gt; spawns a child workflow that performs the actual deletion of the volume. We construct the arguments for the teardown and run the workflow with a strict timeout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Simplified logic
await wf.executeChild(deleteVolumeInstances, {
  args: [{ volumeId, environmentId, tombstone }],
  workflowExecutionTimeout: "1h",
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child workflow iterates over all matching volume instances, deleting them one by one. This isolates errors, allows retries per instance, and provides granular observability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// simplified logic
export async function deleteVolumeInstances({ volumeId, environmentId, tombstone }) {
  for (const volumeInstance of volumeInstances) {
    await volumeActivities().deleteVolumeInstance({ volumeInstanceId: volumeInstance.id, tombstone });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each instance follows the same lifecycle: mark state, detach services, remove from infrastructure, and finally clean up in the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const deleteVolumeInstanceById = async (ctx, { volumeInstanceId, tombstone }) =&amp;gt; {
  // Mark schedules and state
  // Detach deployments
  // Remove from infrastructure
  // Cleanup in database
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router resolves the appropriate compute host node, instructs it to delete, and then tidies its own caches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (c *Controller) RemoveVolumeInstance(ctx context.Context, req *Request) (*Response, error) {
    // Resolve compute host
    // Request deletion
    // Update store
    return &amp;amp;Response{}, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the compute host performs the physical destruction using ZFS with a recursive destroy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (g *Gateway) RemoveVolumeInstance(ctx context.Context, volumeID string) error {
    // Run zfs destroy -r
    // Update counters and orchestrator
    return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Final Cleanup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the infrastructure reports success, the system cleans up logical state in the database.&lt;/p&gt;

&lt;p&gt;For volume instances, we either tombstone (soft-delete) with a timestamp and unique mount path, or hard-delete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const deleteVolumeInstanceInDatabase = async (ctx, { volumeInstanceId, tombstone }) =&amp;gt; {
  if (tombstone) {
    // Mark deleted with timestamp and state
  } else {
    // Hard delete
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parent volume record is also cleaned up with the same tombstone or hard-delete semantics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const deleteVolumeById = async (ctx, { volumeId, tombstone }) =&amp;gt; {
  if (tombstone) {
    // Mark deleted with timestamp and name change
  } else {
    // Hard delete
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any backup schedules or deployments tied to the volume are finalized, and finally, orchestrator updates ensure all distributed systems converge on the same state.&lt;/p&gt;

&lt;p&gt;By the end of this process, the volume has been torn down across every layer: backups stopped, deployments detached, bytes destroyed on disk, and records reconciled in the database. This guarantees that once the grace period passes, deletion is thorough, consistent, and leaves no dangling resources behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By giving volumes a grace period before they disappear forever, we’re making infrastructure a little more forgiving, and a lot less stressful. Mistakes can always happen, but our goal is to make sure they don’t turn into disasters. Whether it’s a late-night deploy, a misclick, or simply a change of heart, you now have the safety net to undo it.&lt;/p&gt;

&lt;p&gt;If solving hard problems, shaping resilient infrastructure, and making life easier for developers sounds like your kind of fun, &lt;a href="https://railway.com/careers" rel="noopener noreferrer"&gt;we’re hiring&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why We’re Moving on From Nix</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Mon, 25 Aug 2025 17:13:11 +0000</pubDate>
      <link>https://dev.to/sarah-railway/why-were-moving-on-from-nix-3cb1</link>
      <guid>https://dev.to/sarah-railway/why-were-moving-on-from-nix-3cb1</guid>
      <description>&lt;p&gt;&lt;em&gt;Author: Jake Runzer&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We've released &lt;a href="https://railpack.com/" rel="noopener noreferrer"&gt;Railpack&lt;/a&gt; — the next iteration of the Railway builder, developed from the ground up and based on everything we’ve learned from building over 14 million apps with &lt;a href="https://nixpacks.com/" rel="noopener noreferrer"&gt;Nixpacks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We first announced Nixpacks nearly 3 years ago and it quickly became the default way to build images from user code on Railway. While Nixpacks works great for 80% of users, that still left us with 200k Railway users who might encounter limitations daily. &lt;/p&gt;

&lt;p&gt;It became clear we needed a major builder upgrade to scale our user base from 1M to 100M.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tqp137jdg1yiwcjap3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tqp137jdg1yiwcjap3u.png" alt="Cumulative builds with Nixpacks over time" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the highlights of Railpack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Granular Versioning&lt;/strong&gt;: Support for &lt;code&gt;major.minor.patch&lt;/code&gt; versions of packages (instead of Nix’s approximate versions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller Builds&lt;/strong&gt;: We’ve been able to reduce image sizes between 38% (Node) and 77% (Python), enabling faster deploys on Railway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better caching&lt;/strong&gt;: Railpack interfaces directly with &lt;a href="https://github.com/moby/buildkit" rel="noopener noreferrer"&gt;BuildKit&lt;/a&gt; to control the layers and filesystem, resulting in more cache hits (with sharable caches across environments)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can opt-in to using Railpack for your builds today. It is already powering builds for &lt;a href="http://railway.com?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;railway.com&lt;/a&gt; and &lt;a href="https://station.railway.com?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;central station&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our problems with Nix
&lt;/h3&gt;

&lt;p&gt;The biggest problem with Nix is its commit-based package versioning. Only the latest major version of each package is available, with versions tied to specific commits in the &lt;a href="https://github.com/NixOS/nixpkgs" rel="noopener noreferrer"&gt;nixpkgs repo&lt;/a&gt;. We tried to support every patch version, but it looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;AVAILABLE_SWIFT_VERSIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"c82b46413401efa740a0b994f52e9903a4f6dcd5"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.4.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"c82b46413401efa740a0b994f52e9903a4f6dcd5"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.5.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"7592790b9e02f7f99ddcb1bd33fd44ff8df6a9a7"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.5.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"7cf5ccf1cdb2ba5f08f0ac29fc3d04b0b59a07e4"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.6.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3c3b3ab88a34ff8026fc69cb78febb9ec9aedb16"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.7.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"8cad3dbe48029cb9def5cdb2409a6c80d3acfe2e"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"9957cd48326fe8dbd52fdc50dd2502307f188b0d"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach isn’t clear or maintainable, especially for contributors unfamiliar with Nix’s version management.&lt;/p&gt;

&lt;p&gt;For languages like Node and Python, we ended up only supporting their latest major version. &lt;/p&gt;

&lt;p&gt;But even this was problematic because versions are tied to a single commit SHA. Updating the commit hash to support the latest version of a package meant all other package versions would also update. If a default version changed, there was a high likelihood that a user's build would suddenly fail with unexpected errors. &lt;/p&gt;

&lt;p&gt;We feel bad when users can't access the latest packages, but feel worse when previously functional builds suddenly fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image sizes and caching
&lt;/h3&gt;

&lt;p&gt;The way Nixpacks uses Nix to pull in dependencies often results in massive image sizes with a single &lt;code&gt;/nix/store&lt;/code&gt; layer ... all Nix and related packages and libraries needed for &lt;em&gt;both&lt;/em&gt; the build and runtime are here. &lt;/p&gt;

&lt;p&gt;With no way of splitting up the Nix dependencies into separate layers, there was not much we could do to reduce the final image sizes. Not a problem with Nix per se but certainly a problem with how we were using it.&lt;/p&gt;

&lt;p&gt;Caching was also problematic as we had little control over when layer caches were invalidated. &lt;/p&gt;

&lt;p&gt;Railway injects a deployment ID environment variable into all builds. This means that any layers that run after these variables are added to the Dockerfile are always invalidated and can never be cached.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3p6a4glyf8tct1254lp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3p6a4glyf8tct1254lp.png" alt="Result of running  raw `dive` endraw  on a simple python uv app" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to be clear, we don’t have any problem with Nix itself. But there is a problem with how we were using it. Trying to abstract all the parts of Nix that make Nix… Nix, just fundamentally doesn't work. &lt;/p&gt;

&lt;p&gt;We don’t want our users to have to understand what a derivation is or why Node 22.14.0 is available on archive version &lt;code&gt;757d2836919966eef06ed5c4af0647a6f2c297f4&lt;/code&gt; of the unstable channel.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introducing Railpack
&lt;/h1&gt;

&lt;p&gt;To fix the issues we’ve had with Nixpacks, we built Railpack. &lt;/p&gt;

&lt;p&gt;Since we transitioned away from Nix, we also transitioned away from the name Nixpacks in favor of Railpack. We also changed the codebase from Rust to Go because of the Buildkit libraries. &lt;/p&gt;

&lt;p&gt;Here are some architectural highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We generate a custom &lt;a href="https://docs.docker.com/build/buildkit/frontend/" rel="noopener noreferrer"&gt;BuildKit LLB&lt;/a&gt; + &lt;a href="https://docs.docker.com/build/buildkit/frontend/" rel="noopener noreferrer"&gt;Frontend&lt;/a&gt; to give us much more control over how the final image is constructed — resulting in 38% smaller base Node and 77% smaller base Python images compared to building with Nixpacks&lt;/li&gt;
&lt;li&gt;We use &lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;Mise&lt;/a&gt; for version resolution and most package installation, though it leaves room to support other executable sources in the future&lt;/li&gt;
&lt;li&gt;We're now able to &lt;em&gt;lock&lt;/em&gt; the dependencies used when a successful build happens. This means that builds won’t break when we update the default Node version from 22 to 24&lt;/li&gt;
&lt;li&gt;We improved secret environment variable management. Railpack leverages &lt;a href="https://docs.docker.com/build/building/secrets/" rel="noopener noreferrer"&gt;BuildKit secrets&lt;/a&gt; to prevent variables from appearing in build logs or the final image&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The Railpack process is split into three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Analyze&lt;/strong&gt;: Look at the code and determine what packages should be installed, what commands should be run, and what the start command should be&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan&lt;/strong&gt;: Create a build plan in a JSON-serializable format that contains several steps, each with inputs derived from other steps or entire images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generates&lt;/strong&gt;: Construct a BuildKit build graph based on the inputs and outputs from the plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fww1q6fu87ipf43eyuw55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fww1q6fu87ipf43eyuw55.png" alt="Screenshot" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While Dockerfiles are very linear in nature, BuildKit graphs are extremely parallel. Each command runs in its own stage of a multi-stage build and provide precise control over the input layers and how the final file system is assembled. &lt;/p&gt;

&lt;p&gt;Railpack analyzes the code and generates a build plan of all the necessary steps needed to build. &lt;/p&gt;

&lt;p&gt;Each step specifically defines what previous step or image is required — a format that is much lower-level than what was used in Nixpacks. This plan is then turned into a graph in LLB format and solved. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ra4vd53v576ci9e267q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ra4vd53v576ci9e267q.png" alt="Flow screenshot" width="800" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BuildKit starts at the end and works backwards, pulling from the cache if possible or running the commands to resolve each requested layer.&lt;/p&gt;

&lt;p&gt;To invalidate layers when specific environment variables change, Railpack will hash the used variable values and mount a file with the hash to an input filesystem. If the code and used variables don’t change, the layer cache will be hit.&lt;/p&gt;

&lt;p&gt;Railpack can therefore fully define how an image is made.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvno7jog1mr059lmkjg64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvno7jog1mr059lmkjg64.png" alt="Deploy inputs for a static Vite build" width="800" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What else does Railpack unlock? We're glad you asked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support for building and deploying Vite, Astro, CRA, and Angular static sites with zero config&lt;/li&gt;
&lt;li&gt;Tight integration between your builds and the Railway UI&lt;/li&gt;
&lt;li&gt;Support for the latest versions of languages with no Railpack release necessary&lt;/li&gt;
&lt;li&gt;Optimized layer caching for a project across environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How you can use it today
&lt;/h1&gt;

&lt;p&gt;Railpack is available in Beta today. Just enable it in your service settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9emv77wy16t9efriy0r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9emv77wy16t9efriy0r.png" alt="Deploy app in Railway" width="639" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It currently supports Node, Python, Go, Php, and Static HTML deployments, including out-of-the-box support for Vite, Astro, CRA, and Angular static sites, making Railway the easiest place to deploy both your frontend and backend. &lt;/p&gt;

&lt;p&gt;We are adding more framework and language support actively, so let us know in &lt;a href="https://station.railway.com/feedback/feedback-railpack-409fc7d5?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Help Station&lt;/a&gt; what you want to see first. We are prioritizing depth on the more commonly used languages rather than breadth, at least until the core API and abstraction are nailed.&lt;/p&gt;

&lt;p&gt;Railpack is also open source with documentation available at &lt;a href="http://railpack.com" rel="noopener noreferrer"&gt;railpack.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>javascript</category>
      <category>react</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Railway MCP - Stateful, Serverful, Pay-per-use Infrastructure</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Wed, 20 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarah-railway/railway-mcp-stateful-serverful-pay-per-use-infrastructure-5b2</link>
      <guid>https://dev.to/sarah-railway/railway-mcp-stateful-serverful-pay-per-use-infrastructure-5b2</guid>
      <description>&lt;p&gt;Author: Mahmoud Abdelwahab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7obe14zfdpinfo1dg2ic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7obe14zfdpinfo1dg2ic.png" alt="Railway MCP - Stateful, Serverful, Pay-per-use Infrastructure" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, yes we know it can feel like “Just one more MCP server, bro. I swear this one’s different…” But in all honesty, we think you’ll like what the Railway MCP server can do. &lt;/p&gt;

&lt;p&gt;Beyond the 0 → 1 experience, the MCP server offers a bunch of tools that coding agents can use to iterate on existing projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deploy&lt;/code&gt; - Deploy a service. This tool can be called more than once so coding agents can continuously apply changes. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deploy-template&lt;/code&gt; - Deploy a template from the &lt;a href="https://railway.com/deploy" rel="noopener noreferrer"&gt;Railway Template Library&lt;/a&gt;. This makes it possible to deploy arbitrarily complex collections of services and databases.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create-environment&lt;/code&gt; and &lt;code&gt;link-environment&lt;/code&gt; for working with environments. Great for ensuring that coding agents are working in an isolated environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list-variables&lt;/code&gt; and &lt;code&gt;set-variables&lt;/code&gt; for configuring and pulling variables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get-logs&lt;/code&gt; - Retrieve build or deployment logs for a service. Useful for having coding agents debug deployed services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the complete list of tools as well as detailed setup instructions in the &lt;a href="https://github.com/railwayapp/railway-mcp-server" rel="noopener noreferrer"&gt;project’s README on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In most cases, using MCP to manage infrastructure doesn’t really make sense. Infrastructure is typically complex, hard to automate, and with most providers you end up paying for resources regardless of your usage.&lt;/p&gt;

&lt;p&gt;With Railway you can one-shot your infra and only pay for what you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Railway as the ideal deployment target for agents
&lt;/h2&gt;

&lt;p&gt;Agents need deployment targets that are reliable, scalable, and cost-efficient. Railway checks all of these boxes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing and autoscaling
&lt;/h3&gt;

&lt;p&gt;If an agent spins up resources that go idle shortly after, you don’t get stuck with a big bill. On Railway you only pay for active compute time and resources you actually use. This makes the platform ideal for for experimentation and fast iteration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe84vdhu65fhwgga67ujg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe84vdhu65fhwgga67ujg.png" alt="Railway’s usage-based pricing" width="800" height="466"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Railway’s usage-based pricing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Additionally, all deployed services on Railway support vertical autoscaling out-of-the-box. So you don’t need to pick an instance size and pay a fixed monthly fee that doesn’t take your usage into account.&lt;/p&gt;
&lt;h3&gt;
  
  
  Environments
&lt;/h3&gt;

&lt;p&gt;Railway enables you to spin up isolated environments. This means that coding agents can make changes to deployed resources without affecting resources in other environments. You can run multiple agents in parallel and give each one their own environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhps9ngkgiq9xou71xofq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhps9ngkgiq9xou71xofq.png" alt="Environments on Railway" width="800" height="494"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Environments on Railway&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus: Design decisions we made
&lt;/h2&gt;

&lt;p&gt;We made a few design decisions along the way when building the Railway MCP Server. None of them are set in stone, but we thought it would be useful to share our reasoning and the trade-offs that led us here.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;No destructive actions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This one is the most obvious one. If there are no &lt;code&gt;delete-x&lt;/code&gt; MCP tools, the odds of the coding agent running a destructive action goes down significantly. This way, you avoid running into this situation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3e6l62cplsyv5hc5roxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3e6l62cplsyv5hc5roxk.png" alt="Coding agent deciding to nuke a database" width="800" height="952"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Coding agent deciding to nuke a database&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, coding agents can still run arbitrary CLI commands, so you should be careful.&lt;/p&gt;
&lt;h3&gt;
  
  
  Local MCP
&lt;/h3&gt;

&lt;p&gt;MCP has a transport layer responsible for how clients and servers talk to each other and how authentication is handled. It takes care of setting up connections, framing messages, and making sure communication between MCP participants is secure.&lt;/p&gt;

&lt;p&gt;MCP currently supports two types of transport:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stdio transport&lt;/strong&gt; : This uses standard input and output streams for communication between local processes on the same machine. It’s the fastest option since there’s no network overhead, which makes it ideal when everything is running locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamable HTTP transport&lt;/strong&gt; : This uses HTTP POST for sending messages from client to server, with optional Server-Sent Events for streaming responses. It’s what enables remote servers to work and supports common HTTP authentication methods like bearer tokens, API keys, and custom headers. MCP recommends OAuth as the way to obtain these tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remote MCP servers make a lot of sense in the broader vision of MCP. In that world, any AI tool could act as a host, connect to multiple remote MCP servers, and pick the right tool for the job.&lt;/p&gt;

&lt;p&gt;For Railway, though, most of our users are developers working inside editors like VS Code, Cursor, or Claude Code. In that context, a remote MCP server doesn’t bring much benefit.&lt;/p&gt;

&lt;p&gt;Another limitation is authentication. Since Railway doesn’t yet support OAuth, the only way to connect to a remote MCP server would be to hardcode API tokens. That means going to the Railway dashboard, generating an API key in your account settings, and then manually adding it to your MCP config file. Not exactly a great experience.&lt;/p&gt;

&lt;p&gt;We also haven’t come across a real use case where an MCP host only works with remote servers, nor have users asked us to integrate Railway that way. So instead, we went with a local MCP server. The Railway CLI already offers a seamless authentication flow, so setup is as simple as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.railway.com/guides/cli" rel="noopener noreferrer"&gt;Install the CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;railway login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/railwayapp/railway-mcp-server" rel="noopener noreferrer"&gt;Install the MCP server&lt;/a&gt;&lt;a href="https://github.com/railwayapp/railway-mcp-server" rel="noopener noreferrer"&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There’s also a nice side effect of using the CLI as a dependency. If something breaks or the agent hits an edge case, it can fall back to the same workflows a developer would use manually. Rather than getting stuck, it just calls the CLI, which makes the system more resilient and avoids frustrating dead ends.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Using the Railway CLI under the hood&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Under the hood, the MCP server runs CLI commands. This approach helped us spot gaps in the experience of integrating with the CLI programmatically, which gives us valuable feedback for improving it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { exec } from "node:child_process";
import { promisify } from "node:util";
import { analyzeRailwayError } from "./error-handling";

const execAsync = promisify(exec);

export const runRailwayCommand = async (command: string, cwd?: string) =&amp;gt; {
    const { stdout, stderr } = await execAsync(command, { cwd });
    return { stdout, stderr, output: stdout + stderr };
};

export const checkRailwayCliStatus = async (): Promise&amp;lt;void&amp;gt; =&amp;gt; {
    try {
        await runRailwayCommand("railway --version");
        await runRailwayCommand("railway whoami");
    } catch (error: unknown) {
        return analyzeRailwayError(error, "railway whoami");
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We’d love to hear how you’re using the Railway MCP server and what improvements you’d like to see. Share your feedback with us &lt;a href="https://station.railway.com/feedback/model-context-protocol-for-railway-railw-c040b796" rel="noopener noreferrer"&gt;on Central Station&lt;/a&gt; and help us shape future versions. And if you’re building an agent platform and want to use Railway to power the underlying infrastructure, &lt;a href="https://railway.com/pricing#enterprise-calendar-embed" rel="noopener noreferrer"&gt;we’d love to chat&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>ai</category>
    </item>
    <item>
      <title>Zero-Touch Bare Metal at Scale</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Tue, 19 Aug 2025 16:53:56 +0000</pubDate>
      <link>https://dev.to/sarah-railway/zero-touch-bare-metal-at-scale-56e4</link>
      <guid>https://dev.to/sarah-railway/zero-touch-bare-metal-at-scale-56e4</guid>
      <description>&lt;p&gt;&lt;em&gt;Author: Charith Amarasinghe&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We’ve all gotten used to clicking a button and getting a Linux machine running in the cloud. But when you’re building your own cloud, you’ve got to build the button first. &lt;/p&gt;

&lt;p&gt;Lately we’ve been &lt;a href="https://blog.railway.com/p/data-center-build-part-one?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;writing&lt;/a&gt; about building out our &lt;a href="https://blog.railway.com/p/launch-week-02-welcome?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Metal infrastructure&lt;/a&gt; one rack at a time. &lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://blog.railway.com/p/data-center-build-part-one?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;last blog&lt;/a&gt;, we spoke about the trials of building out the physical infrastructure. In this episode, we talk about how we operationalize the hardware once it’s installed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sorting your LEGO pieces
&lt;/h1&gt;

&lt;p&gt;You’ve built your dream server with dual-redundant NICs and multiple redundant NVMe drives for resilience. You’ve ordered 100 units and got them all racked up and wired to your detailed diagrams. You go to the DC with your USB stick and reboot into your favorite Linux distro’s installer only to be greeted by the “Choose your Network Interface” screen with a dozen or so incomprehensible interface names. &lt;/p&gt;

&lt;p&gt;Herein lies our first hurdle — how do you map the physical arrangement of hardware to what your operating system sees? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvh0vjxqx0iuf1seum2n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvh0vjxqx0iuf1seum2n.png" alt="A Supermicro server with 12 NVMe Bays - Guess which bay is /dev/nvme0n2 as seen by Linux? (That’s a trick question)." width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 When buying build-to-order servers, it’s essential to include instructions specifying exactly where each Network Card or NVMe Drive should get installed. Otherwise you might end up with multiple different configurations across different orders.&lt;/p&gt;

&lt;p&gt;It helps first to take a step back and discuss how Linux names devices.&lt;/p&gt;

&lt;p&gt;When a host boots Linux, the OS enumerates the attached hardware. Most commonly, devices are attached to the PCIe bus and Linux begins enumerating these according to the hierarchical structure of the bus. When Linux encounters a device during this traversal, the &lt;a href="https://en.wikipedia.org/wiki/Udev" rel="noopener noreferrer"&gt;udev&lt;/a&gt; daemon will get an event and associate a number of identifiers with the device - it’ll then use these identifiers to formulate a name which it then assigns to the device nodes it creates in &lt;code&gt;/dev&lt;/code&gt; and elsewhere.&lt;/p&gt;

&lt;p&gt;The consequence of this approach is that device names can be very unstable, especially if the hardware layout changes between boots or if the enumeration order is non-deterministic. If you used Linux in the olden days, you’d know the pain of plugging in a new PCIe card and booting only to figure out that your networking broke. Despite the many critiques that could be leveled against SystemD, it does succeed in &lt;a href="https://github.com/systemd/systemd/blob/main/docs/PREDICTABLE_INTERFACE_NAMES.md" rel="noopener noreferrer"&gt;addressing these problems for network interfaces since v197&lt;/a&gt;. But storage device naming is still a crapshoot and better achieved by device serial number.&lt;/p&gt;

&lt;p&gt;Our approach to addressing this unpredictability is to lean on Redfish - a HTTP API for Board Management Controllers (BMCs) attached to server motherboards. Redfish APIs can enumerate the hardware on a board, detailing PCIe cards, NVMe drives, their serial numbers and/or MAC addresses and their physical locations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyewj4zvq1n5n9kqceung.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyewj4zvq1n5n9kqceung.png" alt="An Extract of a Redfish System object scraped from one of our storage nodes" width="800" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our very first step once a rack is installed is to build a CSV of identifiers for the equipment in the rack - hostname, BMC MAC address, BMC password, and a few other details. We then push this data via gRPC to an internal control plane called MetalCP. &lt;/p&gt;

&lt;p&gt;MetalCP runs a Temporal worker which implements a Host Import workflow. For each server, we then kick off a workflow that runs through the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Match the device to its representation in our internal DCIM tool (Railyard) &lt;/li&gt;
&lt;li&gt;Connect to the datacenter's management network via Tailscale&lt;/li&gt;
&lt;li&gt;Connect to the management router at the datacenter and identify the DHCP lease assigned to the BMC (via its MAC)&lt;/li&gt;
&lt;li&gt;Connect to the BMC of the server via this IP and scrape all available data&lt;/li&gt;
&lt;li&gt;Create an internal Protobuf representation of the hardware layout&lt;/li&gt;
&lt;li&gt;Create static DHCP leases for the BMC and for the management NIC on the host using discovered MAC addresses from the scrape&lt;/li&gt;
&lt;li&gt;Update a DB with all the details about the server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An import workflow takes less that a minute to complete in most cases and Temporal ensures recovery from any transient failures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f5np3isgwxin6bts361.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f5np3isgwxin6bts361.png" alt="The timeline of a Host Import workflow" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The database record that is stored by a host workflow contains all the information you could want about a server. We generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of Network Interface Cards, their Physical Location (Slot), and MAC addresses for each Port&lt;/li&gt;
&lt;li&gt;A list of NVMe Drives, their Serial Numbers, Model Numbers, and Physical Slot IDs&lt;/li&gt;
&lt;li&gt;System stats such as CPU core counts, RAM size, and hardware identifiers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We then match this hardware specification against a list of known configurations. These configurations encode details such as network interface names assigned to specific PCIe slots and NVMe drive bay identifiers. A hardware configuration is as simple as a set of conditionals in Golang that match the key distinguishing factors of a specific type of server, and a config object containing stable interface and drive names.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpxrpz1skhvbrkwibmvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpxrpz1skhvbrkwibmvi.png" alt="An example hardware identification function, pb.SystemInfo is an encoding of raw data we’ve scraped from Redfish" width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A custom plugin exposes this Hardware Config object to Ansible, allowing us to reference NVMe disks and network interface names with Jinja template expressions. For example, a NVMe drive in Bay 0 can be uniquely addressed as &lt;code&gt;/dev/disks/by-id/nvme-{{ drive_bays.DiskBay0.device_model }}_{{ drive_bays.DiskBay0.serial_number }}&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;This approach lets us build config in any shape we want without leaving anything to chance. The import workflow also flags faults in the hardware; if a server is not reporting a NIC or a DIMM of RAM, the workflow will fail since the hardware won’t match a known configuration. We’ve thus far identified servers with faulty RAM and servers with NICs installed in the wrong slots through this mechanism.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlz3lyqdiy51lmen7k28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlz3lyqdiy51lmen7k28.png" alt="Configuration" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configuration is one step, but getting at Ansible still needs an OS to get installed. So how do we one-click ourselves out of installing Linux in the first place? The answer involves a pinch of AI 🪄.&lt;/p&gt;

&lt;h1&gt;
  
  
  Who needs webhooks when we’ve got Claude
&lt;/h1&gt;

&lt;p&gt;When we first started provisioning servers, we did it manually with 20 Web KVM tabs in Chrome and manually interacting with debian-installer. Over time we’ve evolved to use less and less human intervention.&lt;/p&gt;

&lt;p&gt;The Debian Installer can network boot from PXE, and &lt;a href="https://github.com/danderson/netboot/tree/main/pixiecore" rel="noopener noreferrer"&gt;Pixiecore from Dave Anderson&lt;/a&gt; can wrap all the PXE complexity in a few HTTP calls. We use MetalCP as a backend to Pixicore and return a simple JSON payload describing the netboot kernel, initramfs, and kernel command-line. Debian can accept a pre-seed file over HTTP if networking is configured. &lt;/p&gt;

&lt;p&gt;We use our knowledge of the PXE booting servers MAC address, plus the system info we’ve scraped from Redfish, to create a kernel command-line and preseed file tailored to the booting machine. &lt;/p&gt;

&lt;p&gt;These are all exposed as HTTP APIs proxied by Pixiecore to the PXE booting machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mqdnpjs1k4cp0hk4vp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mqdnpjs1k4cp0hk4vp0.png" alt="The function that generates our PXE boot response" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Getting a host to a PXE bootable state requires us to reboot the server, but we don’t want this reboot action to happen on a server that may be running user code. To achieve this, we implement a logical state machine for each host in the provisioning process and orchestrate the OS install via another Temporal workflow. &lt;/p&gt;

&lt;p&gt;But how does the Workflow know which state the server is in during the install? Redfish APIs tell us that it’s powered on, but little else. &lt;/p&gt;

&lt;p&gt;Since it’s 2025, we just ask Claude.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh0rky90zw4wbrx9kius.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh0rky90zw4wbrx9kius.png" alt="An example of Claude’s happily recognizing a server POST screen" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supermicro introduced a CaptureScreen OEM API in their Redfish 1.14 release; with this API we can obtain a near real-time image of the server screen. With a basic prompt to Claude, we can then get a JSON payload that describes the state of the server at this point. Combining this into a Temporal workflow - alongside the PXE boot automation above - we can achieve an OS install and provision with one gRPC API call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4my7yvwvvsm44vfcr8df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4my7yvwvvsm44vfcr8df.png" alt="The temporal workflow polls the screen and monitors the install to completion, updating an internal DB at each step" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are probably more effective methods of achieving the same, but it costs us less than a dollar to provision 50 servers using Claude to screen-scrape every minute during the install.&lt;/p&gt;

&lt;p&gt;Now that an OS is installed, some duct-tape and Ansible will allow us to get some basic software running on the machine. However, bringing up networking is something else that’s typically an annoyance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Low Config Networking with BGP Unnumbered
&lt;/h1&gt;

&lt;p&gt;All the solutions we’ve discussed thus far have relied on a Management (or Out of Band) network. This is a dedicated Gigabit Ethernet network that links a management NIC, BMCs and other support infrastructure inside the cage. This network isn’t built to scale as it instead relies on being limited to a few hundred hosts at most and uses off-the-shelf routers, DHCP and VLANs for isolation and operation with low fault tolerance. The network that carries user traffic, the dataplane network as we term it, has very different requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6839ch391tjmu8bc6pe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6839ch391tjmu8bc6pe.png" alt="The dataplane network is a CLOS topology with redundant switches and redundant links" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For starters, the dataplane has multiple redundant links and must tolerate link failure or maintenance of redundant network switches. This requires running a routing protocol between switches and servers, with the protocol needing to route around device or link failures. Typically this routing must be configured for every point-to-point link; but this scales poorly in large deployments as the config needs to be customized for each rack.&lt;/p&gt;

&lt;p&gt;Unlike typical BGP where the BGP peer relationship must be defined between two configured IPv4 addresses for each router-router or router-server link, BGP unnumbered allows the use of autogenerated IPv6 link-local addresses as next-hops and peer-addresses for IPv4 and IPv6 BGP routing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhffv381ypcpop6pjbfku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhffv381ypcpop6pjbfku.png" alt="With FRR, BGP adjacencies can be declared on interfaces and FRR will pick the IPv6 Link-Local address of peer connected to that interface as its next-hop for routing" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This makes the routing setups on switches and servers uniform. Add all the interfaces connecting to a given type of device (eg: Top-of-Rack switch uplinks, server downlinks or Spine switch uplinks) as a BGP peer-group and configure BGP as normal; then the same config can be shipped to every equivalent device in the cluster. &lt;/p&gt;

&lt;p&gt;At Railway, we have 1 BGP config template per kind of device and roll these all via Ansible to all switches. We do not need to reconfigure any network gear as we scale a rack or cage.&lt;/p&gt;

&lt;p&gt;💡&lt;/p&gt;

&lt;p&gt;Config updates will eventually be needed and the cleanest/easiest way to apply them is to update the on-disk configuration via Ansible and reboot the switch. Hot-reloads with FRR are complicated, the prevailing approach seems to be &lt;a href="https://github.com/FRRouting/frr/blob/master/tools/frr-reload.py" rel="noopener noreferrer"&gt;frr-reload.py&lt;/a&gt; which diffs two textual configs and comes up with a list of CLI commands needed to reconcile them. &lt;/p&gt;

&lt;p&gt;Long-term we hope &lt;a href="https://docs.kernel.org/networking/switchdev.html" rel="noopener noreferrer"&gt;switchdev&lt;/a&gt; and &lt;a href="https://www.danosproject.org/" rel="noopener noreferrer"&gt;DANOS&lt;/a&gt; will get wider ASIC support so we can directly integrate with our control plane. Failing that, at larger scale, directly integrating with &lt;a href="https://www.opencompute.org/projects/sai" rel="noopener noreferrer"&gt;SAI&lt;/a&gt; and going in a &lt;a href="https://github.com/facebook/fboss" rel="noopener noreferrer"&gt;FBoss&lt;/a&gt;-esque direction seems inevitable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bfda6dq2fyw29sae2rr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bfda6dq2fyw29sae2rr.png" alt="Every possible port that can connect to a Top-of-Rack switch is marked as such on a Spine switch, regardless if those Racks are populated or not. This makes expansion plug-and-play." width="800" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the switch fabric configured in this way, and by running FRR with this same configuration down to servers, we build a full L3 network with ECMP across redundant links.&lt;/p&gt;

&lt;p&gt;💡 In addition to BGP unnumbered, this L3 fabric also requires &lt;code&gt;bgp bestpath as-path multipath-relax&lt;/code&gt; and specific AS numbering to avoid unintended consequences from BGP loop detection. FRR provides a set of “datacenter” defaults that adjust timing for fast eBGP convergence.&lt;/p&gt;

&lt;p&gt;When we want to add a new IP to a host on the network, we have a small agent insert a route into the Linux kernel routing table. A preconfigured FRR daemon picks this up and then propagates it through the rest of the network - as long as the routed prefix is within one of the subnets assigned to the site.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkraaqmayrtf48a0hspht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkraaqmayrtf48a0hspht.png" alt="IPv4 routes with IPv6 nexthops. These IPv6 addresses are Link-Local addresses discovered via IPv6 Neighbor Discovery" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Building Software to Run Hardware to Run Software
&lt;/h1&gt;

&lt;p&gt;Building &lt;a href="https://docs.railway.com/railway-metal?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Railway Metal&lt;/a&gt;, we’re more conscious than ever that we need to invest in tooling to enable us to deliver the best Metal experience we can build. We’re finding off-the-shelf solutions to be lacking or outdated in various ways, and the tooling we’re building for Railway Metal is proving an entire software vertical in itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1izm5tdj838bapk9va2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1izm5tdj838bapk9va2.png" alt="MetalCP is branching into Network Automation and is already our in-house RANCID/Oxidized replacement" width="800" height="849"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll continue to write more about our exploits, but in the interim - if you find any of this interesting or fun, we’re hiring! &lt;/p&gt;

&lt;p&gt;Pop on over to &lt;a href="http://railway.com/careers?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;railway.com/careers&lt;/a&gt; and check out our open roles.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>devops</category>
      <category>database</category>
    </item>
    <item>
      <title>So You Want to Build Your Own Data Center</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Fri, 15 Aug 2025 18:49:34 +0000</pubDate>
      <link>https://dev.to/sarah-railway/so-you-want-to-build-your-own-data-center-1fo8</link>
      <guid>https://dev.to/sarah-railway/so-you-want-to-build-your-own-data-center-1fo8</guid>
      <description>&lt;p&gt;&lt;em&gt;Author: Charith Amarasinghe&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since the beginning, &lt;a href="https://railway.com/?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Railway&lt;/a&gt;’s compute has been built on top of Google Cloud Platform. The platform supported Railway's initial journey, but it has caused a multitude of problems that have posed an existential risk to our business. More importantly, building on a hyperscaler prevents us from delivering the best possible platform to our customers. &lt;/p&gt;

&lt;p&gt;It directly affected the pricing we could offer (egress fees anyone?), limited the level of service we could deliver, and introduced engineering constraints that restricted the features we could build. &lt;/p&gt;

&lt;p&gt;And not only is it &lt;a href="https://github.com/GoogleCloudPlatform/guest-agent/issues/401" rel="noopener noreferrer"&gt;rare&lt;/a&gt; that we understand &lt;a href="https://blog.railway.com/p/2023-12-01-incident-report?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;why&lt;/a&gt; &lt;a href="https://x.com/JustJake/status/1667478906591666176" rel="noopener noreferrer"&gt;things&lt;/a&gt; &lt;a href="https://blog.railway.com/p/incident-december-16-2024?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;break&lt;/a&gt; upstream, but also despite multi-million dollar annual spend, we get about as much support from them as you would spending $100. &lt;/p&gt;

&lt;p&gt;So in response, we kicked off a Railway Metal project last year. Nine months later we were live with the first site in California, having designed, spec-ed, and installed everything from the fiber optic cables in the cage to the various contracts with ISPs. We’re lighting up three more data center regions as we speak.&lt;/p&gt;

&lt;p&gt;To deliver an “infra-less” cloud experience to our customers, we’ve needed to get good fast at building out our own physical infrastructure. That’s the topic of our blogpost today.&lt;/p&gt;

&lt;h2&gt;
  
  
  So you want to build a cloud
&lt;/h2&gt;

&lt;p&gt;From kicking off the Railway Metal project in January 2024, it took us five long months to get the first servers plugged in. It took us an additional three months before we felt comfortable letting our users onto the hardware (and an additional few months before we started writing about it here). &lt;/p&gt;

&lt;p&gt;The first step was finding some space.&lt;/p&gt;

&lt;p&gt;When you go “on-prem” in cloud-speak, you need somewhere to put your shiny servers and reliable power to keep them running. Also you want enough cooling so they don’t melt down. &lt;/p&gt;

&lt;p&gt;In general you have three main choices: Greenfield buildout (buying or leasing a datacenter), Cage Colocation (getting a private space inside a provider's  datacenter enclosed by mesh walls), or Rack colocation (leasing individual racks or partitions of racks in a colocation datacenter). &lt;/p&gt;

&lt;p&gt;We chose the second option: a cage to give us four walls, a secure door, and a blank slate for everything else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrkxib2x3wr6yn0xvkrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrkxib2x3wr6yn0xvkrm.png" alt="A cage before any racks have been fitted" width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The space itself doesn’t cost much, but power (and by proxy, cooling) costs the most. Depending on the geography, the $/kW rate can vary hugely — on the US west coast for example we may pay less than half as much as we pay in Singapore. Power is paid for as a fixed monthly commit, regardless of whether its consumed or not, to guarantee it will be available on-demand.&lt;/p&gt;

&lt;p&gt;But how much power do you need?&lt;/p&gt;

&lt;h2&gt;
  
  
  With great power comes great responsibility
&lt;/h2&gt;

&lt;p&gt;Ideally if you’ve embarked on your data center migration mission, you should have an idea of the rough amount of compute you want to deploy. We started with a target number of vCPUs, GBs of RAM, and TBs of NVMe to match our capacity on GCP. &lt;/p&gt;

&lt;p&gt;Using these figures, we converged on a server and CPU choice. There are many knobs to turn when doing this computation — probably worth a blogpost in itself — but the single biggest factor for us was power density e.g. how do we get the compute density we want inside of a specific power draw. &lt;/p&gt;

&lt;p&gt;The calculations aren’t as simple as summing watts though, especially with 3-phase feeds — Cloudflare has &lt;a href="https://blog.cloudflare.com/an-introduction-to-three-phase-power-and-pdus/" rel="noopener noreferrer"&gt;a great blogpost&lt;/a&gt; covering this topic.&lt;/p&gt;

&lt;p&gt;Power is the most critical resource for data centers, and a power outage can have extremely long recovery times. So redundancy is critical, and it’s important to have two fully independent power feeds per rack. Both feeds will share load under normal operation, but the design must be resilient to a feed going down.&lt;/p&gt;

&lt;p&gt;To deliver this power to your servers, you’ll also want a Power Distribution Unit, which you'll  select based on the number of sockets and management features it provides. The basic ones are glorified extension cords, while the ones we deploy allow control and metering of individual sockets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxgsknuzlr6xkmj5jsob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxgsknuzlr6xkmj5jsob.png" alt="Each PDU is accessible over the network and individual sockets can be remotely metered and controlled" width="800" height="710"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that, power is now available in the cage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let there be light
&lt;/h2&gt;

&lt;p&gt;No cloud machine is an island and that's where networks come into play. &lt;/p&gt;

&lt;p&gt;To achieve the lowest possible latency on Railway, we need to set you up with solid connections to the rest of the world. &lt;/p&gt;

&lt;p&gt;We look for DC facilities that are on-network with Tier 1 Internet Services Providers (ISPs), that are part of an Internet Exchange (IX), and that have available fiber to other data centers in close proximity. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgb505dl56a5wpv9hbeas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgb505dl56a5wpv9hbeas.png" alt="Each cage gets diverse and redundant network links" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your applications deployed to Railway will want to connect to a diverse mix of endpoints over the network — be it a home internet user in Sydney, Australia or a API hosted on an AWS server in the US. To get you the best possible latency and the lowest bandwidth cost, we contract with a mix of internet providers optimized for each use case.&lt;/p&gt;

&lt;p&gt;We select ISPs for the maturity of their networks in each geography we target. Partnering with the wrong ISP in a region can lead to extra network hops (and thus latency) to reach specific target markets — or in the worst case — convoluted network routes. So for each region, we pick at least two separate networks based on their regional footprints. &lt;/p&gt;

&lt;p&gt;Once connected, we receive full internet routing tables from each ISP and consolidate them on our network switches to resolve the best path for each IP prefix. If you have an end user in Australia trying to reach an app deployed to Singapore, we’ll likely hand those packets off directly to &lt;a href="https://bgp.tools/as/4637#connectivity" rel="noopener noreferrer"&gt;Telstra&lt;/a&gt; who have one of the densest access networks in Australia. If that same app needs to send packets to a end-user or server in Japan, then we’d likely be handing them over to PCCW who peer directly with NTT in Japan and have a &lt;a href="https://www.pccwglobal.com/wp-content/uploads/2019/11/world_fold_20191115.pdf" rel="noopener noreferrer"&gt;dense footprint in APAC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;👉 Peering information is public, head-over to &lt;a href="https://bgp.tools/" rel="noopener noreferrer"&gt;bgp.tools&lt;/a&gt; to see how your favorite networks interconnect.&lt;/p&gt;

&lt;p&gt;For redundancy we’re building out multiple zones in each region, and interconnectivity between these sites is also critical for our expansion. There are several tools such as dark fiber or wavelength services that we look for to plan this expansion. The result is that your apps won’t notice if your database is in the same room or if it’s 4 blocks over in a neighboring building — this is a feature not a bug — as it builds resilience against the failure of an individual data center.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Ok, now that you’ve found a space you like, signed a deal with a data center, and signed deals with several ISPs, you're all-systems-go to install some servers, right? &lt;/p&gt;

&lt;p&gt;Well, not exactly. First you need a bunch of other things to give your server a nice snug home to warm up in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aisles, racks and overhead infrastructure
&lt;/h2&gt;

&lt;p&gt;In a data center, racks are arranged in rows, and the space between racks, the aisle, is used for airflow. &lt;/p&gt;

&lt;p&gt;The Cold Aisle is where cold air is blown in from the DC facility, and servers in your rack suck this air up and exhaust it towards the rear onto the Hot Aisle. The DC facility will remove this hot air from the Hot Aisle. For optimum efficiency, you don’t want air between these aisles to mix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcppsq0dzxps28gtxit5r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcppsq0dzxps28gtxit5r.png" alt="A cage schematic - all equipment must be mounted such that fans blow air from the cold aisle to the hot" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The racks themselves have some variability, even if you opt to use conventional 19" wide equipment. You can select the height, width, and depth to suit your equipment and cabling needs. &lt;/p&gt;

&lt;p&gt;Most server equipment can slide on rails to allow for easy maintenance, so it’s important to ensure that cage dimensions allow for this. Cabling and cable management also requires some space, so there’s a tradeoff to be made with how crowded you want each rack to be vs. how many racks you can fit into a cage. &lt;/p&gt;

&lt;p&gt;In our experience, power and cooling is often the limiting factor rather than the actual space available. In newer sites we opt for wider 800mm racks to allow for better airflow by getting cables out of the way of the exhausts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjot1w7jbw14lhx1j8vz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjot1w7jbw14lhx1j8vz.png" alt="A rack-mount server on rails can be pulled out and serviced in place, each weighs nearly 20kg…" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to racks, you'll need several bits of infrastructure to get power and data to your racks. This will likely involve installing some overhead infrastructure and trays that let you route fiber cables from the edge of your cage to each of your racks, and to route cables between racks. This is something the DC operator will throw in when quoting the cage.&lt;/p&gt;

&lt;p&gt;Depending on your design, you’ll want to optimize for short cable paths by ensuring your overhead infrastructure, rack local cabling, and device orientations align. Because our racks have dense switch-to-server fiber cabling in each rack, we buy switches that have their ports oriented to the back of the rack (these are called reverse airflow switches because they exhaust air on the side with the network ports). &lt;/p&gt;

&lt;p&gt;This allows us to align the cable trays such that all cabling happens on one side of the rack and there’s no zig-zagging of cables between the front and back of the rack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5shp5c9xaq6y5sec34w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5shp5c9xaq6y5sec34w.png" alt="A ladder rack and fibre cable tray allow routing cables to racks from above" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you’ve got the space, signed up ISPs, ordered the hardware, got the racks, and a pretty good picture on how to lay it all out. But it’s still a pretty expensive lego set sitting in the loading bay of a data center. To assemble it you now need to leverage the most versatile programming tool ever devised in the history of mankind … Microsoft Excel.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rack and stack
&lt;/h2&gt;

&lt;p&gt;Let’s first step back and publish a disclaimer: neat and organized cabling requires a lot of practice; we tried it ourselves first - with… &lt;em&gt;mixed…&lt;/em&gt; results. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfjnljtmxp2s42vbyt9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfjnljtmxp2s42vbyt9n.png" alt="Our DIY attempts at cabling to get the first server online vs. what our installation partner put together" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To install it properly, we bring in professionals, but the professionals need to know what to install. A comprehensive documentation pack is essential. A cabling matrix and rack elevation are common documents that communicate to contractors how to rack and wire-up servers.&lt;/p&gt;

&lt;p&gt;A cabling matrix describes the termination of each cable, specifying the device position and port for each side of the connection, along with the specification of the cable itself (type of fiber, length, etc). The rack elevation is a visual representation of the rack itself, showing the position and orientation of each device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvgfshu0x6hfkaasia93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvgfshu0x6hfkaasia93.png" alt="The cabling matrix for our first install - 300+ cables all manually entered and cross-checked" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The documentation exercise can be intense, each of our install phases involved 60+ devices, 300+ discrete cables, and dozens of little details. This was all  handcrafted into written specifications and spreadsheets we used as a basis for the installation and commissioning. From the materials being on site to getting everything installed takes us about 6-14 days.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9ougxhz1tfnjx3ovl0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9ougxhz1tfnjx3ovl0s.png" alt="We have now built internal tooling to automate generating build specifications" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This all seems very far removed from software, DevOps, or what you’d typically think of as “infrastructure,” and that is very true — building a datacenter cage is probably closer to building a house than to deploying a Terraform stack. &lt;/p&gt;

&lt;p&gt;To compound this, every datacenter facility, contractor and  vendor will do things slightly differently, even within the same organization. The operational aspect requires you to stay on your toes and be extremely detail-oriented. &lt;/p&gt;

&lt;p&gt;Some what-the-duck moments we’ve had thus far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contractor: “We need longer power cables” - the PDUs at that site were upside down because the power came in from the floor, so our socket numbering was reversed in the plan&lt;/li&gt;
&lt;li&gt;Phonecall from Amsterdam: “There’s no demarcation point at the site?” - a specific facility installs external fibre links direct to a box in one of our racks rather than via a dedicated demarcation point overhead&lt;/li&gt;
&lt;li&gt;Railway Discord quote: “Why are the phases wired so weirdly on this PDU?” - the facility was wired differently to our other sites and the power sockets were wired phase-to-neutral vs. phase-to-phase (WYE vs Delta circuits for you EE’s)&lt;/li&gt;
&lt;li&gt;Contractor: “Your data cables are too short” - the contractor didn’t realize the network gear was reverse-airflow and tried to mount things the wrong way around&lt;/li&gt;
&lt;li&gt;Us raising a support ticket: “There’s no link coming up on this cable” - the fiber was wired in the wrong polarity; we learnt what “rolling fibre cables” was that day… it’s when they rip out the plugs from the &lt;a href="https://www.fs.com/uk/blog/lc-fiber-optics-a-comprehensive-guide-2684.html" rel="noopener noreferrer"&gt;LC connector&lt;/a&gt; and swap them around&lt;/li&gt;
&lt;li&gt;Railway Discord quote: “I brought a rubber mallet from HomeDepot today” - a batch of nearly 24 PDUs from one vendor were delivered with faulty sockets that didn’t properly engage with the power plugs, even with &lt;del&gt;appropriate&lt;/del&gt; extreme mechanical force being applied&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But from this point - the hardware is in place the task begins to feel more familiar; we’re now needing to do some BGP, install some OS’es, setup monitoring and bring everything up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6asx2hsrx0uw65jxsrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6asx2hsrx0uw65jxsrb.png" alt="The completed cage ready for configuration" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The completed cage ready for configuration&lt;/p&gt;

&lt;h2&gt;
  
  
  Pedal on Metal
&lt;/h2&gt;

&lt;p&gt;The installed cage is but a blank canvas, the network devices need configuring, router config needs writing, RIR (regional internet registry) records need updating, and we must interact with the likes of &lt;a href="https://www.dmtf.org/standards/redfish" rel="noopener noreferrer"&gt;Redfish APIs&lt;/a&gt; (HTTP APIs to dedicated controllers on server motherboards and PDUs) and PXE (a protocol to boot servers over the network) to get everything up and running. &lt;/p&gt;

&lt;p&gt;We've also not discussed how networking works. Our design uses &lt;a href="https://frrouting.org/" rel="noopener noreferrer"&gt;FRR&lt;/a&gt; and whitebox network switches running &lt;a href="https://sonicfoundation.dev/" rel="noopener noreferrer"&gt;SONiC&lt;/a&gt; to build a L3-only software-driven network that deeply integrates with our control plane. &lt;/p&gt;

&lt;p&gt;We’ve regaled you with tales from the frontline … but any more and you’d be here all day. &lt;/p&gt;

&lt;p&gt;In a future post, we’ll discuss how we go from a bunch of servers in a room to a functional Railway zone. In the space of the last few months we’ve built two new software tools, &lt;em&gt;Railyard&lt;/em&gt; and &lt;em&gt;MetalCP&lt;/em&gt;, to enable a button click experience from designing a new cage, tracing and visualizing the cabling, to installing OSes on servers and getting them on the internet.&lt;/p&gt;

&lt;p&gt;Until then, if any of this excites you, check out our &lt;a href="https://railway.com/careers/infra-platform?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;open Infrastructure Engineering roles&lt;/a&gt; and reach out if they catch your interest.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>devops</category>
      <category>database</category>
    </item>
    <item>
      <title>Speed Isn’t Just About Code, It’s About Where That Code Runs</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Thu, 07 Aug 2025 14:22:36 +0000</pubDate>
      <link>https://dev.to/sarah-railway/speed-isnt-just-about-code-its-about-where-that-code-runs-3do1</link>
      <guid>https://dev.to/sarah-railway/speed-isnt-just-about-code-its-about-where-that-code-runs-3do1</guid>
      <description>&lt;p&gt;Once upon a time, the complexity of backend was all developers talked about. (We even started as a platform to spin-up and host databases, backend services, and APIs.)&lt;/p&gt;

&lt;p&gt;However, the complexity has gradually shifted to the frontend. What started out as a dead-simple push of some HTML, CSS and JS to a static host has became much more. Frontend is batshit crazy now. &lt;/p&gt;

&lt;p&gt;Frontend developers are doing Server-side rendering (SSR), Client-side rendering (CSR), making tons of API calls, serving dynamic content and pushing the boundaries of the web. But despite the emergence of "frontend-only hosting" platforms, one fundamental problem exists — performance!&lt;/p&gt;

&lt;p&gt;Users now bear the cost of waiting for page loads because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend apps are served from one platform&lt;/li&gt;
&lt;li&gt;API requests are made to a different platform miles away from their frontend services&lt;/li&gt;
&lt;li&gt;Increased latency due to multiple round trips to databases across different regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a fragmented mess. Multiple requests across multiple regions and clouds resulting in a huge pile of unnecessary latency drags.&lt;/p&gt;

&lt;p&gt;Speed isn’t just about code, it’s about &lt;strong&gt;where your code runs&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;That’s why today we're talking about how you can now deploy your UI, API, and data side by side on the same infra. &lt;/p&gt;

&lt;p&gt;No cross-cloud delays, no extra hops, no wasted milliseconds. Just pure speed and fast apps.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Railway approach — Less is more
&lt;/h1&gt;

&lt;p&gt;We’re not just a database provider. We’re not just a backend platform. We’re not just a static host. We’re all of it.&lt;/p&gt;

&lt;p&gt;Walk with me while I show you what’s possible and why you should deploy your frontend apps on Railway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast everywhere — Global presence, local performance
&lt;/h2&gt;

&lt;p&gt;Your users aren’t all in one place, so why should your app be?&lt;/p&gt;

&lt;p&gt;We’ve got multi-region compute so that your frontend, backend, and database can live in the same place. NOT just your static files but everything in-between. No more long-haul flights. Your API calls deserve first-class, not a middle seat with crying babies.&lt;/p&gt;

&lt;p&gt;Our built-in anycast routing and automatic geosteering ensures requests hit the nearest server and region for your users. Eliminating performance bottlenecks means keeping compute where your users are!&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-config fast frontend builds
&lt;/h2&gt;

&lt;p&gt;If your apps are fast while your deploys take forever, then you or your team’s iteration speed and shipping velocity becomes a joke. We know you’ve been there. Don’t lie to us. It’s hell! No one deserves that.&lt;/p&gt;

&lt;p&gt;We built &lt;a href="https://blog.railway.com/p/introducing-railpack?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Railpack&lt;/a&gt; (&lt;em&gt;our next-gen app builder&lt;/em&gt;) with first-class support for Next, Vite, Astro, CRA, and Angular static sites. With significantly smaller images and better caching, Railpack builds and auto-deploys your frontend frameworks faster and efficiently. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dp7wrpf50nx7cfejkfl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dp7wrpf50nx7cfejkfl.png" alt="Angular app built with Railpack" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We cache your builds, so every deploy gets faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pay less, get more
&lt;/h2&gt;

&lt;p&gt;While many cloud providers love to charge you more for &lt;strong&gt;egress,&lt;/strong&gt; we flip the script and charge you less. We don’t think it’s fair to have a hidden tax on your success as you scale. &lt;/p&gt;

&lt;p&gt;Our Egress costs are &lt;strong&gt;10x cheaper&lt;/strong&gt; than the alternatives. This means you can move more data at a fraction of the cost, whether you’re serving large files, streaming content, or need to run APIs with high outbound traffic.&lt;/p&gt;

&lt;p&gt;We’ve also got &lt;a href="https://docs.railway.com/reference/app-sleeping?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Serverless Mode&lt;/a&gt;. When turned on, your apps automatically scale to zero when idle. This immensely reduces usage cost by ensuring your apps run only when it’s necessary!&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-side rendering — Fast and painless
&lt;/h2&gt;

&lt;p&gt;With Railway, everything lives in one place — your frontend, backend, and database are hosted on one platform and run on the same infrastructure. &lt;/p&gt;

&lt;p&gt;Setting everything to run in the same region (&lt;em&gt;which we recommend&lt;/em&gt;) means query latency bottlenecks are gone because your compute runs directly on your storage. Data retrieval is instant. No extra config &amp;amp; no complexity!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuynqgw49qhaoku60dgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuynqgw49qhaoku60dgz.png" alt="All together in one place—frontend, backend and database" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SSR can be slow due to network latency and multiple round trips to databases. Your services have to be as close as possible to each other, side by side. Railway keeps everything tightly integrated and gives you fast, real SSR the way it was designed to be!&lt;/p&gt;

&lt;p&gt;For even better performance and caching, you can throw Cloudflare &lt;em&gt;(or your preferred CDN)&lt;/em&gt; in front of your Railway services. This gives you the best of both worlds — lightning-fast performance and reliability. &lt;/p&gt;

&lt;p&gt;In the future, we’ll build native edge caching into the platform so you won’t need any extra setup.&lt;/p&gt;

&lt;h1&gt;
  
  
  How you can get started today
&lt;/h1&gt;

&lt;p&gt;We’ve got pre-built templates and 1-click deploys for every major frontend framework. All you need to do is select a template, deploy instantly, eject the template source into your repo, and keep shipping!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/yDom4a?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/lQQgLR?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Nuxt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/Ic0JBh?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/Qh0OAU?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/NeiLty?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/svelte-kit?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.com/template/A5t142?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Less Infra. More Shipping
&lt;/h1&gt;

&lt;p&gt;We believe developers should ship products without managing servers (infraless).&lt;/p&gt;

&lt;p&gt;Frontend engineers should focus on creating snappy, responsive and beautiful user experiences, &lt;em&gt;not fighting costs and infra.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://railway.com/new?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Deploy your frontend on Railway today&lt;/a&gt; and experience what happens when everything just works.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>One-Second Deploys? We Didn’t Believe It Either</title>
      <dc:creator>Sarah Bedell</dc:creator>
      <pubDate>Tue, 05 Aug 2025 18:21:32 +0000</pubDate>
      <link>https://dev.to/sarah-railway/one-second-deploys-we-didnt-believe-it-either-4h57</link>
      <guid>https://dev.to/sarah-railway/one-second-deploys-we-didnt-believe-it-either-4h57</guid>
      <description>&lt;p&gt;&lt;em&gt;Author: Jared Lunde&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let me introduce you to someone: they just released a new app, they started to tell people about it, and now they find themselves checking their Postgres database every 15 minutes to see if anyone has signed up. The thought of setting up Zapier or Mixpanel for a few simple product alerts puts them in physical agony.&lt;/p&gt;

&lt;p&gt;That was me just a few months ago. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Alright&lt;/em&gt;, I thought: &lt;em&gt;I’ll write a script, upload it to a Lambda function, and set up an EventBridge schedule to run it every hour&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;After over two hours of writing CDK, debugging permissions, waiting in the slow CloudFormation loop, and setting up a package to deploy, I finally received a Discord notification. &lt;/p&gt;

&lt;p&gt;Wahooo!!…? Not really.&lt;/p&gt;

&lt;p&gt;Because that’s how I spent my entire Saturday morning.&lt;/p&gt;

&lt;p&gt;Look at this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DiscordNotification&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@penseapp/discord-notification&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DiscordNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product Alerts (Hourly)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DISCORD_WEBHOOK_URL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Bun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`
  SELECT COUNT(*) as total FROM users WHERE created_at &amp;gt;= NOW() - INTERVAL '1 hour';
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;notify&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;infoMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hypebot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Engagement&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How is it possible that with the &lt;em&gt;simplicity&lt;/em&gt; of a cloud, &lt;em&gt;the magic&lt;/em&gt; of IaC, and the &lt;em&gt;truly&lt;/em&gt; crisp API of the Bun runtime — a mere 20 lines took over two hours to ship? &lt;/p&gt;

&lt;p&gt;That couldn't be right. There had to be a better way. &lt;/p&gt;

&lt;p&gt;Spoiler alert: There was a better way. &lt;/p&gt;

&lt;p&gt;We built it and added it to Railway. &lt;/p&gt;

&lt;p&gt;That's what we're announcing today.&lt;/p&gt;

&lt;p&gt;Allow me to show you how to deploy that same code in a secure Railway environment in 45 seconds &lt;a href="https://res.cloudinary.com/railway/video/upload/v1754413949/blog/hypebot-final_ueynti.mp4" rel="noopener noreferrer"&gt;in this video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No package manager, no GitHub repo, no IaC, no container builds. Just code to deploy.&lt;/p&gt;

&lt;p&gt;We're calling this feature Railway Functions, and it's available today in GA.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1uvy5vvboyv59qnkuet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1uvy5vvboyv59qnkuet.png" alt="Deploy Railway function" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s now possible to create a Function anywhere on the Railway canvas&lt;/p&gt;

&lt;p&gt;Let's get into how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;With Functions, we aim to cut out every step that isn’t absolutely necessary for running native code in a container and deploy it in under 5 seconds. &lt;/p&gt;

&lt;p&gt;Behind the curtain, Functions run on the same infraless compute that &lt;a href="https://docs.railway.com/reference/services?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Services&lt;/a&gt; do, which means you can still use volumes, variables, and other features you know and love.&lt;/p&gt;

&lt;p&gt;Here's what's happens when Railway runs a Function:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Parse and analyze:&lt;/strong&gt; We first parse your source code into an AST, determine its dependencies, then generate a package.json file on-the-fly. Our analyzer allows you to pin versions of packages without the need for a lockfile or package.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pkg@6.2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pkg@^6.2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pkg@~6.2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pkg@next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pkg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// evaluates to @latest&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pkg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Install dependencies&lt;/strong&gt;: Dependencies are then installed at the beginning of each deploy using &lt;code&gt;bun install&lt;/code&gt;. We tested every package manager under the sun and found Bun consistently outperformed the competition by &lt;em&gt;up to 10x&lt;/em&gt;. With Bun, our entire deploy step takes less time to finish than &lt;code&gt;npm install fastify&lt;/code&gt; did.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package Manager&lt;/th&gt;
&lt;th&gt;Time to install &lt;code&gt;fastify&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;npm/pnpm/yarn&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;without lockfile generation&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;6s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bun&lt;/td&gt;
&lt;td&gt;500ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. Run your code:&lt;/strong&gt; Bun allows you to execute TypeScript/JavaScript without transpiling first. Once we’ve stripped the version strings from your imports, we can then run your code as is without a bundler and Bun will strip the types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start deploying. Instantly.
&lt;/h2&gt;

&lt;p&gt;Shortening the feedback loop to seconds isn't a marginal improvement — it fundamentally changes how you work. When you can deploy in 1 second, you're free to experiment, iterate, and ship while staying in flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9rmsrlsm6l3nmjdx2n6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9rmsrlsm6l3nmjdx2n6.png" alt="Railway functions example" width="800" height="308"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is a real Function that took 00:01 seconds to deploy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today Functions offers support out of the box for TypeScript, but in the future we'll be adding more language support.&lt;/p&gt;

&lt;p&gt;To get started, head over to Railway, open up the canvas, and add a new Function service. We provide a few code examples out of the box, including a REST API, a landing page in JSX, and a cron utility.&lt;/p&gt;

&lt;p&gt;If you're curious about other use cases, you can also &lt;a href="https://docs.railway.com/reference/functions?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;check out the docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Either way, let us know what you want to see next in the Functions thread in &lt;a href="https://station.railway.com/feedback/feedback-railway-functions-231e8b94?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto" rel="noopener noreferrer"&gt;Help Station&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'd love for you to try &lt;a href="//railway.com/?utm_medium=blog&amp;amp;utm_source=devto&amp;amp;utm_campaign=devto"&gt;Railway for yourself&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>railway</category>
      <category>cloud</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
