<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Satyam Kumar</title>
    <description>The latest articles on Forem by Satyam Kumar (@satyamkr1729).</description>
    <link>https://forem.com/satyamkr1729</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3462903%2F2f090463-da00-4e20-8ada-a2d9b3df2549.jpg</url>
      <title>Forem: Satyam Kumar</title>
      <link>https://forem.com/satyamkr1729</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/satyamkr1729"/>
    <language>en</language>
    <item>
      <title>Linux: Signalling custom events with kill</title>
      <dc:creator>Satyam Kumar</dc:creator>
      <pubDate>Mon, 15 Sep 2025 04:25:09 +0000</pubDate>
      <link>https://forem.com/satyamkr1729/linux-signalling-custom-events-with-kill-c35</link>
      <guid>https://forem.com/satyamkr1729/linux-signalling-custom-events-with-kill-c35</guid>
      <description>&lt;p&gt;Often we need a daemon process to respond to some external events. We might want to notify a running process of some external event and make that process do some work corresponding to that event or we may simply want to modify the behavior of a process corresponding to some external events.&lt;/p&gt;

&lt;p&gt;Now one way to achieve this will be to use IPC (inter process communication) using sockets or pipes or dbus. All of these requires the running process to constantly poll the IPC elements used like socket files, pipe files etc or expose some methods on dbus. The daemon and the notifier process are tied by a thin bridge of socket files or pipe files or the dbus methods. Is there a native OS way to achieve this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Real time signals
&lt;/h2&gt;

&lt;p&gt;Linux already has the functionality that allows us to send a variety of signals to our running processes and we use them on a daily basis to terminate, kill or HUP a process. But linux also has an additional set of signals called &lt;strong&gt;real time signals&lt;/strong&gt; that allows the daemons to respond to arbitrary events. On its own, these signals are meaningless to the kernel or the process. The definition and behavior for these signals are left for the process/daemons to define.&lt;/p&gt;

&lt;p&gt;There are a total of 33 real time signals that the linux kernel supports, numbered between 32 to 64. Internally, glibc threading implementation might use 2 or 3 real time signals. Due to this, the actual range available for the use might not begin with 32. Hence we should refrain from using these hard-coded numbers for the signals and instead the recommended way to use these signals is to use notation &lt;strong&gt;SIGRTMIN+n&lt;/strong&gt; or &lt;strong&gt;SIGRTMAX-n&lt;/strong&gt;, where n starts from 1. We should ensure that the value of these signals doesn't go outside &lt;strong&gt;SIGRTMIN - SIGRTMAX&lt;/strong&gt;. Here &lt;strong&gt;SIGRTMIN&lt;/strong&gt; and &lt;strong&gt;SIGRTMAX&lt;/strong&gt; are defined by the kernel depending upon the glibc threading implementation and we don't need to worry about their exact values.&lt;/p&gt;

&lt;p&gt;We can actually list all the available signals in bash for our system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"kill -l"&lt;/span&gt;
&lt;span class="go"&gt;1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the signals which starts with &lt;strong&gt;SIGRTMIN&lt;/strong&gt; or &lt;strong&gt;SIGRTMAX&lt;/strong&gt; are the real time signals and their corresponding numeric value is shown before them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Real Time Signals in apps
&lt;/h2&gt;

&lt;p&gt;Here I will demo the use of these signals using a simple bash script but the same approch can be taken with any language. First we need to decide which real time signals our app will be using and what function those signals would correspond to. Then we need to implement below two steps in our program for each of the real time signals we have decided to use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;depending upon the signal function we have decided, define separate handler function for each signal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;assign these handler functions to their respective real time signals we have decided.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Sample program
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#### Section 1: define all handler functions ######################&lt;/span&gt;

&lt;span class="c"&gt;# handler function to send email SIGRTMIN+1&lt;/span&gt;
send_email_notification&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sending email notification"&lt;/span&gt;
  &lt;span class="c"&gt;# code for ending email goes here&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# handler function to enable execution with set -x&lt;/span&gt;
enable_debug_logs&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"enabling debug logs"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# handler function to disable -x&lt;/span&gt;
disable_debug_logs&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;set&lt;/span&gt; +x
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"disabling debug logs"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;######################################################33333&lt;/span&gt;

&lt;span class="c"&gt;#### Section 2: adding handler to signals #########################&lt;/span&gt;

&lt;span class="c"&gt;## Now we will lay down our traps to handle these signals&lt;/span&gt;
&lt;span class="c"&gt;## In bash 'trap' is how we define the signal handler&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;send_email_notification &lt;span class="s1"&gt;'SIGRTMIN+1'&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;enable_debug_logs &lt;span class="s1"&gt;'SIGRTMIN+2'&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;disable_debug_logs &lt;span class="s1"&gt;'SIGRTMIN+3'&lt;/span&gt;
&lt;span class="c"&gt;########################################################&lt;/span&gt;

&lt;span class="c"&gt;### the main loop which will do some work&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Doing some work"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;10
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very simple bash program that I have written to demo the concept and the possible use case. Here corresponding to the steps mentioned previously, I have divided the program into two sections. The real time signals and their function that our program will be using are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SIGRTMIN+1&lt;/strong&gt; : to send email notification on demand&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SIGRTMIN+2&lt;/strong&gt;: to enable printing of debug output&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SIGRTMIN+3&lt;/strong&gt;: to disable printing of debug output&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its not mandatory to go serially or pick these real time signals from start. You can pick any real time signal in the range allowed by the OS . Let’s now look at what the two sections defined in the program do.&lt;/p&gt;

&lt;h4&gt;
  
  
  Section 1: defining the handlers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# handler function to send email SIGRTMIN+1&lt;/span&gt;
send_email_notification&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sending email notification"&lt;/span&gt;
  &lt;span class="c"&gt;# code for ending email goes here&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# handler function to enable execution with set -x&lt;/span&gt;
enable_debug_logs&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"enabling debug logs"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# handler function to disable -x&lt;/span&gt;
disable_debug_logs&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;set&lt;/span&gt; +x
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"disabling debug logs"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are defining three handler functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;send email notification handler (send_email_notification)&lt;/strong&gt;: This function is supposed to send some email notification to some defined set of email IDs when the signal is received.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;enable debug logs (enable_debug_logs)&lt;/strong&gt;: This function will run &lt;code&gt;set -x&lt;/code&gt; which will cause the bash script to print the lines it is executing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;disable debug logs (disable_debug_logs)&lt;/strong&gt;: This function will run &lt;code&gt;set +x&lt;/code&gt; which will cause the bash script to stop printing the lines it is executing&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Section 2: assigning the handlers to the signals
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;trap &lt;/span&gt;send_email_notification &lt;span class="s1"&gt;'SIGRTMIN+1'&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;enable_debug_logs &lt;span class="s1"&gt;'SIGRTMIN+2'&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;disable_debug_logs &lt;span class="s1"&gt;'SIGRTMIN+3'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the actual magic happens. In bash we assign the handlers to the signal using &lt;code&gt;trap&lt;/code&gt; command. The syntax is quite simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &amp;lt;handler &lt;span class="k"&gt;function &lt;/span&gt;name&amp;gt; &amp;lt;signal name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the program above using &lt;code&gt;trap&lt;/code&gt; command, we have assigned each of our handler function to their respective real time signals we had decided to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeing signals in action
&lt;/h3&gt;

&lt;p&gt;Time to see this in action now. Lets run our bash script in one terminal and open a new terminal to run our command to send the real time signals. Using the &lt;code&gt;ps&lt;/code&gt; command, note the PID of our bash script (in my case it is 692560). Now to send the signal to our program, we will be using the &lt;strong&gt;bash kill&lt;/strong&gt; command. The syntax is like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &amp;lt;signal name&amp;gt; &amp;lt;pid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now see each of the three scenarios in action.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: below demo uses bash kill command. If you are using any other shell then please check the respective documentation on how to send the real time signals in that shell.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Experiment 1: tell our bash process to send email notification
&lt;/h4&gt;

&lt;p&gt;Let's run the below command in the second terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; SIGRTMIN+1 692560
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in the first terminal, after few seconds (utmost 10), you should see the the following sequence of lines (the delay will happen because sleep command will be running):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Doing some work
Doing some work
Sending email notification
Doing some work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows that our &lt;code&gt;send_email_notification&lt;/code&gt; handler was executed successfully.&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 2: enable printing of debug output
&lt;/h4&gt;

&lt;p&gt;Run the below command in the second terminal now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; SIGRTMIN+2 692560
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe the output in the first terminal for next 20 secs. You will see output like below. The program will start printing the lines it is executing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Doing some work
++ echo 'enabling debug logs'
enabling debug logs
+ true
+ echo 'Doing some work'
Doing some work
+ sleep 10
+ true
+ echo 'Doing some work'
Doing some work
+ sleep 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus we can see that our &lt;code&gt;enable_debug_logs&lt;/code&gt; handler was also executed successfully.&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 3: disable printing of debug logs
&lt;/h4&gt;

&lt;p&gt;Now final experiment. Lets disable the debug logs printing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; SIGRTMIN+3 692560
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again lets observe the output on the first terminal for next 20 secs. You will see output similar to below. The program will stop printing the lines it is executing and go back to normal output sequence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Doing some work
+ sleep 10
++ disable_debug_logs
++ set +x
disabling debug logs
Doing some work
Doing some work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This show that our &lt;code&gt;disable_debug_logs&lt;/code&gt; was also successfully executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thus we saw how we can easily implement these real time signal handling in our program and notify our running process using native linux signalling. This is the simplest way of notifying a running process without requiring expensive socket, pipe or dbus setup. Of course this doesn't allows you to send some data along if your process requires that but it is quite good for scenarios where the process just needs to be notified of some event to do some work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;As a bonus, I leave you with a simple python program that implements the above idea using python. Try to run it and send the same real time signals to it as above and see the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;

&lt;span class="c1"&gt;# setup logger
&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_logger&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Set the log level
&lt;/span&gt;&lt;span class="n"&gt;stdout_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StreamHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;stdout_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%(asctime)s - %(name)s - %(levelname)s - %(message)s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;stdout_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stdout_handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FrameType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sending email notification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enable_debug_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FrameType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;stdout_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enabling debug log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disable_debug_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FrameType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;stdout_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disabling debug log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGRTMIN&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_email_notification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGRTMIN&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enable_debug_logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGRTMIN&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;disable_debug_log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Doing some work&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;this is debug log for that work&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;For further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man7/signal.7.html" rel="noopener noreferrer"&gt;Signal man page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>linuxprocess</category>
      <category>bash</category>
      <category>programming</category>
    </item>
    <item>
      <title>Analyse and Optimise your container image size</title>
      <dc:creator>Satyam Kumar</dc:creator>
      <pubDate>Wed, 27 Aug 2025 15:41:25 +0000</pubDate>
      <link>https://forem.com/satyamkr1729/optimising-your-container-image-size-24f7</link>
      <guid>https://forem.com/satyamkr1729/optimising-your-container-image-size-24f7</guid>
      <description>&lt;p&gt;Whether you are a software developer, a devops engineer or a linux sys admin, everyone has seen how ubiquitous the containers have become. There is no denying the fact that they are quite convenient to run the applications. You have your application ready, all you need is write a neat little &lt;code&gt;Dockerfile&lt;/code&gt; and run the docker build command and there you have your application containerized. Declarative nature of writing a &lt;code&gt;Dockerfile&lt;/code&gt; is so easy that we often dont even think about optmising it and the end result is ridiculously larger image size. Although there are a ton of ways to analyse your dockerfile and optimise it but in this post today I would like to focus on one specific scenario that I recently faced in my organisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example setup
&lt;/h2&gt;

&lt;p&gt;As this blog post that I am writing is outside the organisation I am  working with so I will try to imagine a situation which is close to the scenario I faced within my org and try to build up a solution from there.&lt;/p&gt;

&lt;p&gt;Lets say we have the below dockerfile with us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;make
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /workdir
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /workdir&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Makefile  .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;make
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;755 mybinary
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;mybinary /usr/bin/mybinary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this will do is compile an application of size 2GB and then install it to the &lt;code&gt;/usr/bin&lt;/code&gt;. Lets trigger the build of this image and see what the image size is after the end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;satyam@wakanda ~/demo $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; mydemo:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;[+] Building 9.1s (13/13) FINISHED                                                         docker:default
&lt;/span&gt;&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile                                                 0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 215B                                                                 0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/library/ubuntu:latest                                     0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                                                    0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 2B                                                                      0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;1/8] FROM docker.io/library/ubuntu:latest                                                       0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load build context                                                                    0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 30B                                                                     0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;2/8] RUN apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;make                                                  0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;3/8] RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; /workdir                                                                  0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;4/8] WORKDIR /workdir                                                                    0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;5/8] COPY Makefile  &lt;span class="nb"&gt;.&lt;/span&gt;                                                                    0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;6/8] RUN make                                                                            0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;7/8] RUN &lt;span class="nb"&gt;chmod &lt;/span&gt;755 mybinary                                                              0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;8/8] RUN &lt;span class="nb"&gt;mv &lt;/span&gt;mybinary /usr/bin/mybinary                                                          2.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exporting to image                                                                               7.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                                              7.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; writing image sha256:0047f0b0f1bb5e2ffbf178f5f0cddbad64aaa71d37d28eb0c6c0aecf1def9963         0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/mydemo:latest                                                     0.0s
&lt;span class="gp"&gt;satyam@wakanda ~/demo $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker image &lt;span class="nb"&gt;ls &lt;/span&gt;mydemo:latest
&lt;span class="go"&gt;REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
mydemo       latest    0047f0b0f1bb   4 minutes ago   6.57GB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What!!!&lt;/p&gt;

&lt;p&gt;How did the image end up being 6.5 GB when the application is just 2GB !!&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it all went wrong ?
&lt;/h2&gt;

&lt;p&gt;Now surely if my container image is ending up being 3 times my application size then I am not doing a very good job at containerizing it. Large containers take large disk space and are time taking to build and export. &lt;/p&gt;

&lt;p&gt;So how can we analyse how our image size ballooned to this size?&lt;/p&gt;

&lt;p&gt;We will use an open source tool called &lt;a href="https://github.com/wagoodman/dive" rel="noopener noreferrer"&gt;dive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's pass our image tag as input to this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;satyam@wakanda ~demo $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dive mydemo:latest
&lt;span class="go"&gt;Image Source: docker://mydemo:latest
Extracting image from docker-engine... (this can take a while for large images)
Analyzing image...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will take some time to run and then you will be presented with a screen containing very detailed view of each layer of the image and also the filesystem view of the last layer. We will focus on the layer view. This is what you are expected to get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fei6sya1uiv8zfbkrlcf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fei6sya1uiv8zfbkrlcf4.png" alt="dive tool output for the unoptimised image" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the output we can clearly see how much size each of our dockerfile commands have contributed to the overall image size. In Dockerfile, &lt;strong&gt;each dockerfile command you write corresponds to one layer of container image&lt;/strong&gt; and &lt;strong&gt;each new layer is added upon the previous layer&lt;/strong&gt;. Thus at the end the container image is the &lt;strong&gt;sum of all the layers that was created by each of the dockerfile commands&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the image above, we can clearly see that the last three layers are the ones adding to the bulk of image size. So lets start by analysing each of them one by one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The third last layer&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   2.1 GB  RUN /bin/sh -c make # buildkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this output we can see that our &lt;code&gt;RUN make&lt;/code&gt; command created our application of &lt;code&gt;2.1GB&lt;/code&gt; in size which is ok because we know that the app was supposed to be 2GB. So far so good !!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The second last layer - The first mistake&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   2.1 GB  RUN /bin/sh -c chmod 755 mybinary # buildkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we ran &lt;code&gt;chmod 755 mybinary&lt;/code&gt; to modify the permission of our binary file. Now on its own this looks like a very innocent command so why is this creating an image layer equivalent to the previous layer in size?&lt;/p&gt;

&lt;p&gt;The answer lies in the docker's &lt;strong&gt;copy on write&lt;/strong&gt; approach of creating the image layer. When the container is being built, each of the layers that are created are &lt;strong&gt;immutable&lt;/strong&gt;. They can never be changed by the commands of the next layer. So when we do &lt;code&gt;RUN chmod ...&lt;/code&gt;, this will &lt;strong&gt;copy the binary onto the new layer&lt;/strong&gt; and there it will modify its permissions. Hence this simple &lt;code&gt;chmod&lt;/code&gt; ends up contributing a size equal to its previous layer. Now this make up total size of ~4GB. What about the rest 2GB?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The final layer - The second mistake&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   2.1 GB  RUN /bin/sh -c mv mybinary /usr/bin/mybinary # buildkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we ran &lt;code&gt;mv mybinary /usr/bin/mybinary&lt;/code&gt;. Now again a very innocent command. Technically we are just moving files so this should not be adding to the size. But remember our observation for the previous layer. &lt;strong&gt;Every previously built layer is immutable.&lt;/strong&gt; So even though we are using an &lt;code&gt;mv&lt;/code&gt; commad, docker &lt;strong&gt;cannot perform this operation on the previous layer&lt;/strong&gt;. What it will end up doing is again &lt;strong&gt;copy the file from previous layer to the new layer&lt;/strong&gt; and then perform the &lt;code&gt;mv&lt;/code&gt; operation here. Thus it will again end up adding 2GB. &lt;/p&gt;

&lt;p&gt;Hence we have now 6Gb of the image...&lt;/p&gt;

&lt;h2&gt;
  
  
  How could this have been avoided ?
&lt;/h2&gt;

&lt;p&gt;We can easily fix this problem by using docker's &lt;strong&gt;multi stage build&lt;/strong&gt;. What this means is that we build out the application and fix its permission in one stage and copy the final application onto the next stage. The &lt;strong&gt;layers of one stage are not added to the layers of the other stage&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Here is our fixed dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stage 1&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;make
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /workdir
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /workdir&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Makefile  .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;make
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;755 mybinary

&lt;span class="c"&gt;# Stage 2&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt;  --from=0 /workdir/mybinary /usr/bin/mybinary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each stage in a multi stage build is identified by a seperate &lt;code&gt;FROM&lt;/code&gt; command. In the first stage, we build out the application and fix the permissions of the application and then in the second stage, we copy the final application from the first stage to the required destination in the final image.&lt;/p&gt;

&lt;p&gt;Lets build the image and see the image size now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;satyam@wakanda ~demo $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; mydemo:fixed &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;[+] Building 10.6s (13/13) FINISHED                                                                                                                                                                    docker:default
&lt;/span&gt;&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile                                                                                                                                                             0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 251B                                                                                                                                                                             0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/library/ubuntu:latest                                                                                                                                                 0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                                                                                                                                                                0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 2B                                                                                                                                                                                  0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 1/7] FROM docker.io/library/ubuntu:latest                                                                                                                                                    0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;internal] load build context                                                                                                                                                                                0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 30B                                                                                                                                                                                 0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 2/7] RUN apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;make                                                                                                                                                      0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 3/7] RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; /workdir                                                                                                                                                                      0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 4/7] WORKDIR /workdir                                                                                                                                                                        0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 5/7] COPY Makefile  &lt;span class="nb"&gt;.&lt;/span&gt;                                                                                                                                                                        0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 6/7] RUN make                                                                                                                                                                                0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-0 7/7] RUN &lt;span class="nb"&gt;chmod &lt;/span&gt;755 mybinary                                                                                                                                                                  0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;stage-1 2/2] COPY  &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 /workdir/mybinary /usr/bin/mybinary                                                                                                                                             2.1s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exporting to image                                                                                                                                                                                           7.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                                                                                                                                                          7.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; writing image sha256:ec007c5a4c19fd0481b7f41fff27024a3ac25ba0e9e41f25a7fa70e2cae98d46                                                                                                                     0.0s
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/mydemo:fixed
&lt;span class="gp"&gt;satyam@wakanda ~demo $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker image &lt;span class="nb"&gt;ls &lt;/span&gt;mydemo:fixed
&lt;span class="go"&gt;REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
mydemo       fixed     ec007c5a4c19   16 seconds ago   2.23GB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behold!! we reduced our image size from 6GB to ~2GB. We can also visualize it with &lt;code&gt;dive&lt;/code&gt; tool&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkn84kg2k2r0465i5zue9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkn84kg2k2r0465i5zue9.png" alt="dive tool output for the fixed image" width="800" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just two layers!!! Remember I mentioned that in multi stage builds, the &lt;strong&gt;layers from previous stage are not added to the final stage&lt;/strong&gt;. Since in the second stage all we did was copy the application from first stage so we only have two layers and both of them are of expected size.&lt;/p&gt;




&lt;p&gt;So this is how we used &lt;code&gt;dive&lt;/code&gt; tool to analyse our image layers and used &lt;code&gt;multi stage builds&lt;/code&gt; to reduce the image size. &lt;/p&gt;

&lt;p&gt;Let me know your thoughts on this in the comments below and how you approached and optimised the image size in your projects. &lt;/p&gt;




&lt;p&gt;For more reading:&lt;br&gt;
&lt;a href="https://docs.docker.com/build/building/multi-stage/" rel="noopener noreferrer"&gt;Multi Stage Builds&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/wagoodman/dive" rel="noopener noreferrer"&gt;Dive tool&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>docker</category>
      <category>podman</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
