<?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: Scrooge Mcduck</title>
    <description>The latest articles on DEV Community by Scrooge Mcduck (@vaescrooge).</description>
    <link>https://dev.to/vaescrooge</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%2F3961956%2Fd859940c-7251-4cf9-8e7e-7abe52002db8.jpeg</url>
      <title>DEV Community: Scrooge Mcduck</title>
      <link>https://dev.to/vaescrooge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vaescrooge"/>
    <language>en</language>
    <item>
      <title>SSH Server Management Without the Ansible Overhead</title>
      <dc:creator>Scrooge Mcduck</dc:creator>
      <pubDate>Mon, 01 Jun 2026 15:31:34 +0000</pubDate>
      <link>https://dev.to/vaescrooge/ssh-server-management-without-the-ansible-overhead-181e</link>
      <guid>https://dev.to/vaescrooge/ssh-server-management-without-the-ansible-overhead-181e</guid>
      <description>&lt;p&gt;You SSH into 10 servers to check disk space. You write a quick for loop. A typo crashes one session. You're not sure which ones you've already checked. By the time you're done, 20 minutes are gone and you have a terminal full of scrollback you need to piece together.&lt;/p&gt;

&lt;p&gt;There's a better way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Most developers SSH into servers one at a time. When you need to run the same command across multiple machines, your options are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open 10 terminals&lt;/strong&gt; — error-prone, slow, no audit trail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write a shell loop&lt;/strong&gt; — &lt;code&gt;for host in web-01 web-02 ...; do ssh $host "df -h"; done&lt;/code&gt; — works until a host is down and your loop breaks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ansible&lt;/strong&gt; — powerful, but you need to learn YAML DSL, write playbooks, manage inventory files. Overkill for "check disk space"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fabric&lt;/strong&gt; — good Python library, but no built-in host management or CLI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these give you a simple CLI that just works out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meet remote-cmd
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;remote-cmd&lt;/code&gt; is a Python CLI + API for SSH server management. It gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host CRUD with tag-based grouping (&lt;code&gt;production&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;web&lt;/code&gt;, &lt;code&gt;db&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;One-shot command execution on single or multiple hosts&lt;/li&gt;
&lt;li&gt;File upload/download via SFTP&lt;/li&gt;
&lt;li&gt;A Python API for scripting automation&lt;/li&gt;
&lt;li&gt;Zero setup — &lt;code&gt;pip install&lt;/code&gt; and go&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Demo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;remote_cmd_manager

&lt;span class="c"&gt;# Add your first server&lt;/span&gt;
remote-cmd host add web-01 192.168.1.10 ubuntu &lt;span class="nt"&gt;--key&lt;/span&gt; ~/.ssh/id_rsa

&lt;span class="c"&gt;# Run a command&lt;/span&gt;
remote-cmd run web-01 &lt;span class="s2"&gt;"uptime"&lt;/span&gt;
&lt;span class="c"&gt;# → 14:32:10 up 45 days,  2:15,  1 user,  load average: 0.08, 0.03, 0.05&lt;/span&gt;

&lt;span class="c"&gt;# Add more servers and run across all production hosts&lt;/span&gt;
remote-cmd host add web-02 192.168.1.11 ubuntu &lt;span class="nt"&gt;--key&lt;/span&gt; ~/.ssh/id_rsa &lt;span class="nt"&gt;--tag&lt;/span&gt; production
remote-cmd host add db-01 192.168.1.20 ubuntu &lt;span class="nt"&gt;--key&lt;/span&gt; ~/.ssh/id_rsa &lt;span class="nt"&gt;--tag&lt;/span&gt; production &lt;span class="nt"&gt;--tag&lt;/span&gt; database

remote-cmd batch-run &lt;span class="nt"&gt;-t&lt;/span&gt; production &lt;span class="s2"&gt;"df -h /"&lt;/span&gt;
&lt;span class="c"&gt;# ✓ web-01  → Disk: 32G/100G (32%)&lt;/span&gt;
&lt;span class="c"&gt;# ✓ web-02  → Disk: 45G/100G (45%)&lt;/span&gt;
&lt;span class="c"&gt;# ✓ db-01   → Disk: 12G/50G  (24%)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-World Use Cases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Incident Response&lt;/strong&gt;&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;# Check errors across all web servers in 3 seconds&lt;/span&gt;
remote-cmd batch-run &lt;span class="nt"&gt;-t&lt;/span&gt; web &lt;span class="s2"&gt;"journalctl -xe -n 50 | grep -i error"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deploy Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;remote_cmd.core.host_manager&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HostManager&lt;/span&gt;

&lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HostManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hosts.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_hosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect_to_host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cd /app &amp;amp;&amp;amp; git pull&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_sudo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;systemctl restart app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudopass&lt;/span&gt;&lt;span class="sh"&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;Config Update&lt;/strong&gt;&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;# Upload new nginx config and reload across all web servers&lt;/span&gt;
remote-cmd upload web-01 ./nginx.conf /tmp/nginx.conf
remote-cmd run web-01 &lt;span class="s2"&gt;"sudo cp /tmp/nginx.conf /etc/nginx/nginx.conf &amp;amp;&amp;amp; sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Stacks Up
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;remote-cmd&lt;/th&gt;
&lt;th&gt;ssh + shell&lt;/th&gt;
&lt;th&gt;Ansible&lt;/th&gt;
&lt;th&gt;Fabric&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;10 seconds&lt;/td&gt;
&lt;td&gt;None (you know ssh)&lt;/td&gt;
&lt;td&gt;30 min+&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host management&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Inventory YAML&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;ansible cmd&lt;/td&gt;
&lt;td&gt;fab cmd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python API&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No (YAML)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ideal for&lt;/td&gt;
&lt;td&gt;Ad-hoc + scripts&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Config mgmt&lt;/td&gt;
&lt;td&gt;Python scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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



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

&lt;span class="c"&gt;# Full documentation&lt;/span&gt;
remote-cmd &lt;span class="nt"&gt;--help&lt;/span&gt;

&lt;span class="c"&gt;# GitHub&lt;/span&gt;
&lt;span class="c"&gt;# https://github.com/Vae-Scrooge/remote-cmd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is in beta, the core API is stable, and contributions are welcome.&lt;/p&gt;

&lt;p&gt;If you SSH into more than a couple servers regularly, give it a try. It'll save you the "which servers did I check again?" moment.&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>devops</category>
      <category>python</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
