<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://blog.yechiel.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.yechiel.me/" rel="alternate" type="text/html" /><updated>2026-05-28T08:26:32-04:00</updated><id>https://blog.yechiel.me/feed.xml</id><title type="html">Rabbi On Rails</title><subtitle>Rabbi on Rails: where I pontificate on matters related to Judaism, tech, career, ethical AI and more.</subtitle><author><name>Yechiel Kalmenson</name></author><entry><title type="html">Shipping Your Machine: Building a Container in 50 Lines of Code (Part 2)</title><link href="https://blog.yechiel.me/shipping-your-machine-building-a-container-in-50-lines-of-code-part-2-4cm4" rel="alternate" type="text/html" title="Shipping Your Machine: Building a Container in 50 Lines of Code (Part 2)" /><published>2026-05-26T11:03:07-04:00</published><updated>2026-05-26T11:03:07-04:00</updated><id>https://blog.yechiel.me/shipping-your-machine-building-a-container-in-50-lines-of-code-part-2-4cm4</id><content type="html" xml:base="https://blog.yechiel.me/shipping-your-machine-building-a-container-in-50-lines-of-code-part-2-4cm4"><![CDATA[<h2 id="welcome-back-to-the-jailhouse">Welcome Back to the Jailhouse</h2>

<p>In <a href="/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma">Part 1 of this series</a>, we built the foundation of our container using Go. We successfully used the <code class="language-plaintext highlighter-rouge">CLONE_NEWUTS</code> namespace and process forking to isolate our container’s hostname from the host machine.</p>

<p>But we still have a massive security flaw. Right now, if we drop into our container’s bash shell, we can still see all of the host’s files. We could easily <code class="language-plaintext highlighter-rouge">cd</code> straight out of our “isolated” environment and mess with the host machine.</p>

<p>Let’s lock it down.</p>

<h3 id="chroot-to-jail"><code class="language-plaintext highlighter-rouge">chroot</code> to Jail</h3>

<p>Linux has a wonderful system call called <code class="language-plaintext highlighter-rouge">chroot</code> (short for “change root”). It lets us change the root directory (<code class="language-plaintext highlighter-rouge">/</code>) for a given process. As far as the process is concerned, the directory we point <code class="language-plaintext highlighter-rouge">chroot</code> to <em>is the entire universe</em>. Anything outside of it simply doesn’t exist.</p>

<p>Let’s update our <code class="language-plaintext highlighter-rouge">child()</code> function to set the root directory to our current working directory:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">child</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running in new child process %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
	
	<span class="n">must</span><span class="p">(</span><span class="n">syscall</span><span class="o">.</span><span class="n">Sethostname</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"container"</span><span class="p">)))</span>
	
	<span class="c">// Get current directory and lock the process inside it</span>
	<span class="n">pwd</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getwd</span><span class="p">()</span>
	<span class="n">must</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>

	<span class="n">must</span><span class="p">(</span><span class="n">syscall</span><span class="o">.</span><span class="n">Chroot</span><span class="p">(</span><span class="n">pwd</span><span class="p">))</span>
	<span class="c">// chroot changes the root, but doesn't automatically move us there. </span>
	<span class="c">// We must explicitly change our working directory to the new root!</span>
	<span class="n">must</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Chdir</span><span class="p">(</span><span class="s">"/"</span><span class="p">))</span>
	
	<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="p">]</span><span class="o">...</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdin</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdout</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stderr</span>
	
	<span class="n">must</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">())</span>
<span class="p">}</span>

</code></pre></div></div>

<p>Run <code class="language-plaintext highlighter-rouge">sudo go run main.go run /bin/bash</code>.</p>

<p><strong>Crash!</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>panic: fork/exec /bin/bash: no such file or directory
</code></pre></div></div>
<p>What happened?</p>

<p>We just told our process that our current directory is the entire universe. So, when we ask <code class="language-plaintext highlighter-rouge">exec.Command</code> to run <code class="language-plaintext highlighter-rouge">/bin/bash</code>, it isn’t looking at your computer’s actual hard drive anymore. It is looking inside your project folder for a directory called <code class="language-plaintext highlighter-rouge">bin</code> containing an executable called <code class="language-plaintext highlighter-rouge">bash</code>.</p>

<p>Because our current directory doesn’t have those, it fails! We need an actual root filesystem to provide the basic binaries our shell expects.</p>

<p><em>(Note: Production container runtimes like runC actually use a more advanced system call called <code class="language-plaintext highlighter-rouge">pivot_root</code> for better security, but <code class="language-plaintext highlighter-rouge">chroot</code> is perfect for understanding the core concept!).</em></p>

<h3 id="the-image">The Image</h3>

<p>To fix this, we need to provide an actual root filesystem that contains the basic folders and binaries (like /bin/bash) that our shell expects.</p>

<p>You can grab a basic Ubuntu root filesystem yourself using Docker. Open a new terminal tab and run these exact commands in your project directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Use docker to start a Ubuntu container and then export its filesystem to a compressed file called ubuntu.tar</span>
docker <span class="nb">export</span> <span class="si">$(</span>docker create ubuntu<span class="si">)</span> <span class="o">&gt;</span> ubuntu.tar

<span class="c"># Create a directory called ubuntu-rootfs and unzip your tar file into it</span>
<span class="nb">mkdir </span>ubuntu-rootfs
<span class="nb">tar</span> <span class="nt">-xf</span> ubuntu.tar <span class="nt">-C</span> ubuntu-rootfs
</code></pre></div></div>
<p>This creates a folder called <code class="language-plaintext highlighter-rouge">ubuntu-rootfs</code> containing a complete, brand-new Ubuntu file system.</p>

<p>Assuming you have that folder in your project directory, let’s change our <code class="language-plaintext highlighter-rouge">chroot</code> call to point to it as follows:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="n">must</span><span class="p">(</span><span class="n">syscall</span><span class="o">.</span><span class="n">Chroot</span><span class="p">(</span><span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">pwd</span><span class="p">,</span> <span class="s">"ubuntu-rootfs"</span><span class="p">)))</span>
	<span class="n">must</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Chdir</span><span class="p">(</span><span class="s">"/"</span><span class="p">))</span>

</code></pre></div></div>

<p>Now, when we run <code class="language-plaintext highlighter-rouge">sudo go run main.go run /bin/bash</code>, everything works perfectly!</p>

<p>You can run <code class="language-plaintext highlighter-rouge">ls /</code> and you will only see the files inside your <code class="language-plaintext highlighter-rouge">ubuntu-rootfs</code> directory. Try running <code class="language-plaintext highlighter-rouge">cd ..</code> to escape, and you will find yourself in the exact same directory as before. You cannot access the host machine at all.</p>

<h3 id="pids-and-proc">PIDs and /proc</h3>

<p>We’re ready for the next step. If you remember, when we ran <code class="language-plaintext highlighter-rouge">docker run -it ubuntu /bin/bash</code> back in the beginning of Part 1, one of the ways we could tell we were in an isolated container was by running <code class="language-plaintext highlighter-rouge">ps aux</code> and observing only two processes running with very low PIDs.</p>

<p>Let’s try to replicate that. While inside our new container, try running <code class="language-plaintext highlighter-rouge">ps aux</code> to view the running processes.</p>

<p>It breaks with an error:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error, <span class="k">do </span>this: mount <span class="nt">-t</span> proc proc /proc
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">ps</code> command works by reading the <code class="language-plaintext highlighter-rouge">/proc</code> directory, which is a special virtual filesystem in Linux that contains live data about running processes. Our isolated root filesystem has an empty <code class="language-plaintext highlighter-rouge">/proc</code> folder, and the operating system hasn’t been told to attach the live process data to it. Because it’s empty, <code class="language-plaintext highlighter-rouge">ps</code> fails!</p>

<p>To fix this, we need to do two things:</p>

<ol>
  <li>Give our container its own isolated Process IDs (PIDs) using namespaces.</li>
  <li>Mount the <code class="language-plaintext highlighter-rouge">proc</code> filesystem so commands like <code class="language-plaintext highlighter-rouge">ps</code> can read it.</li>
</ol>

<p>First, update the <code class="language-plaintext highlighter-rouge">SysProcAttr</code> in the <code class="language-plaintext highlighter-rouge">run()</code> function to include the PID and Mount namespaces. (Note: <code class="language-plaintext highlighter-rouge">CLONE_NEWNS</code> stands for “New Namespace”, but it specifically refers to the Mount namespace! It just happens to be the first namespace added to the Linux kernel and back then no one thought they might end up needing more so they just called it “namespace” 🤷).</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="n">cmd</span><span class="o">.</span><span class="n">SysProcAttr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">syscall</span><span class="o">.</span><span class="n">SysProcAttr</span><span class="p">{</span>
		<span class="n">Cloneflags</span><span class="o">:</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWUTS</span> <span class="o">|</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWPID</span> <span class="o">|</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWNS</span><span class="p">,</span>
	<span class="p">}</span>

</code></pre></div></div>

<p>Next, mount the <code class="language-plaintext highlighter-rouge">proc</code> directory inside our <code class="language-plaintext highlighter-rouge">child()</code> function, right after we <code class="language-plaintext highlighter-rouge">chroot</code>. We will also use Go’s <code class="language-plaintext highlighter-rouge">defer</code> keyword to ensure we unmount it and clean up after ourselves when the function exits:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="n">must</span><span class="p">(</span><span class="n">syscall</span><span class="o">.</span><span class="n">Chroot</span><span class="p">(</span><span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">pwd</span><span class="p">,</span> <span class="s">"ubuntu-rootfs"</span><span class="p">)))</span>
	<span class="n">must</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Chdir</span><span class="p">(</span><span class="s">"/"</span><span class="p">))</span>
	
	<span class="c">// Mount the proc filesystem</span>
	<span class="n">must</span><span class="p">(</span><span class="n">syscall</span><span class="o">.</span><span class="n">Mount</span><span class="p">(</span><span class="s">"proc"</span><span class="p">,</span> <span class="s">"proc"</span><span class="p">,</span> <span class="s">"proc"</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="s">""</span><span class="p">))</span>
	<span class="c">// Clean up after ourselves when the function exits</span>
	<span class="k">defer</span> <span class="n">syscall</span><span class="o">.</span><span class="n">Unmount</span><span class="p">(</span><span class="s">"proc"</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Now, run your container and type <code class="language-plaintext highlighter-rouge">ps aux</code>. You’ll see only three processes running: <code class="language-plaintext highlighter-rouge">exe</code> (our Go program) running as PID 1, <code class="language-plaintext highlighter-rouge">bash</code> running as PID 2, and the <code class="language-plaintext highlighter-rouge">ps</code> command we just ran!</p>

<h3 id="cgroups-keeping-it-civil">Cgroups (Keeping it Civil)</h3>

<p>We have our invisibility cloak (Namespaces) and our isolated universe (<code class="language-plaintext highlighter-rouge">chroot</code>). But what happens if we write an infinite <code class="language-plaintext highlighter-rouge">while</code> loop inside our container that eats up all the CPU and memory?</p>

<p>It would completely crash the host machine!</p>

<p>To prevent our container from using up all of our resources, Linux uses <strong>cgroups</strong> (Control Groups). Cgroups act as the bouncer, ensuring no single container uses more than its fair share of resources.</p>

<p>To set up a cgroup, we can lean on a famous Linux philosophy: “Everything is a file.” This means we can configure the kernel’s resource limits by creating specific directories and writing text into special files.</p>

<p>Let’s add a quick helper function to our <code class="language-plaintext highlighter-rouge">main.go</code> file to limit the maximum number of processes our container is allowed to spawn to 20:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">cg</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">cgroups</span> <span class="o">:=</span> <span class="s">"/sys/fs/cgroup/"</span>
	<span class="n">pids</span> <span class="o">:=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">cgroups</span><span class="p">,</span> <span class="s">"pids"</span><span class="p">)</span>
	
	<span class="c">// 1. Create a new cgroup for our container</span>
	<span class="n">containerCgroup</span> <span class="o">:=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">pids</span><span class="p">,</span> <span class="s">"my-container"</span><span class="p">)</span>
	<span class="n">os</span><span class="o">.</span><span class="n">Mkdir</span><span class="p">(</span><span class="n">containerCgroup</span><span class="p">,</span> <span class="m">0755</span><span class="p">)</span>
	
	<span class="c">// 2. Write the limit into the cgroup file (max 20 processes)</span>
	<span class="n">must</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">WriteFile</span><span class="p">(</span><span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">containerCgroup</span><span class="p">,</span> <span class="s">"pids.max"</span><span class="p">),</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"20"</span><span class="p">),</span> <span class="m">0700</span><span class="p">))</span>
	
	<span class="c">// 3. Add our current process to this cgroup</span>
	<span class="n">must</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">WriteFile</span><span class="p">(</span><span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">containerCgroup</span><span class="p">,</span> <span class="s">"cgroup.procs"</span><span class="p">),</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="n">strconv</span><span class="o">.</span><span class="n">Itoa</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getpid</span><span class="p">())),</span> <span class="m">0700</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s break down what this function is doing:</p>

<ol>
  <li>
    <p><strong>Create the group:</strong> By making a new directory inside <code class="language-plaintext highlighter-rouge">/sys/fs/cgroup/pids</code>, the Linux kernel automatically creates a new Control Group for us.</p>
  </li>
  <li>
    <p><strong>Set the rule:</strong> Inside that new directory, Linux automatically generates a file called <code class="language-plaintext highlighter-rouge">pids.max</code>. We open that file and write the text <code class="language-plaintext highlighter-rouge">"20"</code> into it. This establishes a rule that our process will only be allowed to run 20 sub-processes.</p>
  </li>
  <li>
    <p><strong>Enforce the rule:</strong> Linux also generates a file called <code class="language-plaintext highlighter-rouge">cgroup.procs</code>. We get our Go program’s current Process ID (<code class="language-plaintext highlighter-rouge">os.Getpid()</code>) and write it into this file. This tells the kernel, <em>“Hey, apply the rules of this folder to me!”</em></p>
  </li>
</ol>

<p>Finally, let’s call this function inside our <code class="language-plaintext highlighter-rouge">run()</code> function, right before we execute our child process:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
	
	<span class="n">args</span> <span class="o">:=</span> <span class="nb">append</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">"child"</span><span class="p">},</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">]</span><span class="o">...</span><span class="p">)</span>
	<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="s">"/proc/self/exe"</span><span class="p">,</span> <span class="n">args</span><span class="o">...</span><span class="p">)</span>
	
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdin</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdout</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stderr</span>
	
	<span class="n">cmd</span><span class="o">.</span><span class="n">SysProcAttr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">syscall</span><span class="o">.</span><span class="n">SysProcAttr</span><span class="p">{</span>
		<span class="n">Cloneflags</span><span class="o">:</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWUTS</span> <span class="o">|</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWPID</span> <span class="o">|</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWNS</span><span class="p">,</span>
	<span class="p">}</span>
	
	<span class="c">// Set up our resource limits!</span>
	<span class="n">cg</span><span class="p">()</span>
	
	<span class="n">must</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And just like that, our container is officially resource-limited! Because cgroups inherit down to child processes, everything that runs inside our container is bound by this rule. If a malicious script inside tries to execute a “fork-bomb” (a script that endlessly copies itself to freeze the computer), the kernel will step in and aggressively kill it the second it hits 20 processes.</p>

<h3 id="the-reveal">The Reveal</h3>

<p>If you put all of this together, we just built Docker from scratch in about 50 lines of code.</p>

<p>Containers aren’t magic. They aren’t heavyweight VMs. They are simply standard Linux processes wrapped in namespaces, jailed in a specific directory, and policed by cgroups.</p>

<p>In fact, you don’t even need Go to do this. You can trigger the exact same isolation using a single line of bash:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>unshare <span class="nt">--uts</span> <span class="nt">--pid</span> <span class="nt">--mount</span> <span class="nt">--fork</span> <span class="nt">--root</span><span class="o">=</span>/home/ubuntu-rootfs <span class="nt">--mount-proc</span> /bin/bash

</code></pre></div></div>

<p>And there you have it! The next time someone says “it works on my machine,” you know exactly what it takes to ship their machine to production.</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="go" /><category term="containers" /><category term="beginners" /><category term="docker" /><summary type="html"><![CDATA[Welcome Back to the Jailhouse]]></summary></entry><entry><title type="html">Shipping Your Machine: Building a Container in 50 Lines of Code (Part 1)</title><link href="https://blog.yechiel.me/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma" rel="alternate" type="text/html" title="Shipping Your Machine: Building a Container in 50 Lines of Code (Part 1)" /><published>2026-05-20T16:09:10-04:00</published><updated>2026-05-20T16:09:10-04:00</updated><id>https://blog.yechiel.me/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma</id><content type="html" xml:base="https://blog.yechiel.me/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma"><![CDATA[<h2 id="the-works-on-my-machine-problem">The “Works on My Machine” Problem</h2>

<p>We’ve all been there. You spend days writing a new feature. You test it locally, everything passes, you push it to production, and… <em>boom</em>. It crashes immediately.</p>

<p><em>“But it works on my machine!”</em> you cry out to your lead engineer.</p>

<p><img src="/assets/images/posts/2026-05-20-shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma-36eaa8.webp" alt="A crying child tells an adult sitting next to him &quot;but it works on my machine&quot;, the adult tells him &quot;then we'll ship your machine&quot;, and that's how Docker was born." /></p>

<p>And that is exactly what containers are. They are a way to bundle up your application and the environment it runs in—your machine—so that it behaves exactly the same way in production as it does on your laptop.</p>

<p>But what <em>is</em> a container, really?</p>

<p>Most people have an intuition of containers as some kind of complicated application that runs on your computer and simulates another computer. That is the image I had in my head even after working on containers for a few months at Pivotal/VMware… <strong>and it’s a myth.</strong></p>

<p>The reality is a lot simpler, and a lot more interesting!</p>

<p>The truth is that a container is simply a directory on your computer—like any other directory—with a process running inside of it. What makes it a container and not just another process running inside a directory is that we use some clever built-in Linux features to trick the process into thinking that this directory <strong>is the entire computer</strong>. As far as this process is concerned, nothing exists outside of the directory we “trapped” it in and that directory is the entirety of its universe.</p>

<p>In this two-part series, we are going to demystify this illusion by building Docker from scratch in exactly 60 lines of Go code.</p>

<p><strong>Note:</strong> <em>Because containers rely heavily on the Linux Kernel, this tutorial will only run natively on a Linux machine. If you are following along on a Mac, you’ll need to spin up a Linux VM first, as Macs run on the Darwin kernel and don’t have these system calls!</em></p>

<p>Let’s get started!</p>

<h3 id="setting-the-stage">Setting the Stage</h3>

<p>We’ll be writing our container in Go. Why Go? Because it gives us incredibly clean access to underlying Linux system calls, which we’ll need to create our container illusion. This is the reason Docker, Kubernetes, and other cloud-native projects are all written in Go.</p>

<p>Let’s try to replicate the core behavior of Docker.</p>

<p>Normally, when using Docker, you run a command like:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-it</span> ubuntu /bin/bash
</code></pre></div></div>

<p>If you run that (assuming you have docker installed) you will see that you have been dropped into a new shell. This shell looks different than your original shell and you can tell it’s completely isolated from the rest of your machine (the host) in a few ways (it’s worth opening a new tab in your terminal so you can see these changes side by side):</p>

<ol>
  <li>
    <p><strong>Your prompt:</strong> Everyone’s prompt is different, but most have something in the beginning that looks like <code class="language-plaintext highlighter-rouge">[username]@[hostname]</code>. The prompt you see now probably looks like <code class="language-plaintext highlighter-rouge">root@[some-random-string]</code>.</p>
  </li>
  <li>
    <p><strong>Your hostname:</strong> If you type <code class="language-plaintext highlighter-rouge">hostname</code> in your host computer’s terminal, it will output your computer’s actual name. If you run <code class="language-plaintext highlighter-rouge">hostname</code> inside your container, on the other hand, you will see that same random sequence of characters from your prompt. Docker assigned this fake hostname to your container at random.</p>
  </li>
  <li>
    <p><strong>Your File System:</strong> If you type <code class="language-plaintext highlighter-rouge">ls /</code> inside your container you will see that none of the files from your computer’s actual root directory are there, instead you will see a fresh list of files and directories, exactly like you would find in a brand new Ubuntu installation.</p>
  </li>
  <li>
    <p><strong>Your processes:</strong> If you type <code class="language-plaintext highlighter-rouge">ps aux</code> in your container you will find only 2 processes with very low Process IDs (PIDs)—typically PID 1 for the shell process you’re in and another low number PID for the <code class="language-plaintext highlighter-rouge">ps</code> command you just ran. Meanwhile, running <code class="language-plaintext highlighter-rouge">ps aux</code> on your host machine will show a massive list of running processes, many of them with very high PIDs.</p>
  </li>
</ol>

<p>We will try to replicate this behavior from scratch, with a few minor differences.</p>

<p>Our goal is to run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>go run main.go run /bin/bash
</code></pre></div></div>

<p>And if we do everything right the behavior will be mostly the same. We will drop into a new shell where:</p>

<ol>
  <li>
    <p>We will see a different hostname than our existing one.</p>
  </li>
  <li>
    <p>The root directory we will have access to will not be the root directory of our computer, instead it will be a new root directory.</p>
  </li>
  <li>
    <p>If we inspect the processes running using <code class="language-plaintext highlighter-rouge">ps</code> we will see only the processes running in our program and not all the processes on our computer.</p>
  </li>
</ol>

<p>So let’s start!</p>

<h3 id="show-me-the-code">Show Me the Code</h3>

<p>Let’s build this step by step. Create a new directory on your Linux computer, make a file named main.go, and let’s add our initial boilerplate:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"os"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c">// os.Args is a list of everything typed in the terminal.</span>
	<span class="c">// [0] is the program name (main.go), [1] is our command ("run")</span>
	<span class="k">switch</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">{</span>
	<span class="k">case</span> <span class="s">"run"</span><span class="o">:</span>
		<span class="n">run</span><span class="p">()</span>
	<span class="k">default</span><span class="o">:</span>
		<span class="nb">panic</span><span class="p">(</span><span class="s">"Invalid argument"</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
	<span class="c">// os.Args[2:] takes everything AFTER the "run" command</span>
	<span class="c">// e.g., ["echo", "hello", "world"]</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
<span class="p">}</span>

</code></pre></div></div>

<p>In Go, <code class="language-plaintext highlighter-rouge">os.Args</code> captures every word you type into the terminal as a list (a slice) of strings. We are telling our program to look at the second word (<code class="language-plaintext highlighter-rouge">os.Args[1]</code>). If it says “run”, we trigger our <code class="language-plaintext highlighter-rouge">run()</code> function which, for now does nothing but print all our arguments to the terminal.</p>

<p>If we run this in our terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go run main.go run <span class="nb">echo </span>hello world
Running <span class="o">[</span><span class="nb">echo </span>hello world]

</code></pre></div></div>

<p>Awesome. It successfully reads our command. But it’s just printing text; it’s not actually executing the command yet.</p>

<p>To make it execute the command, we need to wire up Go’s <code class="language-plaintext highlighter-rouge">os/exec</code> package (a package for executing commands on our computer). Let’s update our file:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"os"</span>
	<span class="s">"os/exec"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">switch</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">{</span>
	<span class="k">case</span> <span class="s">"run"</span><span class="o">:</span>
		<span class="n">run</span><span class="p">()</span>
	<span class="k">default</span><span class="o">:</span>
		<span class="nb">panic</span><span class="p">(</span><span class="s">"Invalid argument"</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
	
        <span class="c">// 1. Define the command we want to execute. This includes the third element in our array </span>
	<span class="c">// (the actual command, in our case "echo"), as well as any optional arguments we may </span>
	<span class="c">// want to pass to it ("hello world").</span>
	<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="p">]</span><span class="o">...</span><span class="p">)</span>
	
	<span class="c">// 2. Wire up the plumbing</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdin</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdout</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stderr</span>
	
	<span class="c">// 3. Run the command and handle any errors</span>
	<span class="n">must</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">())</span>
<span class="p">}</span>

<span class="c">// A tiny helper function to catch errors and crash the program </span>
<span class="c">// cleanly if something goes wrong.</span>
<span class="k">func</span> <span class="n">must</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="nb">panic</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>Let’s look at what we just added to <code class="language-plaintext highlighter-rouge">run()</code>:</p>

<ol>
  <li>
    <p><strong>Define the command:</strong> <code class="language-plaintext highlighter-rouge">exec.Command</code> takes the program we want to run (like <code class="language-plaintext highlighter-rouge">echo</code>) and any arguments we want to pass to it (<code class="language-plaintext highlighter-rouge">hello world</code>).</p>
  </li>
  <li>
    <p><strong>Wire up the plumbing:</strong> This part is crucial. By default, when a program spins up a new process, it runs invisibly in the background. By pointing the new command’s Standard Input, Output, and Error to our own <code class="language-plaintext highlighter-rouge">os.Stdin</code>, <code class="language-plaintext highlighter-rouge">os.Stdout</code>, and <code class="language-plaintext highlighter-rouge">os.Stderr</code>, we are attaching its “mouth and ears” directly to our terminal so we can actually interact with it.</p>
  </li>
  <li>
    <p><strong>Run it:</strong> We execute the command. Because Go requires explicit error handling, we wrapped it in a tiny <code class="language-plaintext highlighter-rouge">must()</code> helper function to keep our code readable.</p>
  </li>
</ol>

<p>Let’s test it out:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go run main.go run <span class="nb">echo </span>hello world
Running <span class="o">[</span><span class="nb">echo </span>hello world]
hello world
</code></pre></div></div>

<p>We actually get <code class="language-plaintext highlighter-rouge">hello world</code> echoed back at us by the system!</p>

<p>Even better, we can tell our program to drop us into an interactive shell:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go run main.go run /bin/bash
Running <span class="o">[</span>/bin/bash]
root@your-computer:/home/yechiel/docker-clone# 
</code></pre></div></div>

<p>However, this shell is absolutely not a container yet. If you type <code class="language-plaintext highlighter-rouge">hostname</code>, you’ll see your host machine’s actual name. If you type <code class="language-plaintext highlighter-rouge">ls /</code>, you can see all your host files. Right now, we have just written a fancy Go wrapper around a regular bash process.</p>

<p>It is time to start building the walls of our container.</p>

<h3 id="introducing-namespaces-the-invisibility-cloak">Introducing Namespaces (The Invisibility Cloak)</h3>

<p>To isolate our process from the rest of the computer, we need to put it inside a <strong>Namespace</strong>. A namespace is like an invisibility cloak that hides the rest of the computer from our process.</p>

<p>There are different namespaces that isolate different aspects of the system. Let’s start with the UTS namespace, which isolates the hostname.</p>

<p>To create a new namespace, we pass a specific “clone flag” to the system call spinning up our process. Update your <code class="language-plaintext highlighter-rouge">run()</code> function:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
	
	<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="p">]</span><span class="o">...</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdin</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdout</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stderr</span>
	
	<span class="c">// Set up the namespace!</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">SysProcAttr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">syscall</span><span class="o">.</span><span class="n">SysProcAttr</span><span class="p">{</span>
		<span class="n">Cloneflags</span><span class="o">:</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWUTS</span><span class="p">,</span>
	<span class="p">}</span>
	
	<span class="n">must</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">())</span>
<span class="p">}</span>

</code></pre></div></div>

<p>If you try to run the command now, you’ll get a “permission denied” error. Because changing a namespace requires modifying kernel-level structures, you now need root privileges.</p>

<p>Instead, let’s run <code class="language-plaintext highlighter-rouge">sudo go run main.go run /bin/bash</code>.</p>

<p>If you run <code class="language-plaintext highlighter-rouge">hostname</code> now you will still see your computer’s actual hostname. This is because, by default, Linux has your new process inherit the hostname of the host computer.</p>

<p>However, if you manually run <code class="language-plaintext highlighter-rouge">hostname container</code> inside this new bash shell, and <em>then</em> type <code class="language-plaintext highlighter-rouge">hostname</code>, you’ll see your hostname is now <code class="language-plaintext highlighter-rouge">container</code>. But if you switch to a terminal tab on your host machine and type <code class="language-plaintext highlighter-rouge">hostname</code>, you’ll see your actual computer’s name hasn’t changed at all! Our process is successfully isolated.</p>

<h3 id="forking-the-process">Forking the Process</h3>

<p>Did you notice something weird when you manually changed the hostname in the last step? Even though typing <code class="language-plaintext highlighter-rouge">hostname</code> printed out <code class="language-plaintext highlighter-rouge">container</code>, your actual bash prompt likely still said <code class="language-plaintext highlighter-rouge">root@your-computer#</code>!</p>

<p>This happens because the bash shell reads the machine’s hostname <em>when it first starts up.</em> Changing the hostname after the shell is already running doesn’t dynamically update your prompt. If we want our container to feel like a truly isolated environment from the second it boots, we need our Go program to change the hostname automatically <em>before</em> we execute /bin/bash.</p>

<p>Luckily, Go has a function to change the hostname: <code class="language-plaintext highlighter-rouge">syscall.Sethostname()</code>.</p>

<p>But here we run into a “chicken and egg” problem: where do we call that function?</p>

<p>If we call it before <code class="language-plaintext highlighter-rouge">cmd.Run()</code>, it executes on our host machine and changes our actual computer’s name (bad!). If we call it after <code class="language-plaintext highlighter-rouge">cmd.Run()</code>, it’ll only run after our bash shell has already exited (still bad!).</p>

<p>We need a middleman. We need our Go program to cross over into the new namespace, set the hostname, and <em>then</em> execute the <code class="language-plaintext highlighter-rouge">/bin/bash</code> command.</p>

<p>To do this, we are going to have our Go program run <strong>itself</strong> again as a child process.</p>

<p>Let’s update our <code class="language-plaintext highlighter-rouge">main.go</code> file. We will update the <code class="language-plaintext highlighter-rouge">main()</code> and <code class="language-plaintext highlighter-rouge">run()</code> functions, and add a brand new <code class="language-plaintext highlighter-rouge">child()</code> function:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"os"</span>
	<span class="s">"os/exec"</span>
	<span class="s">"syscall"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">switch</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">{</span>
	<span class="k">case</span> <span class="s">"run"</span><span class="o">:</span>
		<span class="n">run</span><span class="p">()</span>
	<span class="k">case</span> <span class="s">"child"</span><span class="o">:</span>
		<span class="n">child</span><span class="p">()</span> <span class="c">// We added a new command here!</span>
	<span class="k">default</span><span class="o">:</span>
		<span class="nb">panic</span><span class="p">(</span><span class="s">"Invalid argument"</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
	
	<span class="c">// /proc/self/exe is a special Linux file that points to the program </span>
	<span class="c">// currently running. So, our program is calling itself!</span>
	<span class="c">// We pass "child" as the first argument, followed by the rest of our commands.</span>
	<span class="n">args</span> <span class="o">:=</span> <span class="nb">append</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">"child"</span><span class="p">},</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">]</span><span class="o">...</span><span class="p">)</span>
	<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="s">"/proc/self/exe"</span><span class="p">,</span> <span class="n">args</span><span class="o">...</span><span class="p">)</span>
	
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdin</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdout</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stderr</span>
	
	<span class="n">cmd</span><span class="o">.</span><span class="n">SysProcAttr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">syscall</span><span class="o">.</span><span class="n">SysProcAttr</span><span class="p">{</span>
		<span class="n">Cloneflags</span><span class="o">:</span> <span class="n">syscall</span><span class="o">.</span><span class="n">CLONE_NEWUTS</span><span class="p">,</span>
	<span class="p">}</span>
	
	<span class="n">must</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">())</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">child</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running in new child process %v </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="o">:</span><span class="p">])</span>
	
	<span class="c">// We are now inside the namespace! It is safe to change the hostname.</span>
	<span class="n">must</span><span class="p">(</span><span class="n">syscall</span><span class="o">.</span><span class="n">Sethostname</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"container"</span><span class="p">)))</span>
	
	<span class="c">// Now we run the actual command the user requested (like /bin/bash)</span>
	<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">2</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="p">]</span><span class="o">...</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdin</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stdout</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stderr</span>
	
	<span class="n">must</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">())</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">must</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="nb">panic</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>Let’s break down the magic happening in <code class="language-plaintext highlighter-rouge">exec.Command("/proc/self/exe", args...)</code>:</p>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">"/proc/self/exe"</code>: In Linux, <code class="language-plaintext highlighter-rouge">/proc</code> is a special directory that holds information about all running processes. If you check what’s in there (by running <code class="language-plaintext highlighter-rouge">ls /proc</code>) you will see a whole bunch of directories that have numbers as names. Each number is a PID and that directory contains information about the process with that PID. <code class="language-plaintext highlighter-rouge">/proc/self/exe</code> is essentially a shortcut to the directory for the process that’s currently running (in our case <code class="language-plaintext highlighter-rouge">go run main.go</code>).</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">args...</code>: We are taking the word <code class="language-plaintext highlighter-rouge">"child"</code> and appending the rest of the user’s commands (like <code class="language-plaintext highlighter-rouge">/bin/bash</code>) to it. The <code class="language-plaintext highlighter-rouge">...</code> syntax in Go simply “unpacks” the list so it can be passed as individual arguments.</p>
  </li>
</ol>

<p>So, when you type <code class="language-plaintext highlighter-rouge">go run main.go run /bin/bash</code>, here is what happens:</p>

<ol>
  <li>
    <p>The program starts, sees <code class="language-plaintext highlighter-rouge">"run"</code>, and triggers the <code class="language-plaintext highlighter-rouge">run()</code> function.</p>
  </li>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">run()</code> function sets up the invisibility cloak (the <code class="language-plaintext highlighter-rouge">NEWUTS</code> namespace).</p>
  </li>
  <li>
    <p>Inside that cloak, it runs itself again, but this time passing the command <code class="language-plaintext highlighter-rouge">"child"</code>.</p>
  </li>
  <li>
    <p>The program starts a second time, sees <code class="language-plaintext highlighter-rouge">"child"</code>, and triggers the <code class="language-plaintext highlighter-rouge">child()</code> function.</p>
  </li>
  <li>
    <p>Because we are now safely inside the namespace, it changes the hostname to <code class="language-plaintext highlighter-rouge">container</code> and finally runs <code class="language-plaintext highlighter-rouge">/bin/bash</code>.</p>
  </li>
</ol>

<p>Let’s test it. Run <code class="language-plaintext highlighter-rouge">sudo go run main.go run /bin/bash</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>go run main.go run /bin/bash
Running <span class="o">[</span>/bin/bash]
Running <span class="k">in </span>new child process <span class="o">[</span>/bin/bash]
root@container:/home/yechiel/docker-clone# 
</code></pre></div></div>

<p>Look at that prompt! We’ve successfully isolated the hostname programmatically before bash loaded. You’ll see both print statements execute, and if you type <code class="language-plaintext highlighter-rouge">hostname</code> in the new shell, it will say <code class="language-plaintext highlighter-rouge">container</code>.</p>

<p>We are still not in a proper container, though. If you run <code class="language-plaintext highlighter-rouge">ls /</code>, you will see all the files on your host machine, and you can even use <code class="language-plaintext highlighter-rouge">cd ..</code> to “escape” the directory entirely. Worse, if you were running this in a shared cloud environment, you would theoretically be able to see everyone else’s files and running processes—a massive security risk! We need a way to lock our process down so it can’t escape its designated environment.</p>

<p>In Part 2, we will tackle the final pieces of the container puzzle: jailing the file system, isolating the Process IDs (PIDs) so our container can only see itself, and stopping infinite loops from crashing the host computer using <code class="language-plaintext highlighter-rouge">cgroups</code>. Stay tuned!</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="docker" /><category term="go" /><category term="containers" /><category term="linux" /><summary type="html"><![CDATA[The “Works on My Machine” Problem]]></summary></entry><entry><title type="html">Refactoring Your Bitachon: Moving From Monolith to Modular</title><link href="https://blog.yechiel.me/decoupling-your-livelihood-79c7a122a44d" rel="alternate" type="text/html" title="Refactoring Your Bitachon: Moving From Monolith to Modular" /><published>2025-12-22T12:04:12-05:00</published><updated>2025-12-22T12:04:12-05:00</updated><id>https://blog.yechiel.me/2025-12-22_Refactoring-Your-Bitachon--Moving-From-Monolith-to-Modular</id><content type="html" xml:base="https://blog.yechiel.me/decoupling-your-livelihood-79c7a122a44d"><![CDATA[<hr />

<h3 id="refactoring-your-bitachon-moving-from-monolith-tomodular">Refactoring Your Bitachon: Moving From Monolith to Modular</h3>

<p>I was recently learning <strong>Shaar HaBitachon</strong> (“<em>The Gate of Trust</em>”) — a section of the classic 11th-century work Chovot Halevavot (“<em>Duties of the Heart”)</em> by Rabbeinu Bachya ibn Pekuda dedicated to cultivating trust in G-d — with my Chavruta (study partner). (By the way, shout-out to this amazing book! If you aren’t learning it yet, I highly recommend picking it up! It’s a game-changer for your mindset!)</p>

<p>We were learning a part in <a href="https://www.chabad.org/library/article_cdo/aid/5478430/jewish/Chapter-Five-Part-4-Proper-Attitude.htm"><strong>Chapter Five</strong></a> which discusses the proper attitude one should have towards the “means” (<em>hishtadlut</em>, or personal effort) we employ to make a living.</p>

<p>Rabbeinu Bachya, the author, explains that there is a fundamental difference between someone who has Bitachon (trust in G-d) and someone who trusts in their own efforts:</p>

<blockquote>
  <p><em>“While a person who relies on G-d also involves himself in various means of obtaining his livelihood… he doesn’t rely on them, nor does he expect them to either benefit him or cause him harm unless G-d wills it to be.</em></p>
</blockquote>

<blockquote>
  <p><em>The only reason he involves himself in them is his choice to carry out the service of the Creator, Who instructed him to involve himself in the world…”</em></p>
</blockquote>

<p>Contrast this with the person <strong>without Bitachon</strong> :</p>

<blockquote>
  <p><em>“However, a person who does not rely on G-d involves himself in the means of pursuing his livelihood because he relies on them for help and protection… If they do indeed help him, then he will praise them…</em></p>
</blockquote>

<blockquote>
  <p><em>If, however, they do not help him, then he will abandon them, reject them, and turn his desire away from them.”</em></p>
</blockquote>

<p>As I was reading this, it hit me: <strong>This is exactly the concept of Decoupling in programming.</strong></p>

<h3 id="spaghetti-code-vs-modulardesign">Spaghetti Code vs. Modular Design</h3>

<p>As developers, we know the pain of “tightly coupled” code. That’s when Module A is so knowledgeable about, and dependent on, the inner workings of Module B that you can’t touch one without breaking the other. If you want to swap out your database from MySQL to Postgres, but your business logic is writing raw SQL queries directly inside the controller, you’re in for a nightmare. Everything is tangled. The logic <em>depends</em> on the specific implementation.</p>

<p>Good architecture, on the other hand, strives for <strong>Decoupling</strong>.</p>

<p>You define an interface. Your business logic requests data, but it doesn’t care <em>how</em> that data is retrieved. You can swap out the database, change the API, or refactor the entire backend — and as long as the interface remains the same, the application keeps running smoothly. The logic is independent of the implementation details.</p>

<h3 id="refactoring-yourbitachon">Refactoring Your Bitachon</h3>

<p><em>Shaar HaBitachon</em> is teaching us to refactor our lives to be <strong>loosely coupled</strong>.</p>

<p>A person with true Bitachon has “decoupled” their livelihood (<em>Parnassah</em>) from their job.</p>

<ul>
  <li><strong>The Interface:</strong> G-d’s promise to sustain us.</li>
  <li><strong>The Implementation:</strong> The current job, gig, or business deal (the means/effort).</li>
</ul>

<p>When you are tightly coupled to your job, you are living in a legacy codebase full of dependencies. You think, “This job is the <em>only</em> way I can pay my mortgage.” That’s a fragile architecture. If that specific “module” (the job) crashes, your whole system goes down.</p>

<p>However, when you live with Bitachon, you realize that your livelihood comes from the “Sustenance Service” (G-d), and your job is just one interchangeable module used to deliver it.</p>

<p>If you lose your job, or a deal falls through? It’s not a system failure. It’s just a hot-swap. G-d is simply deprecating one method and initializing another. You don’t panic because your “Sustenance Provider” hasn’t changed — only the delivery mechanism has.</p>

<h3 id="the-whymatters">The “Why” Matters</h3>

<p>What really struck me in that chapter of <em>Shaar HaBitachon</em> is the motivation. The person with Bitachon still works just as hard! But why?</p>

<p>Not to <em>get</em> money, but to <strong>fulfill the will of G-d</strong>.</p>

<p>Just like we write modular code to make our applications robust, scalable, and maintainable, we should strive to make our trust in G-d robust and modular. We do the work because it’s the right thing to do (the spec), but we know that the result is handled entirely by the Core System.</p>

<p>So, the next time you’re refactoring a messy class or abstracting away a dependency, take a second to think: <strong>Is my own Bitachon tightly coupled to my job, or is it modular enough to handle whatever life throws at it?</strong></p>

<p><img src="/assets/images/posts/2025-12-22-2025-12-22_Refactoring-Your-Bitachon--Moving-From-Monolith-to-Modular-0.jpeg" alt="My Sha’ar Habitachon on my desk, in the background you can see my laptop as well as a few stickers strewn about on the desk." /></p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="" /><category term="" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Do You Even Wordle?</title><link href="https://blog.yechiel.me/do-you-even-wordle-e32de7da25fd" rel="alternate" type="text/html" title="Do You Even Wordle?" /><published>2022-01-27T21:56:58-05:00</published><updated>2022-01-27T21:56:58-05:00</updated><id>https://blog.yechiel.me/2022-01-28_Do-You-Even-Wordle-</id><content type="html" xml:base="https://blog.yechiel.me/do-you-even-wordle-e32de7da25fd"><![CDATA[<hr />

<h3 id="do-you-evenwordle">Do You Even Wordle?</h3>

<blockquote>

</blockquote>

<p>If you’re on social media, chances are that you are one of two kinds of people; those who post their Wordle results every day or those who are extremely annoyed by the yellow and green boxes flooding their feeds.</p>

<p>What is it about this word game that took the world by storm and made it so popular (at least until the next trend comes along)?</p>

<p>There are plenty of takes out there, but it seems to me that people find Wordle so wholesome because it stands in contrast to so much of what makes the toxic parts of the internet so toxic.</p>

<p>Unlike most apps and games out there, Wordle isn’t spying on you, it isn’t trying to drive engagement, it’s not addictive, and it isn’t trying to suck you into playing for hours every day. In fact, by only giving one word a day, it sets a pretty tight limit to how much time you can waste on it every day.</p>

<p>A lot has been said about the toxic parts of internet culture, and the large tech companies have been blamed for putting shareholder profits over the well-being of their users.</p>

<p>In Parshat Mishpatim, the first Parshah after the Jews got the Torah at Mount Sinai, the Torah goes into great detail about the laws of civil liability and the responsibility we have for damages caused by our actions and our property.</p>

<p>Just as a person can’t say “I just let my goat graze; it’s not my fault she ate up your tomato patch,” similarly, we can’t absolve ourselves from responsibility for the products and the technology we create.</p>

<p>Sure, profits are important, but they can’t come at the expense of the first principle; don’t be evil.</p>

<p>P.S. Really? “Knoll”? What’s up with <strong>that</strong>?</p>

<p><img src="/assets/images/posts/2022-01-28-2022-01-28_Do-You-Even-Wordle--0.jpg" alt="A screenshot of wordle results." /></p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="" /><category term="" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Who’s Going To RubyConf?</title><link href="https://blog.yechiel.me/whos-going-to-rubyconf-3g9m" rel="alternate" type="text/html" title="Who’s Going To RubyConf?" /><published>2021-11-05T15:00:05-04:00</published><updated>2021-11-05T15:00:05-04:00</updated><id>https://blog.yechiel.me/whos-going-to-rubyconf-3g9m</id><content type="html" xml:base="https://blog.yechiel.me/whos-going-to-rubyconf-3g9m"><![CDATA[<p><a href="https://rubyconf.org/">RubyConf</a> is starting next week (November 8-0), both in person in Denver and remotely online.</p>

<p>This year it’s especially exciting for me; it’s my first in-person conference in exactly two years (the vaccine mandates and mask policy helped me feel safe with the decision to attend in person), AND I’ll be giving a talk (which you should totally <a href="https://rubyconf.org/program/sessions#session-1164">come watch</a> if you’ll be there)!</p>

<p>Who else is planning on attending?</p>

<p>Come say hi (either in person or in the discord)!</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="ruby" /><category term="networking" /><category term="watercooler" /><category term="discuss" /><summary type="html"><![CDATA[RubyConf is starting next week (November 8-0), both in person in Denver and remotely online.]]></summary></entry><entry><title type="html">Talmudic Gems For Rails Developers</title><link href="https://blog.yechiel.me/talmudic-gems-for-rails-developers-77f6cb98c9ad" rel="alternate" type="text/html" title="Talmudic Gems For Rails Developers" /><published>2021-05-19T21:40:59-04:00</published><updated>2021-05-19T21:40:59-04:00</updated><id>https://blog.yechiel.me/2021-05-20_Talmudic-Gems-For-Rails-Developers</id><content type="html" xml:base="https://blog.yechiel.me/talmudic-gems-for-rails-developers-77f6cb98c9ad"><![CDATA[<hr />

<h3 id="talmudic-gems-for-rails-developers">Talmudic Gems For Rails Developers</h3>

<p><img src="/assets/images/posts/2021-05-20-2021-05-20_Talmudic-Gems-For-Rails-Developers-0.jpg" alt="A page from the talmud." /></p>

<p>A few weeks ago, I gave a talk at RailsConf titled Talmudic Gems For Rails Developers.</p>

<p>In the talk I discussed lessons I learned from a lifetime of Talmudic study that helped me in my journey as a developer, and which I felt could benefit other developers in their growth.</p>

<p>The talk is geared at any developer looking to grow using the timeless wisdom of the Talmudic sages, not just Rails developers.</p>

<p>I shared a transcript of the talk in the Torah &amp;&amp; Tech newsletter <a href="https://mailchi.mp/2bc93e8bd182/torah-tech-issue-6751637?e=bb66b91b8e">issue #123</a> (if you haven’t yet, you can sign up to the newsletter, and get the first year’s worth of newsletters in book format, at the Torah &amp;&amp; Tech website: <a href="https://torahandtech.dev/">torahandtech.dev</a>).</p>

<p>The talk has just been posted to YouTube, and I’m happy to share it with my audience!</p>

<iframe src="https://www.youtube.com/embed/d08GFQDT824?feature=oembed" width="700" height="393" frameborder="0" scrolling="no"></iframe>

<p>If you would like to delve into some of the sources I mentioned, you can find links in the source sheet I prepared <a href="https://gist.github.com/achasveachas/000e4e08e193140e372cd4819d025f4a">here</a>.</p>

<p>Happy Learning!</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="" /><category term="" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 3: Add A Dead-Man’s Switch To A Rails Application</title><link href="https://blog.yechiel.me/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f" rel="alternate" type="text/html" title="Part 3: Add A Dead-Man’s Switch To A Rails Application" /><published>2021-02-07T13:47:58-05:00</published><updated>2021-02-07T13:47:58-05:00</updated><id>https://blog.yechiel.me/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f</id><content type="html" xml:base="https://blog.yechiel.me/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f"><![CDATA[<h2 id="bonus-round">Bonus Round!</h2>

<p>In <a href="/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp">part one</a> and <a href="/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j">part two</a> of this series, we created a working deadman’s switch in our rails app.</p>

<p>This part isn’t necessary, but it can be helpful.</p>

<p>The deadman’s switch we wrote will trip if it isn’t reset after seven days (or however long you set it for). That may seem like a safe interval, but it would be nice to have a reminder if you haven’t reset it in a few days and the deadline is coming up.</p>

<p>I set my switch up to send me an SMS reminder if I didn’t reset it in 4 days, and then every day after that until the switch is either rest or if it trips.</p>

<p>That’s what we will do in part three of this tutorial.</p>

<p><strong>Note:</strong> this part of the tutorial uses Twilio to send SMS messages. You will need to sign up for a Twilio account. If you use my <a href="www.twilio.com/referral/PWhqJW">referral link</a> to sign up, you will get $10 of credit, which should be last a while for our use-case.</p>

<h2 id="getting-started">Getting Started</h2>

<p>The first thing we need to do before getting started is to make sure we have a Twilio account. For our purposes, a “demo” account is enough. A demo account is free; it means you will have to register your phone number to receive texts, and you can’t send SMS messages to other numbers, which is fine. Sending an SMS costs money ($0.0075 in the US); the $10 you get at sign-up by using my referral link should cover that for a long time.</p>

<p>After creating an account, follow the instructions to create a new project.</p>

<p>On the dashboard, you will need three pieces of information: The Account SID, Auth Token, and the account phone number.</p>

<p><img src="/assets/images/posts/2021-02-07-part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f-20715c.png" alt="screenshot of the dashboard showing where to find the needed info. " /></p>

<p>Save these credentials in a safe place; we will need them for the next part.</p>

<p><strong>Note:</strong> It’s a bad idea to put API keys in your code as plaintext, especially if you put your code somewhere that’s publicly accessible like GitHub. Someone can find them and use them to rack up quite the phone bill! Store them as environment variables instead. Heroku makes it easy to set <a href="https://devcenter.heroku.com/articles/config-vars#using-the-heroku-dashboard">environment variables</a>.</p>

<h2 id="text-me-maybe">Text me, maybe?</h2>

<p>Now we have everything we need to set up our script to text us when it doesn’t hear from us in a while.</p>

<p>First, put the following near the top of your rake task:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kp">include</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">DateHelper</span>
</code></pre></div></div>
<p>This isn’t strictly necessary, but it will let us use <code class="language-plaintext highlighter-rouge">ActionView</code>’s <code class="language-plaintext highlighter-rouge">time_ago_in_words</code> method to format a nice human-readable message.</p>

<p>Next, let’s set up a conditional to send the message only after the specified time that the app didn’t hear from you. I set mine up to start bugging me after three days:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="mi">3</span><span class="p">.</span><span class="nf">days</span><span class="p">.</span><span class="nf">ago</span> <span class="o">&gt;=</span> <span class="no">Deadman</span><span class="p">.</span><span class="nf">last_reset</span>

<span class="k">end</span>
</code></pre></div></div>
<p>(though for testing purposes, you can make the interval as small as a few seconds so you can trigger the SMS to send and make sure everything is working. Just remember to change it back before pushing to production).</p>

<p>Now, inside the <code class="language-plaintext highlighter-rouge">if</code> block, the first thing to do is to construct a message that the script will send:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">time_to_trigger</span> <span class="o">=</span> <span class="n">time_ago_in_words</span><span class="p">(</span><span class="no">Deadman</span><span class="p">.</span><span class="nf">last_reset</span> <span class="o">+</span> <span class="mi">7</span><span class="p">.</span><span class="nf">days</span><span class="p">)</span>
<span class="n">message_body</span> <span class="o">=</span> <span class="s2">"Your deadman switch will trigger in </span><span class="si">#{</span><span class="n">time_to_trigger</span><span class="si">}</span><span class="s2">, please log in to your account and reset it."</span>
</code></pre></div></div>
<p>You can, of course, edit this to fit your needs and taste.</p>

<p>Next, let’s set up a Twilio client using the credentials we got from the Twilio dashboard:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">twilio_client</span> <span class="o">=</span> <span class="no">Twilio</span><span class="o">::</span><span class="no">REST</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'TWILIO_ACCOUNT_SID'</span><span class="p">],</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'TWILIO_ACCOUNT_TOKEN'</span><span class="p">])</span>
</code></pre></div></div>

<p>Finally, you can  use the client to send an SMS message to your phone:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">twilio_client</span><span class="p">.</span><span class="nf">messages</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">from: </span><span class="no">ENV</span><span class="p">[</span><span class="s1">'TWILIO_PHONE_NUMBER'</span><span class="p">],</span> <span class="ss">to: </span><span class="no">ENV</span><span class="p">[</span><span class="s1">'PHONE_NUMBER'</span><span class="p">],</span> <span class="ss">body: </span><span class="n">message_body</span><span class="p">)</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">from:</code> field is the Twilio phone number you got from your Twilio dashboard, and the <code class="language-plaintext highlighter-rouge">to:</code> field is your personal phone number (make sure to register it with your Twilio account if you’re on the free “Demo” tier). The <code class="language-plaintext highlighter-rouge">body:</code> field is the message we constructed earlier.</p>

<p>The complete code should look something like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kp">include</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">DateHelper</span>

<span class="k">if</span> <span class="mi">3</span><span class="p">.</span><span class="nf">days</span><span class="p">.</span><span class="nf">ago</span> <span class="o">&gt;=</span> <span class="no">Deadman</span><span class="p">.</span><span class="nf">last_reset</span>
    <span class="n">time_to_trigger</span> <span class="o">=</span> <span class="n">time_ago_in_words</span><span class="p">(</span><span class="no">Deadman</span><span class="p">.</span><span class="nf">last_reset</span> <span class="o">+</span> <span class="mi">7</span><span class="p">.</span><span class="nf">days</span><span class="p">)</span>
    <span class="n">message_body</span> <span class="o">=</span> <span class="s2">"Your deadman switch will trigger in </span><span class="si">#{</span><span class="n">time_to_trigger</span><span class="si">}</span><span class="s2">, please log in to your account and reset it."</span>
    <span class="n">twilio_client</span> <span class="o">=</span> <span class="no">Twilio</span><span class="o">::</span><span class="no">REST</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'TWILIO_ACCOUNT_SID'</span><span class="p">],</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'TWILIO_ACCOUNT_TOKEN'</span><span class="p">])</span>
    <span class="n">twilio_client</span><span class="p">.</span><span class="nf">messages</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">from: </span><span class="no">ENV</span><span class="p">[</span><span class="s1">'TWILIO_PHONE_NUMBER'</span><span class="p">],</span> <span class="ss">to: </span><span class="no">ENV</span><span class="p">[</span><span class="s1">'PHONE_NUMBER'</span><span class="p">],</span> <span class="ss">body: </span><span class="n">message_body</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p>And that’s it! That’s all there is to it!</p>

<h2 id="further-reading">Further Reading</h2>

<p>I considered adding the capability of resetting my switch via SMS as well, by replying to one of the reminders.</p>

<p>I didn’t end up going with that for various reasons, but if you would like to, here is a <a href="https://www.twilio.com/blog/2018/04/sms-notifications-ruby-on-rails.html">blog post</a> I wrote a while ago on the Twilio blog that should help get you started.</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="ruby" /><category term="rails" /><category term="twilio" /><summary type="html"><![CDATA[Bonus Round!]]></summary></entry><entry><title type="html">A Tale Of Two Inaugurations</title><link href="https://blog.yechiel.me/a-tale-of-two-inaugurations-3a0f3e7a8632" rel="alternate" type="text/html" title="A Tale Of Two Inaugurations" /><published>2021-01-22T09:16:42-05:00</published><updated>2021-01-22T09:16:42-05:00</updated><id>https://blog.yechiel.me/2021-01-22_A-Tale-Of-Two-Inaugurations</id><content type="html" xml:base="https://blog.yechiel.me/a-tale-of-two-inaugurations-3a0f3e7a8632"><![CDATA[<hr />

<h3 id="a-tale-of-two-inaugurations">A Tale Of Two Inaugurations</h3>

<p><img src="/assets/images/posts/2021-01-22-2021-01-22_A-Tale-Of-Two-Inaugurations-0.jpg" alt="A picture of the inauguration of president Barack Obama." /></p>

<p>Wednesday, the United States and a large part of the world watched as the US swore in Joe Biden as their 46th president.</p>

<p>In his inaugural address, President Biden spoke about unity:</p>

<blockquote>
  <p>“To overcome these challenges, to restore the soul and secure the future of America requires so much more than words. It requires the most elusive of all things in a democracy: Unity.</p>
</blockquote>

<blockquote>
  <p>“In another January, on New Year’s Day in 1863, Abraham Lincoln signed the Emancipation Proclamation. When he put pen to paper, the president said, and I quote: “If my name ever goes down into history, it’ll be for this act. And my whole soul is in it.”</p>
</blockquote>

<blockquote>
  <p>“My whole soul was in it today. On this January day, my whole soul is in this: Bringing America together, uniting our people, uniting our nation. And I ask every American to join me in this cause.</p>
</blockquote>

<blockquote>
  <p>Uniting to fight the foes we face: anger, resentment, hatred, extremism, lawlessness, violence, disease, joblessness and hopelessness. With unity, we can do great things.”</p>
</blockquote>

<p>There is no question that the political discourse has devolved into extreme divisiveness over the last few years, and it’s refreshing to hear from a leader who will do his best to heal the divide rather than fan its flames.</p>

<p><img src="/assets/images/posts/2021-01-22-2021-01-22_A-Tale-Of-Two-Inaugurations-1.jpg" alt="A young picture of the Lubavitcher Rebbe." /></p>

<p class="image-caption"><em>The Lubavitcher Rebbe around the time he assumed leadership of the Chabad Lubavitch movement (1951)</em></p>

<p>But what does unity even mean? Does it mean that everyone has to conform to the same ideas? Do we have to give up our individuality? Are we required to overlook hate and intolerance in the name of “unity”?</p>

<p>Seventy years ago to the day, on the tenth of Shvat 5711 (Jan 17, 1951), a different inaugural address took place.</p>

<p>Rabbi Menachem Mendel Schneerson assumed the leadership of the Chabad Lubavitch movement after the passing of his father in law, and in the first public talk he gave after accepting the position, he said the following:</p>

<blockquote>
  <p>“When my father-in-law, the Rebbe, arrived in America, he quoted the words of the Sages “When you come to a town, follow its customs.” Here in America, people like to hear a “mission statement,” a declaration that is novel and preferably sensational. I don’t know whether there is a need for things to be done in this way, but “when you come to a town, follow its customs.”</p>
</blockquote>

<blockquote>
  <p>“The three loves — the love of G‑d, the love of the Torah, and love toward a fellow Jew — are all one. They are by definition indivisible, like one essence. If a person has a love of G‑d but is without a love of the Torah or love of his fellow Jew, this indicates that something is lacking in his love of G‑d, too. On the other hand, when there is <em>Ahavas Yisrael,</em> then even though this is [merely one] <em>mitzvah,</em> it ultimately leads to a love of the Torah and a love of G‑d.”</p>
</blockquote>

<p>Real unity is around a shared goal and a shared purpose. In the context of Ahavat Yisrael (love of your fellow Jew), that shared purpose is Ahavat Hashem (love of G-d) and Ahavat Hatorah (love of the Torah).</p>

<p>I believe that in the context of political unity, the shared purpose is a love of one’s country. When we acknowledge that, at the end of the day, we all want what’s best for the country, we can put aside personal and political differences and work together toward a common goal.</p>

<p>But just as a person can’t have a true love of Hashem if they don’t love their fellow Jew, similarly, if we let patriotism and love of our country get in the way of our respect for others’ humanity and dignity, then our faux patriotism and love of country aren’t real. They’re merely a cover to justify our disdain for the “other.”</p>

<p>Let us take this moment to reflect, put aside our differences, and work together to build a world that is better for all of us.</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="" /><category term="" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">My Favorite Bash Tips, Tricks, and Shortcuts</title><link href="https://blog.yechiel.me/my-favorite-bash-tips-tricks-and-shortcuts-36bj" rel="alternate" type="text/html" title="My Favorite Bash Tips, Tricks, and Shortcuts" /><published>2021-01-11T17:23:39-05:00</published><updated>2021-01-11T17:23:39-05:00</updated><id>https://blog.yechiel.me/my-favorite-bash-tips-tricks-and-shortcuts-36bj</id><content type="html" xml:base="https://blog.yechiel.me/my-favorite-bash-tips-tricks-and-shortcuts-36bj"><![CDATA[<p>If you’re on DEV, chances are you spend at least <strong>some</strong> time in the terminal, maybe even a <strong>lot</strong> of time.</p>

<p>Over the years, I’ve picked up a number of tips and tricks from fellow developers. Almost every time I pair program with someone new, chances are I’ll notice them doing something neat and ask them how they did it.</p>

<p>Here are some of my favorites.</p>

<p>I use bash as my default terminal, but most of these tips translate to other terminals as well.</p>

<p><em><strong>Note:</strong> This post isn’t meant to teach the basics of using the terminal. There are many great resources online (I remember doing <a href="https://www.codecademy.com/learn/learn-the-command-line">Codecademy’s Command Line course</a> when I was starting out.</em></p>

<h2 id="the---operator">The <code class="language-plaintext highlighter-rouge">-</code> operator</h2>

<p>Do you find yourself switching back and forth between two directories often?</p>

<p>You can use <code class="language-plaintext highlighter-rouge">cd -</code> to change to the last directory you were in like this:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ <span class="nv">$ </span><span class="nb">cd </span>directory1
~/directory1 <span class="nv">$ </span><span class="nb">cd </span>directory2
~/directory2 <span class="nv">$ </span><span class="nb">cd</span> -
~/directory1 <span class="err">$</span>
</code></pre></div></div>

<p>This also works with git when switching between branches:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/my-project<span class="o">(</span>main<span class="o">)</span><span class="nv">$ </span>git checkout feature-branch
~/my-project<span class="o">(</span>feature-branch<span class="o">)</span><span class="nv">$ </span>git checkout -
~/my-project<span class="o">(</span>main<span class="o">)</span><span class="err">$</span>
</code></pre></div></div>

<h2 id="the--operator">The <code class="language-plaintext highlighter-rouge">!!</code> operator</h2>

<p>This happens a lot!</p>

<p>You type a command, only to get a “Permission denied” so you have to retype the command again, this time using <code class="language-plaintext highlighter-rouge">sudo</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">!!</code> operator echoes the last command you typed into your terminal.</p>

<p>You can use it like this:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>some-dangerous-script.sh
<span class="o">=&gt;</span> Error: Permission Denied
<span class="nv">$ </span><span class="nb">sudo</span> <span class="o">!!</span>
<span class="o">=&gt;</span> Enter password <span class="k">for </span>some-dangerous-script.sh: 
</code></pre></div></div>

<h2 id="curly-brace-expansion">{curly brace expansion}</h2>

<p>If you ever need to run a series of very similar commands that differ by just a few characters (like for example, if you want to create a few filenames with lightly different extensions) you can use the characters that will be different between two curly braces and the command will run once for each one.</p>

<p>Like this:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">touch </span>file-<span class="o">{</span>1,2,3<span class="o">}</span>.md
<span class="nv">$ </span><span class="nb">ls</span>
<span class="o">=&gt;</span> file-1.md file-2.md file-3.md
</code></pre></div></div>

<p>You can also pass in a range:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">touch </span>file-<span class="o">{</span>1..3<span class="o">}</span>.md
<span class="nv">$ </span><span class="nb">ls</span>
<span class="o">=&gt;</span> file-1.md file-2.md file-3.md
</code></pre></div></div>

<h2 id="search-using-ctrlr">Search using Ctrl+R</h2>

<p>Are you like me? Would you press the up button 20 times to avoid typing out a 7 character command?</p>

<p>This next one was a lifesaver for me!</p>

<p>You can type <kbd>Ctrl</kbd> + <kbd>R</kbd> followed by the first few letters of the command you want to search through your bash history and bring up the command you need.</p>

<p>(Sorry, I can’t think of how to demonstrate that with a code snippet. Just go to your terminal, type in <kbd>Ctrl</kbd> + <kbd>R</kbd> and start typing).</p>

<h2 id="aliases">Aliases</h2>

<p>Aliases are a great way to save time and keystrokes. If there’s a command or a series of commands you find yourself typing often, it’s making an alias can be very helpful.</p>

<p>In order to set aliases, first open the <code class="language-plaintext highlighter-rouge">~/.bashrc</code> file in your favorite editor and check if it has the following lines in it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> ~/.bash_aliases <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    <span class="nb">.</span> ~/.bash_aliases
<span class="k">fi</span>
</code></pre></div></div>

<p>It should be there already, if it isn’t just add it to the bottom of the file.</p>

<p>Next open <code class="language-plaintext highlighter-rouge">~/.bash_aliases</code> in your editor (or create it if it doesn’t exist) and add your aliases in the following format:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">something</span><span class="o">=</span><span class="s2">"definition"</span>
</code></pre></div></div>

<p>Some playful aliases I have in my <code class="language-plaintext highlighter-rouge">.bash_aliases</code> are:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">please</span><span class="o">=</span><span class="s2">"sudo "</span>
<span class="nb">alias </span><span class="nv">yeet</span><span class="o">=</span><span class="s2">"rm -rf 
</span></code></pre></div></div>

<p>I also have a number of functions defined there, for more complex command series:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mk<span class="o">()</span> <span class="o">{</span>
	<span class="nb">mkdir</span> <span class="nv">$1</span> <span class="o">&amp;&amp;</span> <span class="nb">cd</span> <span class="nv">$1</span>
<span class="o">}</span>

gclone<span class="o">()</span> <span class="o">{</span>
	git clone <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nb">cd</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">basename</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> .git<span class="si">)</span><span class="s2">"</span>
<span class="o">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">mk</code> alias takes a directory name as an argument, <code class="language-plaintext highlighter-rouge">mk</code>s the directory and then <code class="language-plaintext highlighter-rouge">cd</code>s into it.</p>

<p>The <code class="language-plaintext highlighter-rouge">gclone</code> alias takes a git repo, clones it, and then <code class="language-plaintext highlighter-rouge">cd</code>s into it.</p>

<p>After adding aliases to your <code class="language-plaintext highlighter-rouge">.bash_aliases</code> they should load automatically every time you start a new terminal session.</p>

<p>If you would like to use your aliases in your current session, run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> ~/.bash_aliases
</code></pre></div></div>

<p>That’s what I can think of for now.</p>

<p>Do you have any favorite tips and tricks?</p>

<p>Please please do share them! I always love learning new ones!</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="linux" /><category term="bash" /><category term="beginners" /><category term="productivity" /><summary type="html"><![CDATA[If you’re on DEV, chances are you spend at least some time in the terminal, maybe even a lot of time.]]></summary></entry><entry><title type="html">Part 2: Add A Dead-Man’s Switch To A Rails Application</title><link href="https://blog.yechiel.me/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j" rel="alternate" type="text/html" title="Part 2: Add A Dead-Man’s Switch To A Rails Application" /><published>2020-12-17T23:57:34-05:00</published><updated>2020-12-17T23:57:34-05:00</updated><id>https://blog.yechiel.me/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j</id><content type="html" xml:base="https://blog.yechiel.me/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j"><![CDATA[<h3 id="ah-yes-where-were-we">Ah Yes, Where Were We?</h3>

<p>In <a href="/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp">part 1</a> of this tutorial, we learned how to set up the part of the deadman’s switch that allows us to reset the switch.</p>

<p>In part 2, we will get to the main part; how to set up a script that will run if something happens and the switch isn’t reset in a given amount of time.</p>

<h3 id="rake-it-in">Rake It In</h3>

<p>The standard way of running scripts in Rails apps (and Ruby environments generally) is using Rake tasks.</p>

<p>Rake tasks are, in their most basic form, Ruby scripts that you can run using the command <code class="language-plaintext highlighter-rouge">rake [task-name]</code>.</p>

<p>In fact, chances are you’ve run quite a few already! If you’ve run commands like <code class="language-plaintext highlighter-rouge">rails db:setup</code>, <code class="language-plaintext highlighter-rouge">rails db:migrate</code>, these are rake tasks that come built-in with Rails for setting up your database!</p>

<p>We can write our own Rake tasks as well. They usually live in the <code class="language-plaintext highlighter-rouge">lib/tasks/</code> directory of our Rails app, and the files use the <code class="language-plaintext highlighter-rouge">.rake</code> extension instead of the `.rb extension Ruby scripts usually use.</p>

<p>Let’s start by creating a file <code class="language-plaintext highlighter-rouge">lib/tasks/deadmans-switch.rake</code>.</p>

<p>In the file, let’s put the following:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">desc</span> <span class="s2">"Deadman's Switch"</span>

<span class="n">task</span> <span class="ss">:deadman_switch</span> <span class="k">do</span>
    <span class="nb">puts</span> <span class="s2">"Our first task!"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>At this point, we have a very basic Rake task. If you run <code class="language-plaintext highlighter-rouge">rake -T</code> in your terminal, you should get a list of all the Rake asks you have available, including this one:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rake deadmans_switch                       <span class="c"># Deadman's switch</span>
</code></pre></div></div>

<p>On the left side, we have the name of our task, and on the right, the description that we gave it in the first line following the <code class="language-plaintext highlighter-rouge">desc</code> keyword.</p>

<p>If we run <code class="language-plaintext highlighter-rouge">rake deadmans_switch</code> in our terminal, we should see the following:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> rake deadman_switch 
<span class="o">=&gt;</span> Our first task!
</code></pre></div></div>

<p>Of course, at this point, our task does nothing more than print a line to let us know it’s there, but we can replace that with any Ruby code we want, so let’s do that!</p>

<p>Replace the contents of the task with:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">if</span> <span class="mi">7</span><span class="p">.</span><span class="nf">days</span><span class="p">.</span><span class="nf">ago</span> <span class="o">&lt;</span> <span class="no">Deadman</span><span class="p">.</span><span class="nf">last_reset</span>
        <span class="nb">puts</span> <span class="s2">"Still alive!"</span>
    <span class="k">else</span>
        <span class="nb">puts</span> <span class="s2">"Executing Deadman Switch!"</span>
        <span class="c1"># whatever you want to do goes here</span>
    <span class="k">end</span>
</code></pre></div></div>

<p>This script now uses the <code class="language-plaintext highlighter-rouge">.last_reset</code> method we put on our <code class="language-plaintext highlighter-rouge">DeadmansSwitch</code> class to check when you last reset the switch. If it was less than seven days ago, it just prints “Still alive!” and exits; if you didn’t reset the switch in over a week, it’ll execute whatever script you tell it to.</p>

<p><strong>Note:</strong> I put a week in my script because I figured that was a reasonable timeframe for my needs. You can change that to 2 days, two weeks, a month, a year, whatever you feel the right timeframe for your needs. Ruby’s <code class="language-plaintext highlighter-rouge">Time</code> class offers great methods for denoting a timeframe, which you can use similar to the <code class="language-plaintext highlighter-rouge">7.days.ago</code> that I used.</p>

<p>Now if you run <code class="language-plaintext highlighter-rouge">rake deadmans_switch</code> in the console, you should get:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> rake deadman_switch 
<span class="o">=&gt;</span> Still Alive!
</code></pre></div></div>

<p>At this point, you can start working on the actual body of the script, which will be different depending on what it is you need a dead man’s switch, so I’ll leave you to it.</p>

<p>If you want to test and debug the script by running it and don’t want to wait seven days for the switch to expire, you can change the <code class="language-plaintext highlighter-rouge">7.days.ago</code> in your <code class="language-plaintext highlighter-rouge">if</code> statement to something more reasonable like <code class="language-plaintext highlighter-rouge">7.minutes.ago</code>. Just don’t forget to change it back when you’re done.</p>

<h3 id="have-you-been-triggered">Have You Been Triggered?</h3>

<p>Now that we have a script and a way to ensure it only runs after a given time, there’s one more thing we need to do. We probably don’t want this script to run more than once.</p>

<p>For local scripts that run using a cron job, this isn’t such an issue. You can probably use the script to reset the crontab. But this is a script running on Heroku where we don’t have access to the scheduler from within the script, so we’ll have to get creative.</p>

<p>One option I explored, which turned out to be a dead-end for reasons I’ll explain soon, but I’ll include it here anyway purely for its entertainment value, was to put the following on the last line fo my script:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">File</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="kp">__FILE__</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">__FILE__</code> is a Ruby constant representing the current file, so what that line of code does is it deletes the current file once it reaches that line. Sort of like those spy movies where you get a note telling you to destroy it once you’re finished reading it.</p>

<p>At first, I didn’t think it would work. Spending the better part of last year developing for Windows servers and battling numerous “file in use” errors, I was sure Linux wouldn’t let me do it either. But I did it just for kicks, and it turns out Linux is a lot more trusting! You can even try it yourself; create a test file, paste the above line of code, run it using Ruby, and watch it disappear!</p>

<p>While that’s a pretty fun solution, unfortunately, it won’t work for our purposes. Heroku, as mentioned in Part 1, uses an ephemeral file system, so even if we delete the file containing the script, the next time the app deploys, Heroku will do a fresh pull from GitHub, and our file will reappear.</p>

<p>If we want to keep track of whether our script ran, we will need to use something that persists, like a database entry. Fortunately, I’m a psychic who can see into the future, and if you remember, in Part 1, when we created our <code class="language-plaintext highlighter-rouge">deamans_switches</code> table, we added a <code class="language-plaintext highlighter-rouge">triggered</code> column, and we will now make use of it.</p>

<p>The first thing we will do is add two more methods to our <code class="language-plaintext highlighter-rouge">DeadmansSwitch</code> class in <code class="language-plaintext highlighter-rouge">app/models/deadmans_switch.rb</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">triggered</span>
        <span class="n">create</span><span class="p">(</span><span class="ss">triggered: </span><span class="kp">true</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">triggered?</span>
        <span class="n">where</span><span class="p">(</span><span class="ss">triggered: </span><span class="kp">true</span><span class="p">).</span><span class="nf">size</span> <span class="o">&gt;</span> <span class="mi">0</span>
    <span class="k">end</span>
</code></pre></div></div>

<p>The first one, <code class="language-plaintext highlighter-rouge">.triggered</code>, creates a new row in the database, but unlike a regular row, we set the <code class="language-plaintext highlighter-rouge">triggered</code> column to <code class="language-plaintext highlighter-rouge">true</code>.</p>

<p>The second method, <code class="language-plaintext highlighter-rouge">.triggered?</code>, queries the database and checks if there are any rows where <code class="language-plaintext highlighter-rouge">triggered</code> is set to <code class="language-plaintext highlighter-rouge">true</code> and returns <code class="language-plaintext highlighter-rouge">true</code> if there is at least one such row.</p>

<p>Now let’s go back to our Rake task and put those to use.</p>

<p>The first thing we will do is add the following line at the end of the script after everything we wanted to run ran:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Deadman</span><span class="p">.</span><span class="nf">triggered</span>
</code></pre></div></div>

<p>This will enter a row in the database with <code class="language-plaintext highlighter-rouge">triggered: true</code>.</p>

<p>Next, all the way at the beginning of our script, in the very first line, we will put the following:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">abort</span> <span class="k">if</span> <span class="no">Deadman</span><span class="p">.</span><span class="nf">triggered?</span>
</code></pre></div></div>

<p>This will check the database and see if there are any rows where <code class="language-plaintext highlighter-rouge">triggered: true</code> and abort the script if it finds any.</p>

<h3 id="getting-scheduled">Getting Scheduled</h3>

<p>At this point, we have a working deadman’s switch. The only thing left is to deploy it along with our Rails app and set up a scheduler to run it.</p>

<p>My app is deployed on Heroku, so the instructions will be for getting set up on Heroku. If you have a different setup, things will work differently for you.</p>

<p>So once you’ve written up all the code, committed it, and pushed it off to Heroku, here is how to go about scheduling your script.</p>

<p>From the Heroku dashboard, navigate to the “Resources” tab.</p>

<p>Once in the “Resources” tab, find the button that says “Find more add-ons” and click on it.</p>

<p>Search for <a href="https://elements.heroku.com/addons/scheduler">Heroku Scheduler</a> and install it and provision it to your app.</p>

<p>Once it’s installed properly, you should see it under add-ons in your resources tab. Click on the link to the Scheduler and then on the button that says “Add Job.”</p>

<p>You will be prompted to set a schedule to run your task (I set it to run every night at midnight) and which command to run, which you should set to <code class="language-plaintext highlighter-rouge">rake deadmans_switch</code>.</p>

<p>Hit “Save Job,” and that’s it, you’re all set!</p>]]></content><author><name>Yechiel Kalmenson</name></author><category term="rails" /><category term="ruby" /><category term="scripting" /><summary type="html"><![CDATA[Ah Yes, Where Were We?]]></summary></entry></feed>