<?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: Martin Voldřich</title>
    <description>The latest articles on DEV Community by Martin Voldřich (@rbas).</description>
    <link>https://dev.to/rbas</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%2F3791793%2F9b8be844-496d-4c61-b03e-638267be9d7e.png</url>
      <title>DEV Community: Martin Voldřich</title>
      <link>https://dev.to/rbas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rbas"/>
    <language>en</language>
    <item>
      <title>I Built a Local Dev Proxy. Six Months Later, Its Daemon No Longer Runs as Root</title>
      <dc:creator>Martin Voldřich</dc:creator>
      <pubDate>Thu, 20 Aug 2026 17:34:51 +0000</pubDate>
      <link>https://dev.to/rbas/i-built-a-local-dev-proxy-six-months-later-its-daemon-no-longer-runs-as-root-36on</link>
      <guid>https://dev.to/rbas/i-built-a-local-dev-proxy-six-months-later-its-daemon-no-longer-runs-as-root-36on</guid>
      <description>&lt;p&gt;Six months ago, I wrote about why I was tired of developing on URLs like &lt;code&gt;http://localhost:3000&lt;/code&gt; and built &lt;a href="https://dev.to/rbas/i-got-tired-of-localhost3000-so-i-built-a-local-dev-proxy-with-custom-domains-and-trusted-https-1dj6"&gt;Roxy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first version had a simple goal: give every local project a memorable&lt;br&gt;
&lt;code&gt;.roxy&lt;/code&gt; domain with trusted HTTPS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000

became

https://myapp.roxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked, and I started using it every day. &lt;/p&gt;

&lt;p&gt;Today, Roxy 1.1.0 is available, and the project finally has a proper home at &lt;a href="https://roxy.rbas.cz" rel="noopener noreferrer"&gt;roxy.rbas.cz&lt;/a&gt;. It still does the same simple thing, but the project around that idea has changed quite a bit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Compose services can be discovered automatically&lt;/li&gt;
&lt;li&gt;Linux is supported alongside macOS&lt;/li&gt;
&lt;li&gt;wildcard subdomains get trusted HTTPS&lt;/li&gt;
&lt;li&gt;one domain can route different paths to different services&lt;/li&gt;
&lt;li&gt;configuration changes are applied without restarting the daemon&lt;/li&gt;
&lt;li&gt;static directories have a built-in file browser&lt;/li&gt;
&lt;li&gt;and, most importantly, the daemon no longer runs as root&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is what changed, why it changed, and what I learned while turning a small experiment into a tool I can rely on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The daily workflow is still deliberately boring
&lt;/h2&gt;

&lt;p&gt;Roxy is not trying to become a general-purpose reverse proxy. The workflow is&lt;br&gt;
still centered around one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy register myapp.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;https://myapp.roxy&lt;/code&gt; and you are done. Roxy handles local DNS, creates a trusted certificate, and proxies the request to the application on port 3000.&lt;/p&gt;

&lt;p&gt;For a full-stack project, several services can share the same domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy register myapp.roxy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/api=3001"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/admin=8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://myapp.roxy          -&amp;gt; frontend on port 3000
https://myapp.roxy/api      -&amp;gt; API on port 3001
https://myapp.roxy/admin    -&amp;gt; admin app on port 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The longest matching path wins, and routes can now be changed after a domain is registered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy route add myapp.roxy /webhooks 9000
roxy route remove myapp.roxy /admin
roxy route list myapp.roxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The daemon reloads the new configuration immediately. No hand-written proxy configuration and no service restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Compose without another routing file
&lt;/h2&gt;

&lt;p&gt;Docker auto-discovery was the biggest addition in Roxy 1.0.&lt;/p&gt;

&lt;p&gt;Enable it once in the Roxy configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[docker]&lt;/span&gt;
&lt;span class="py"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start a normal Compose project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Roxy watches Docker events and exposes the services automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://web.myproject.roxy
https://api.myproject.roxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;roxy register&lt;/code&gt; command and no second configuration file describing the same containers. When a container starts or stops, Roxy updates its routing table.&lt;/p&gt;

&lt;p&gt;The default convention can be changed with Compose labels when needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;roxy.domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shop.roxy"&lt;/span&gt;
      &lt;span class="na"&gt;roxy.wildcard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted the default case to require no Roxy-specific configuration while keeping an escape hatch for projects that need a stable domain or a specific port.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wildcard domains became a real requirement
&lt;/h2&gt;

&lt;p&gt;The original exact-domain model was enough until I used Roxy with a&lt;br&gt;
multi-tenant application. Registering every tenant subdomain individually was clearly the wrong workflow.&lt;/p&gt;

&lt;p&gt;Now one registration can cover the base domain and its subdomains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy register myapp.roxy &lt;span class="nt"&gt;--wildcard&lt;/span&gt; &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These URLs then work immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://myapp.roxy
https://acme.myapp.roxy
https://globex.myapp.roxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Roxy generates an exact certificate from TLS SNI when a hostname is first requested and keeps it in an in-memory cache. Per-domain private keys are not written to disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roxy now works on Linux
&lt;/h2&gt;

&lt;p&gt;The first release was macOS-only. Roxy now supports Ubuntu and Debian as well.&lt;/p&gt;

&lt;p&gt;The user-facing workflow remains the same, but the system integration is native to each platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS uses &lt;code&gt;/etc/resolver&lt;/code&gt; and launchd&lt;/li&gt;
&lt;li&gt;Linux uses &lt;code&gt;systemd-resolved&lt;/code&gt; and systemd&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that the platform-specific work stays behind the same CLI.&lt;br&gt;
Developers should not need to know which service file or DNS configuration Roxy&lt;br&gt;
created to run &lt;code&gt;roxy register&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The daemon no longer runs as root
&lt;/h2&gt;

&lt;p&gt;This is the change in Roxy 1.1 that matters most to me.&lt;/p&gt;

&lt;p&gt;Local HTTPS normally wants ports 80 and 443, and binding those ports requires privileges. The early version took the straightforward route: the Roxy daemon ran as root.&lt;/p&gt;

&lt;p&gt;It worked, but it became increasingly uncomfortable as Roxy gained more responsibilities. A process that reads project directories, watches Docker, loads user configuration, and handles network traffic should not have unlimited system privileges just because it needs two low-numbered ports.&lt;/p&gt;

&lt;p&gt;Roxy 1.1 uses socket activation instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;launchd/systemd owns ports 80 and 443
                |
                v
      passes open listeners to Roxy
                |
                v
       Roxy runs as your user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The one-time installation still needs elevated privileges because it installs the local CA, DNS integration, and system service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, routine commands run without &lt;code&gt;sudo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy register myapp.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt;
roxy reload
roxy restart
roxy logs &lt;span class="nt"&gt;-f&lt;/span&gt;
roxy status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration, logs, runtime files, and the CA are owned by the developer account. The operating system owns only the privileged listeners and hands them to the unprivileged Roxy process.&lt;/p&gt;

&lt;p&gt;This was not a flashy feature, but it made the security model much easier to explain: Roxy has the privileges it needs, and no more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading from an older Roxy installation
&lt;/h2&gt;

&lt;p&gt;If you already use a version with the old root daemon, upgrade Roxy and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installer imports registrations and the Root CA from &lt;code&gt;/etc/roxy&lt;/code&gt;, stops the old service, creates the user-owned configuration, and installs socket activation. It leaves the old &lt;code&gt;/etc/roxy&lt;/code&gt; directory in place as a migration backup.&lt;/p&gt;

&lt;p&gt;New installations use the same command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap rbas/roxy
brew &lt;span class="nb"&gt;install &lt;/span&gt;roxy
&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then register a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy register myapp.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your browser once after the initial installation so it picks up the local certificate authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Roxy 1.1
&lt;/h2&gt;

&lt;p&gt;Roxy is open source and written in Rust. It runs on macOS and Linux and ships as a single binary with no external runtime dependencies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://roxy.rbas.cz" rel="noopener noreferrer"&gt;roxy.rbas.cz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source code: &lt;a href="https://github.com/rbas/roxy" rel="noopener noreferrer"&gt;github.com/rbas/roxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://roxy.rbas.cz/docs" rel="noopener noreferrer"&gt;Roxy docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I would love to hear what works, what breaks, and which part of your local development setup is still more complicated than it should be.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>devtools</category>
      <category>docker</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I got tired of localhost:3000, so I built a local dev proxy with custom domains and trusted HTTPS</title>
      <dc:creator>Martin Voldřich</dc:creator>
      <pubDate>Wed, 25 Feb 2026 13:45:51 +0000</pubDate>
      <link>https://dev.to/rbas/i-got-tired-of-localhost3000-so-i-built-a-local-dev-proxy-with-custom-domains-and-trusted-https-1dj6</link>
      <guid>https://dev.to/rbas/i-got-tired-of-localhost3000-so-i-built-a-local-dev-proxy-with-custom-domains-and-trusted-https-1dj6</guid>
      <description>&lt;p&gt;Every web developer knows the routine. You start your frontend on port 3000,&lt;br&gt;
your API on 3001, maybe an admin dashboard on 8080. Open a browser tab, stare&lt;br&gt;
at it... which port was which again?&lt;/p&gt;

&lt;p&gt;It gets worse when you juggle multiple projects. Two apps both want port 3000.&lt;br&gt;
You're switching between client projects and can't remember if the e-commerce&lt;br&gt;
site was :3000 or :3001 today. And your bookmarks bar is full of&lt;br&gt;
&lt;code&gt;localhost:something&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I decided to fix this for myself and ended up building&lt;br&gt;
&lt;a href="https://github.com/rbas/roxy" rel="noopener noreferrer"&gt;Roxy&lt;/a&gt; — a local development proxy that&lt;br&gt;
replaces localhost ports with custom &lt;code&gt;.roxy&lt;/code&gt; domains and browser-trusted HTTPS.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;

&lt;p&gt;Instead of &lt;code&gt;http://localhost:3000&lt;/code&gt;, you get &lt;code&gt;https://myapp.roxy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy register shop.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt; &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/api=3001"&lt;/span&gt;
roxy register blog.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=4000"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Open &lt;code&gt;https://shop.roxy&lt;/code&gt; in your browser — green lock, no&lt;br&gt;
certificate warnings, no config files. &lt;code&gt;https://blog.roxy&lt;/code&gt; is a completely&lt;br&gt;
separate project running alongside it.&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%2F1pvbsyt5pa2bruo91anx.gif" 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%2F1pvbsyt5pa2bruo91anx.gif" alt="Roxy demo" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;Roxy is a single binary that bundles three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A DNS server&lt;/strong&gt; — resolves all &lt;code&gt;.roxy&lt;/code&gt; domains to &lt;code&gt;127.0.0.1&lt;/code&gt;. No need to
edit &lt;code&gt;/etc/hosts&lt;/code&gt; or install dnsmasq.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A certificate authority&lt;/strong&gt; — generates trusted HTTPS certificates on the
fly. The root CA gets installed into your system trust store during
&lt;code&gt;roxy install&lt;/code&gt;, so browsers trust everything automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A reverse proxy&lt;/strong&gt; — routes incoming requests to your local services based
on the domain and path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One-time setup takes about 10 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (macOS via Homebrew, or build from source on Linux)&lt;/span&gt;
brew tap rbas/roxy &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;roxy

&lt;span class="c"&gt;# One-time setup — creates Root CA, configures DNS&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you just register domains and start the proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features I use every day
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Path-based routing&lt;/strong&gt; — run multiple services behind one domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy register myapp.roxy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/api=3001"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/admin=8080"&lt;/span&gt;

&lt;span class="c"&gt;# https://myapp.roxy       → frontend&lt;/span&gt;
&lt;span class="c"&gt;# https://myapp.roxy/api   → API&lt;/span&gt;
&lt;span class="c"&gt;# https://myapp.roxy/admin → admin panel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Wildcard subdomains&lt;/strong&gt; — great for multi-tenant SaaS development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy register myapp.roxy &lt;span class="nt"&gt;--wildcard&lt;/span&gt; &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt;

&lt;span class="c"&gt;# https://myapp.roxy            → main app&lt;/span&gt;
&lt;span class="c"&gt;# https://acme.myapp.roxy       → tenant "acme"&lt;/span&gt;
&lt;span class="c"&gt;# https://globex.myapp.roxy     → tenant "globex"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Static file serving&lt;/strong&gt; — point a route at a directory and get a built-in file&lt;br&gt;
browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy register docs.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=./build"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real-time traffic logs&lt;/strong&gt; — see every request flowing through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roxy logs &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The tech
&lt;/h2&gt;

&lt;p&gt;I wrote Roxy in Rust. The whole thing is about 5k lines and compiles to a&lt;br&gt;
single binary with zero runtime dependencies. The main building blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/tokio-rs/axum" rel="noopener noreferrer"&gt;axum&lt;/a&gt; for the HTTP server and reverse
proxy&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rustls/rustls" rel="noopener noreferrer"&gt;rustls&lt;/a&gt; for TLS (no OpenSSL dependency)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rustls/rcgen" rel="noopener noreferrer"&gt;rcgen&lt;/a&gt; for certificate generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It runs on &lt;strong&gt;macOS&lt;/strong&gt; (Monterey+) and &lt;strong&gt;Linux&lt;/strong&gt; (Ubuntu 22.04+ / Debian 12+).&lt;br&gt;
The Linux support just landed in v0.5.0, which I shipped this week.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew tap rbas/roxy &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;roxy

&lt;span class="c"&gt;# Or build from source (macOS or Linux)&lt;/span&gt;
git clone https://github.com/rbas/roxy.git
&lt;span class="nb"&gt;cd &lt;/span&gt;roxy &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cargo &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Then&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy &lt;span class="nb"&gt;install
sudo &lt;/span&gt;roxy register myapp.roxy &lt;span class="nt"&gt;--route&lt;/span&gt; &lt;span class="s2"&gt;"/=3000"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;roxy start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is open source (MIT):&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/rbas/roxy" rel="noopener noreferrer"&gt;github.com/rbas/roxy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear how you handle local dev routing — do you just live with&lt;br&gt;
localhost ports, or have you found a setup that works? Let me know in the&lt;br&gt;
comments.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
