<?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: Aditya Anil</title>
    <description>The latest articles on DEV Community by Aditya Anil (@aditya_an1l).</description>
    <link>https://dev.to/aditya_an1l</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%2F1917254%2F7064f6c2-6bc1-4d31-ba47-b616d5d68f34.png</url>
      <title>DEV Community: Aditya Anil</title>
      <link>https://dev.to/aditya_an1l</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aditya_an1l"/>
    <language>en</language>
    <item>
      <title>SSH keeps dying after 2 minutes? It’s not the firewall, it’s idleness.</title>
      <dc:creator>Aditya Anil</dc:creator>
      <pubDate>Sun, 18 Oct 2026 07:36:41 +0000</pubDate>
      <link>https://dev.to/aditya_an1l/ssh-keeps-dying-after-2-minutes-its-not-the-firewall-its-idleness-14o3</link>
      <guid>https://dev.to/aditya_an1l/ssh-keeps-dying-after-2-minutes-its-not-the-firewall-its-idleness-14o3</guid>
      <description>&lt;p&gt;You spin up a Linux machine, and the next thing you want is SSH access from anywhere.&lt;/p&gt;

&lt;p&gt;Then the connection dies after about two minutes of silence.&lt;/p&gt;

&lt;p&gt;If you were like me, your first hypothesis would always be “the campus/network firewall is blocking SSH.” Well, more often that’s the case if you are working in a public/corporate network.&lt;/p&gt;

&lt;p&gt;However, on the other side, it isn’t the firewall that is blocking the protocol, but it is complacency: &lt;strong&gt;evicting idle connections&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;What’s the difference? A blocked protocol needs a different transport. An idle timeout just needs you to never go silent.&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%2F03nk5nbpkcnvbjdtmtt7.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%2F03nk5nbpkcnvbjdtmtt7.png" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I will guide you on a quick way to keep the SSH connection alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  I. The setup
&lt;/h2&gt;

&lt;p&gt;The solution presented below uses no public ports and no outbound SSH filtering. &lt;/p&gt;

&lt;p&gt;The solution is this: relay SSH over HTTPS using Cloudflare’s free Quick Tunnel.&lt;/p&gt;

&lt;p&gt;This setup does not require you to have an account.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install &lt;code&gt;cloudflared&lt;/code&gt; on the server
&lt;/h3&gt;

&lt;p&gt;Pick the method that matches your distro:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debian / Ubuntu&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="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0755 /usr/share/keyrings
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://pkg.cloudflare.com/cloudflare-main.gpg | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /usr/share/keyrings/cloudflare-main.gpg &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/cloudflared.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fedora / RHEL / CentOS&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;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://pkg.cloudflare.com/cloudflared.repo | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/yum.repos.d/cloudflared.repo
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;cloudflared   &lt;span class="c"&gt;# or yum&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Arch Linux&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="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-Syu&lt;/span&gt; cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Any other distro&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Download the binary from the &lt;a href="https://developers.cloudflare.com/tunnel/downloads/" rel="noopener noreferrer"&gt;official downloads page&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Start the tunnel on the server
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel &lt;span class="nt"&gt;--url&lt;/span&gt; ssh://localhost:22 &lt;span class="nt"&gt;--protocol&lt;/span&gt; http2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It prints a temporary &lt;code&gt;*.trycloudflare.com&lt;/code&gt; URL. &lt;strong&gt;Keep that terminal open.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Connect from another Linux machine
&lt;/h3&gt;

&lt;p&gt;Install &lt;code&gt;cloudflared&lt;/code&gt; on the client the same way (or just grab the binary), then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ProxyCommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cloudflared access ssh --hostname %h"&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;@YOUR-LINK.trycloudflare.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if everything goes well, you notice that it works… until you stop typing for about a minute. Then it vanishes.&lt;/p&gt;

&lt;h2&gt;
  
  
  II. But why does it drop?
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4cyp4ivm6hkh0olwrgmi.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%2F4cyp4ivm6hkh0olwrgmi.png" alt=" " width="800" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stateful packet filters (and Cloudflare’s edge) treat silent connections as stale and reap them. &lt;/p&gt;

&lt;p&gt;To overcome this, there are two fixes that have worked for me:&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%2Fx0kpx18uej9gymmyqnqa.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%2Fx0kpx18uej9gymmyqnqa.png" alt=" " width="750" height="805"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1: Client side
&lt;/h2&gt;

&lt;p&gt;We want the client to never be idle.&lt;/p&gt;

&lt;p&gt;Add this helper to &lt;code&gt;~/.bashrc&lt;/code&gt; (or &lt;code&gt;~/.zshrc&lt;/code&gt;) on the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cf-ssh&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;%.trycloudflare.com&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="p"&gt;//[^[&lt;/span&gt;:alnum:]-]/&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveInterval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveCountMax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ProxyCommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cloudflared access ssh --hostname %h"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.trycloudflare.com"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cf-ssh some-random-words.trycloudflare.com
&lt;span class="c"&gt;# or just&lt;/span&gt;
cf-ssh some-random-words
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ServerAliveInterval 15&lt;/code&gt; sends a small encrypted packet every 15 seconds of silence.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ServerAliveCountMax 3&lt;/code&gt; gives up only after three missed replies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To the filter, the session now looks busy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: Server side
&lt;/h2&gt;

&lt;p&gt;If the server is not at fault, let’s make the client busy. The other end can time out too. &lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;ClientAliveInterval&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
&lt;span class="k"&gt;ClientAliveCountMax&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reload:&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;systemctl restart sshd
&lt;span class="c"&gt;# or on systems without systemd:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service ssh restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both sides now communicate with each other. Both of them are now busy. The network does not filter, nor does the tunnel ever see a dead link.&lt;/p&gt;

&lt;p&gt;And there you go, the problem is 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgd7d57qsjn9bfikivfwm.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%2Fgd7d57qsjn9bfikivfwm.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  NOTE:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;trycloudflare.com&lt;/code&gt; URLs are semi-public and change every time you restart the tunnel. Use key-based authentication only, never passwords, and shut the tunnel down when you’re finished.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>linux</category>
      <category>devops</category>
      <category>ssh</category>
    </item>
  </channel>
</rss>
