<?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: Junwon Lee</title>
    <description>The latest articles on DEV Community by Junwon Lee (@cpprhtn).</description>
    <link>https://dev.to/cpprhtn</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%2F4102353%2F9d461b3e-793f-4781-bb79-463b24989af7.jpg</url>
      <title>DEV Community: Junwon Lee</title>
      <link>https://dev.to/cpprhtn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cpprhtn"/>
    <language>en</language>
    <item>
      <title>My home server needs 2 GB for the app, and more for the dev tooling. So I moved the tooling off it.</title>
      <dc:creator>Junwon Lee</dc:creator>
      <pubDate>Mon, 31 Aug 2026 13:55:50 +0000</pubDate>
      <link>https://dev.to/cpprhtn/my-home-server-needs-2-gb-for-the-app-so-i-moved-the-tooling-off-it-pal</link>
      <guid>https://dev.to/cpprhtn/my-home-server-needs-2-gb-for-the-app-so-i-moved-the-tooling-off-it-pal</guid>
      <description>&lt;p&gt;RAM got expensive. When you build a home server or grab a used Mini PC for a side project, you try to match the spec to what the service actually needs. Mine need about &lt;strong&gt;2 GB&lt;/strong&gt;. They run fine on that.&lt;/p&gt;

&lt;p&gt;The problem showed up somewhere else entirely. On a project with any real size, the &lt;em&gt;tooling&lt;/em&gt; I bring to the server eats more than the service does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VS Code's remote server and its daemons&lt;/li&gt;
&lt;li&gt;background AI agents like Claude Code or Codex — heavy enough that memory usage is a known, tracked pain&lt;/li&gt;
&lt;li&gt;monitoring tools that sit resident and sip resources forever&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I've got a lean little box that runs the service fine, and I keep getting pushed to over-spec it — not for the workload, for the &lt;strong&gt;tools&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Just develop locally and git-deploy"
&lt;/h2&gt;

&lt;p&gt;Sure — and I do. Shipping a feature is still local build → git → deploy, and I wouldn't change that.&lt;/p&gt;

&lt;p&gt;But git covers exactly one moment: &lt;strong&gt;code reaching the server.&lt;/strong&gt; After the deploy, a different set of jobs is waiting — read the logs, check resource usage, figure out &lt;em&gt;why a container died&lt;/em&gt;, fix one line in a config in a hurry. "What is this box doing right now" isn't answered by the repo, so eventually someone SSHes in. And if that someone brings heavy tools, you're back to square one.&lt;/p&gt;

&lt;p&gt;The question I got stuck on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can the server's resources go entirely to the &lt;strong&gt;service&lt;/strong&gt;, while the dev tools and the AI run from &lt;em&gt;outside&lt;/em&gt; without weighing it down?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the whole reason I built &lt;strong&gt;LiteDeck&lt;/strong&gt; (open source, Apache-2.0).&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: the tools live on your machine, not the server
&lt;/h2&gt;

&lt;p&gt;LiteDeck is &lt;strong&gt;agentless&lt;/strong&gt; (Go). It talks to the server over the one SSH port you already have open, and the heavy work — the UI, the editor, the AI — runs on &lt;strong&gt;your&lt;/strong&gt; local machine. The server just answers SSH — nothing installed, and almost nothing left running. (There's exactly one honest exception, and it's in the monitoring section below.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcin87kblyfs7doj6x192.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcin87kblyfs7doj6x192.gif" alt="LiteDeck: files, editor, services, processes, containers, network, monitoring and terminal in one window" width="799" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-overhead monitoring
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You can monitor a server meaningfully with basically no footprint on it.&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo4t6mc27mmcn8rqqey5l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo4t6mc27mmcn8rqqey5l.png" alt="The monitoring tab: CPU breakdown, per-core, memory, GPU, network, disk I/O and system facts" width="799" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LiteDeck's monitoring tab reads &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;df&lt;/code&gt; — those numbers already exist; the kernel keeps them whether you look or not. CPU split into user / kernel / iowait / steal, per-core, memory, disk I/O, network counters, PSI (how long things &lt;em&gt;waited&lt;/em&gt;, not just how much was used), open descriptors. GPU (VRAM, temperature, utilization) comes from &lt;code&gt;nvidia-smi&lt;/code&gt;, which ships with the driver — so still nothing new installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The one honest exception to "nothing resident":&lt;/strong&gt; GPU monitoring keeps a &lt;em&gt;single&lt;/em&gt; &lt;code&gt;nvidia-smi&lt;/code&gt; running on a 2-second loop while you're watching that host — re-launching it every poll would re-initialize NVML each time, which is the slow, wasteful part. It's the only thing LiteDeck leaves running on the server, and it stops the moment you disconnect. The &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;df&lt;/code&gt; reads for CPU, memory, disk and network need nothing resident at all — they're plain reads.&lt;/p&gt;

&lt;p&gt;No exporter, no time-series database, no agent living on the box. For a &lt;em&gt;few&lt;/em&gt; servers you don't need Prometheus + node_exporter + Grafana to answer "is this thing swapping right now" — you need to read &lt;code&gt;/proc&lt;/code&gt; over the SSH connection you already have.&lt;/p&gt;

&lt;p&gt;Is that enough for everyone? No. If you need history, alerting, or dozens of hosts, you want the real stack. But for a lean home server, this is the bet — and it's the difference between a monitoring tool that costs you RAM and one that costs you nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers, grouped the way Compose sees them
&lt;/h2&gt;

&lt;p&gt;The other half of "what is this box doing right now" is Docker. LiteDeck lists containers, images and volumes over the same SSH connection — it shells out to the &lt;code&gt;docker&lt;/code&gt; CLI, so no daemon socket is exposed and nothing new lands on the server.&lt;/p&gt;

&lt;p&gt;Containers are &lt;strong&gt;grouped by their Compose project&lt;/strong&gt;, read straight from the &lt;code&gt;com.docker.compose.project&lt;/code&gt; label. So you act on a whole project &lt;em&gt;or&lt;/em&gt; a single service inside it — start, stop, restart — and it keeps working &lt;strong&gt;even when the compose file is long gone&lt;/strong&gt;, because it drives &lt;code&gt;docker compose --project-name &amp;lt;name&amp;gt;&lt;/code&gt; off the labels Docker keeps, not a YAML file on disk. The images and volumes views flag what nothing references — the usual answer to a full disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI runs on your machine too — and sits in the same seat you do
&lt;/h2&gt;

&lt;p&gt;Offloading the tooling includes the AI. A local MCP client (Claude Code, Claude Desktop) sits in the &lt;strong&gt;same seat the GUI does&lt;/strong&gt;: the same adapter, the same already-authenticated SSH connection, the same Command Log. No second credential handed to the model, nothing extra listening on the server. The inference runs on your machine, not the box.&lt;/p&gt;

&lt;p&gt;It gets &lt;strong&gt;17 tools — 12 read, 5 write.&lt;/strong&gt; The write side runs through an approval gate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The default isn't "nag on everything."&lt;/strong&gt; A file write gets a diff dialog — because LiteDeck can show the change against what's on the server &lt;em&gt;right now&lt;/em&gt;, which the AI client can't. Service, container and process actions pass through, since the client already showed you those in the same words. (Two other modes, per host: &lt;code&gt;strict&lt;/code&gt; asks before &lt;em&gt;anything&lt;/em&gt;, for the box you can't afford to be wrong about; &lt;code&gt;bypass&lt;/code&gt; asks nothing — and &lt;strong&gt;expires on its own&lt;/strong&gt;, max 8 hours. There is no "forever".)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Here's the catch that keeps it safe:&lt;/strong&gt; &lt;em&gt;you&lt;/em&gt; set the mode, per host, in the app. No MCP tool changes it, no request field relaxes it — so a prompt injection buried in a log can't say "turn bypass on" and be believed.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;bypass&lt;/code&gt; on, an injected instruction &lt;em&gt;can&lt;/em&gt; run. What's left then isn't prevention — it's &lt;strong&gt;attribution and blast radius&lt;/strong&gt;: every command in the Command Log verbatim, the mode auto-expiring, and production able to stay gated while staging doesn't. &lt;code&gt;sudo&lt;/code&gt; is never added silently either. And a &lt;em&gt;file&lt;/em&gt; it changed can be rolled back — LiteDeck keeps the previous contents (a service restart or a killed process, of course, can't be un-rung).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trust boundary is you and the app — not the model's good intentions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http litedeck http://127.0.0.1:&amp;lt;port&amp;gt;/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The five rules I hold
&lt;/h2&gt;

&lt;p&gt;LiteDeck is really five constraints, and a couple are genuinely arguable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero server install.&lt;/strong&gt; &lt;em&gt;(Push back: "then your monitoring is weaker than an agent's." True — that's the &lt;code&gt;/proc&lt;/code&gt; bet above. Where's the line for you?)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH only.&lt;/strong&gt; The port you already have. SFTP and the terminal come from SSH itself, so even a box with no adapter still gives you files and a shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing hidden.&lt;/strong&gt; Every command — yours or the AI's — is in the Command Log, verbatim.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No account, no telemetry, open source.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight.&lt;/strong&gt; Not Electron. A 5–10 MB download, cold start under a second.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The one I keep getting asked about: &lt;strong&gt;there's no resident watcher, so no background alerting.&lt;/strong&gt; If the disk fills at 3 a.m. while nobody's looking, LiteDeck won't page you — that job belongs to something built to be always-on — a purpose-built monitor like Uptime Kuma or Netdata. Too strict? Maybe. I'd like to hear it.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But is any of this actually tested?"
&lt;/h2&gt;

&lt;p&gt;The repo ships a &lt;strong&gt;What's verified vs not&lt;/strong&gt; page that separates &lt;em&gt;what I've actually run on real hardware&lt;/em&gt; from &lt;em&gt;what merely ought to work&lt;/em&gt;. "Not verified" doesn't mean the code is missing — it means I haven't personally driven it yet, and I say so.&lt;/p&gt;

&lt;p&gt;That page exists because of stories like this: on Windows, downloading a directory from a Linux server, a file the server named &lt;code&gt;..\..\PWNED.txt&lt;/code&gt; &lt;strong&gt;landed two folders above the one I chose&lt;/strong&gt; — a backslash is an ordinary character in a Linux filename and a path separator on Windows (same class as scp's CVE-2019-6111). I reproduced it on a real Windows box, added a guard, and re-verified the fix on the same machine. That loop — reproduce on real hardware, fix, re-verify — is the bar I try to hold before I write "verified."&lt;/p&gt;

&lt;h2&gt;
  
  
  What it deliberately does &lt;em&gt;not&lt;/em&gt; do
&lt;/h2&gt;

&lt;p&gt;So nobody has to ask "why not just Ansible": not &lt;strong&gt;dozens of servers at once&lt;/strong&gt; (Ansible/Salt), not &lt;strong&gt;declarative state&lt;/strong&gt; (Terraform), not a &lt;strong&gt;remote IDE&lt;/strong&gt; (VS Code Remote-SSH), not &lt;strong&gt;screen streaming&lt;/strong&gt; (RDP/VNC). LiteDeck is for &lt;em&gt;a few&lt;/em&gt; servers, over SSH, with nothing installed, and with what it runs visible while it runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it / tell me where it breaks
&lt;/h2&gt;

&lt;p&gt;Builds for macOS, Windows and Linux are on the &lt;a href="https://github.com/cpprhtn/LiteDeck/releases" rel="noopener noreferrer"&gt;releases page&lt;/a&gt; (unsigned for now; the notes explain the Gatekeeper/SmartScreen prompts). It's an early project — the rough edges are real, and feedback or PRs from anyone running lean home servers, old Mini PCs, or low-end GPU boxes are exactly what I want.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;&lt;a href="https://github.com/cpprhtn/LiteDeck" rel="noopener noreferrer"&gt;https://github.com/cpprhtn/LiteDeck&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two open questions I'd like opinions on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is "agentless, read /proc over SSH" enough monitoring&lt;/strong&gt; — or does real observability need an agent on the box?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"AI in the same seat, human approves every write"&lt;/strong&gt; — too cautious, or about right?&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
