<?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: Mohammad Abdorrahmani</title>
    <description>The latest articles on DEV Community by Mohammad Abdorrahmani (@mohammad_abdorrahmani).</description>
    <link>https://dev.to/mohammad_abdorrahmani</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4144845%2F67e89140-09a4-4310-800a-f862a318d7d1.jpg</url>
      <title>DEV Community: Mohammad Abdorrahmani</title>
      <link>https://dev.to/mohammad_abdorrahmani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohammad_abdorrahmani"/>
    <language>en</language>
    <item>
      <title>Phelix: zero-downtime deploys and instant rollback for Go and Rust apps, without Kubernetes</title>
      <dc:creator>Mohammad Abdorrahmani</dc:creator>
      <pubDate>Sat, 26 Sep 2026 20:59:41 +0000</pubDate>
      <link>https://dev.to/mohammad_abdorrahmani/phelix-zero-downtime-deploys-and-instant-rollback-for-go-and-rust-apps-without-kubernetes-2lkm</link>
      <guid>https://dev.to/mohammad_abdorrahmani/phelix-zero-downtime-deploys-and-instant-rollback-for-go-and-rust-apps-without-kubernetes-2lkm</guid>
      <description>&lt;p&gt;Deploying a small fleet of services usually turns into a pile of glue: a build step, a process manager, a reverse proxy, a rollback plan, and something to watch it all. For three services, standing up Kubernetes is overkill — but "SSH in, pull, restart the process, and hope it comes back" isn't much of a plan either.&lt;/p&gt;

&lt;p&gt;Phelix is the tool I built for that middle ground. It's a single binary you run on the host that compiles your Go or Rust app, records every build as a numbered version, and cuts traffic over to a new version only after it passes a health check — so a broken deploy never takes down the version that's already serving.&lt;/p&gt;

&lt;p&gt;This post is a tour of how it works and the design decisions behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core idea: every build is a version, and health gates promotion
&lt;/h2&gt;

&lt;p&gt;Most deploy pain comes from doing the risky thing in place: you stop the running process to start the new one, and if the new one doesn't come up, you have downtime while you scramble.&lt;/p&gt;

&lt;p&gt;Phelix never does that. Each build is recorded as &lt;code&gt;v1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;, … on disk, and the new version is brought up &lt;em&gt;next to&lt;/em&gt; the current one. Only after it passes its health check does the proxy promote it to &lt;code&gt;current&lt;/code&gt;. If it never gets healthy, the old version keeps serving and nothing changed. Rollback, then, is just re-pointing at a version that's still sitting on disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick walk-through
&lt;/h2&gt;

&lt;p&gt;Install (detects your OS/arch; no toolchain needed just to install):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://phelix.anophel.com/install.sh | bash
phelix version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From a Go or Rust project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phelix init                     &lt;span class="c"&gt;# creates phelix.yaml&lt;/span&gt;
phelix build myapp &lt;span class="nt"&gt;--port&lt;/span&gt; 8080  &lt;span class="c"&gt;# auto-detects Go or Rust, builds v1, starts it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's one rule: your app must read its port from the &lt;code&gt;PORT&lt;/code&gt; environment variable instead of hardcoding one. That's what lets Phelix run two versions side by side during a cut-over. &lt;code&gt;phelix doctor&lt;/code&gt; checks this for you.&lt;/p&gt;

&lt;p&gt;Now ship a change with zero downtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phelix rebuild myapp &lt;span class="nt"&gt;--blue-green&lt;/span&gt;   &lt;span class="c"&gt;# or --replicas 3 for a rolling deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Phelix builds &lt;code&gt;v2&lt;/code&gt; alongside the running &lt;code&gt;v1&lt;/code&gt;, waits for &lt;code&gt;v2&lt;/code&gt; to pass its health check, then flips the proxy to &lt;code&gt;v2&lt;/code&gt; atomically. If &lt;code&gt;v2&lt;/code&gt; never gets healthy, &lt;code&gt;v1&lt;/code&gt; keeps serving.&lt;/p&gt;

&lt;p&gt;Want to be more careful? Send a slice of traffic first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phelix rebuild myapp &lt;span class="nt"&gt;--canary&lt;/span&gt; 5     &lt;span class="c"&gt;# 5% to the new version, verify, then promote&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It compares the canary's health and metrics against the stable baseline and auto-rolls-back on a regression.&lt;/p&gt;

&lt;p&gt;And when you need to undo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phelix rollback myapp &lt;span class="nt"&gt;--to&lt;/span&gt; v7 &lt;span class="nt"&gt;--dry-run&lt;/span&gt;   &lt;span class="c"&gt;# preview first&lt;/span&gt;
phelix rollback myapp &lt;span class="nt"&gt;--to&lt;/span&gt; v7             &lt;span class="c"&gt;# then for real&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rollback is effectively instant, because &lt;code&gt;v7&lt;/code&gt; never left disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's fail-safe by construction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A version is promoted to &lt;code&gt;current&lt;/code&gt; only after its health check passes — tiered as HTTP 2xx, any HTTP status, TCP connect, or PID liveness.&lt;/li&gt;
&lt;li&gt;The proxy switches targets atomically, so in-flight connections aren't dropped.&lt;/li&gt;
&lt;li&gt;A failed candidate never touches the live instance.&lt;/li&gt;
&lt;li&gt;Environment variables are encrypted at rest (AES-256-GCM) and snapshotted &lt;em&gt;together with the binary&lt;/em&gt; for each version. So a rollback restores the matching config, not just the code — which is exactly the bug that's bitten me elsewhere: rolling the binary back but keeping the new environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The rest of the toolbox
&lt;/h2&gt;

&lt;p&gt;Beyond the core loop, Phelix also does Docker either way (generate optimized multi-stage images with &lt;code&gt;dockerize&lt;/code&gt;, or run your app &lt;em&gt;instances&lt;/em&gt; as containers under the same proxy), matrix builds across toolchain versions and platforms, Git webhook deploys that rebuild the exact pushed commit (HMAC-verified), and per-instance CPU/memory limits via cgroups v2 on Linux.&lt;/p&gt;

&lt;p&gt;It's offline-first: build, run, deploy, and rollback all work with no account and no network. Logging in only adds an optional, per-app dashboard sync — nothing leaves your machine until you opt in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it fits (and where it doesn't)
&lt;/h2&gt;

&lt;p&gt;If you're already on Kubernetes, or you need multi-node scheduling, bin-packing, or a service mesh, use that — Phelix isn't trying to replace it. Phelix is for one host or a small fleet where you want the deploy &lt;em&gt;safety&lt;/em&gt; (versioning, health-gated cut-over, instant rollback, config that rolls back with the code) without running a platform to get it. Its closest neighbors are Kamal (Docker-centric) and Nomad (a real scheduler); Phelix leans toward a single binary and works without Docker at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current state
&lt;/h2&gt;

&lt;p&gt;This is v1.0.0, built by one person. I run it for my own Go and Rust services, so treat it as "works for its author's real workloads" rather than battle-tested across many environments. Linux is the target; Windows support is experimental. The zero-downtime cut-over and rollback paths are what I'd most like other people to stress-test.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on licensing
&lt;/h2&gt;

&lt;p&gt;Phelix is &lt;strong&gt;source-available&lt;/strong&gt;, not classic (OSI) open source — worth being precise about. The CLI is under the Functional Source License 1.1 with an Apache-2.0 future grant (FSL-1.1-ALv2): you can use, study, modify, and redistribute it for anything &lt;em&gt;except&lt;/em&gt; offering a competing hosted/dashboard service, and each released version converts to Apache 2.0 two years after its release. The dashboard backend is proprietary. I'd rather say that up front than have it be a surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/abdorrahmani/phelix" rel="noopener noreferrer"&gt;https://github.com/abdorrahmani/phelix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://phelix.anophel.com/docs" rel="noopener noreferrer"&gt;https://phelix.anophel.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo / dashboard: &lt;a href="https://phelix.anophel.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_01&amp;amp;utm_content=devto_launch" rel="noopener noreferrer"&gt;https://phelix.anophel.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_01&amp;amp;utm_content=devto_launch&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you deploy Go or Rust on your own boxes, I'd genuinely like to hear how you handle zero-downtime restarts today — systemd, a reverse-proxy swap, Kamal, something else — and whether per-version encrypted env snapshots match how you think about config rollback.&lt;/p&gt;

&lt;h1&gt;
  
  
  go #devops #showdev #rust
&lt;/h1&gt;

</description>
      <category>deployment</category>
      <category>go</category>
      <category>infrastructure</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
