<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ra</title>
    <description>The latest articles on DEV Community by Ra (@astrearider).</description>
    <link>https://dev.to/astrearider</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%2F1033041%2Ff5f50078-e0a3-49ce-af76-88e69e72179d.jpg</url>
      <title>DEV Community: Ra</title>
      <link>https://dev.to/astrearider</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/astrearider"/>
    <language>en</language>
    <item>
      <title>CMD vs ENTRYPOINT: Ini bukan cuma masalah selera, pahami perbedaanya!</title>
      <dc:creator>Ra</dc:creator>
      <pubDate>Wed, 05 Mar 2025 07:16:46 +0000</pubDate>
      <link>https://dev.to/astrearider/cmd-vs-entrypoint-ini-bukan-cuma-masalah-selera-pahami-perbedaanya-2m8n</link>
      <guid>https://dev.to/astrearider/cmd-vs-entrypoint-ini-bukan-cuma-masalah-selera-pahami-perbedaanya-2m8n</guid>
      <description>&lt;p&gt;Ketika kita membuat suatu docker image, biasanya kita akan menggunakan command CMD atau ENTRYPOINT untuk menjalankan aplikasi yang kita build. Keduanya terlihat mirip, tetapi apakah keduanya memiliki perbedaan? pada kesempatan kali ini saya akan membahas perbedaan antara command CMD dan ENTRYPOINT dengan cara sederhana. Menggunakan contoh docker image berbasis Alpine dengan command yang familiar dan banyak digunakan oleh kebanyakan orang. Semoga tulisan saya kali ini bisa membantu menjawab pertanyaan kapan harus menggunakan CMD atau ENTRYPOINT, atau mengapa perintah dalam container yang kita buat tidak berjalan seperti yang diharapkan, mari kita mulai. 🚀 &lt;/p&gt;

&lt;h2&gt;
  
  
  Apa itu CMD dan ENTRYPOINT?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CMD&lt;/strong&gt; pada dasarnya adalah &lt;strong&gt;default command&lt;/strong&gt; dan argument yang selalu dijalankan ketika conainer dijalankan. Contoh 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="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.21.3&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["echo", "Hello World"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build dengan menggunakan command &lt;code&gt;docker build -t hello-world:cmd .&lt;/code&gt; dan jalankan docker image yang sudah di-build tadi dengan command &lt;code&gt;docker run hello-world:cmd&lt;/code&gt; maka outputnya akan seperti ini.&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%2F7kr9w5qspqm9avdn1own.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%2F7kr9w5qspqm9avdn1own.png" alt="basic output cmd" width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ENTRYPOINT&lt;/strong&gt; adalah &lt;strong&gt;main command&lt;/strong&gt; yang selalu dikesekusi ketika container dijalankan. Contoh 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="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.21.3&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["echo", "Hello World"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build dengan mengunakan command &lt;code&gt;docker build -t hello-world:entrypoint .&lt;/code&gt; dan jalankan docker image yang sudah di-build tadi dengan command &lt;code&gt;docker run hello-world:entrypoint&lt;/code&gt; maka outputnya akan seperti ini.&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%2Flzvs4z7oy576n30e5z4r.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%2Flzvs4z7oy576n30e5z4r.png" alt="basic output entrypoint" width="800" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nah sekarang apa yang membedakan antara CMD dan ENTRYPOINT? jika kita lihat ketika menjalankan kedua docker image tersebut menghasilkan output yang sama. Mari kita bahas lebih lanjut.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perbedaan CMD dan ENTRYPOINT
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CMD:&lt;/strong&gt; jika pengguna memberikan perintah saat menjalankan container, &lt;strong&gt;default command tersebut bisa digantikan dengan command yang diberikan ketika menjalankan container.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ENTRYPOINT:&lt;/strong&gt; jika pengguna memberikan perintah saat menjalankan container, &lt;strong&gt;main command tersebut tidak bisa digantikan dengan command yang diberikan ketika menjalankan container.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bayangkan CMD adalah kendaraan kantor yang sudah disediakan juga supirnya. Pada dasarnya, kendaraan kantor tersebut memiliki destinasi default (command default) seperti dari kantor pusat ke kantor cabang, namun ditengah-tengah jalan bisa kita ubah destinasinya (command default), seperti mampir untuk makan dulu misalnya. Dari analogi ini bisa disimpulkan bahwa command default pada CMD ini bisa di-override saat menjalakan container.&lt;/p&gt;

&lt;p&gt;Nah sekarang bayangkan ENTRYPOINT adalah kendaraan umum. Pada dasarnya, kendaraan umum tersebut kan sudah memiliki rutenya tersendiri (main command) seperti dari Daerah A ke Daerah B misalnya dan kita tentu tidak bisa merubah rute (main command) kendaraan umum tersebut dengan sesuka hati. Dari analogi ini, bisa disimpulkan bahwa main command pada ENTRYPOINT ini tidak bisa di-override saat menjalankan container.&lt;/p&gt;

&lt;p&gt;Agar bisa dipahami lebih dalam, berikut adalah beberapa contoh dari penggunaan CMD pada docker. Jika kita tetap menggunakan docker image yang kita build sebelumnya untuk CMD dan ingin melakukan override pada default command dengan menjalankan command &lt;code&gt;docker run hello-world:cmd "new message"&lt;/code&gt; dengan harapan akan mengeluarkan output berupa pesan “new message” maka outputnya akan seperti ini.&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%2Fk1ncpoohbkgdnsqn6742.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%2Fk1ncpoohbkgdnsqn6742.png" alt="error penggunaan cmd command" width="800" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;mengapa error ini terjadi? ketika kita menjalankan command &lt;code&gt;docker run hello-world:cmd "new message"&lt;/code&gt; maka ini akan diterjemahkan menjadi CMD [”new message”] pada Dockerfile kita. Karena “new message” bukanlah sebuah executable di dalam container atau linux command maka akan muncul error tersebut. &lt;/p&gt;

&lt;p&gt;Jika kita ingin mengubah output “Hello World” menjadi “new message”, maka command yang harus dijalankan adalah  &lt;code&gt;docker run hello-world:cmd "echo" "new message"&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdlcst33nmaill4iynxsp.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%2Fdlcst33nmaill4iynxsp.png" alt="hasil koreksi penggunaan cmd command" width="800" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;mengapa tidak error? karena ketika command tersebut dijalankan akan diterjemahkan menjadi CMD [”echo”, “new message”].&lt;/p&gt;

&lt;p&gt;Sekarang, kita lanjut ke contoh ENTRYPOINT pada docker. Jika kita menjalankan command &lt;code&gt;docker run hello-world:entrypoint "new message"&lt;/code&gt; pada docker image ENTRYPOINT yang kita build sebelumnya, maka output-nya akan seperti ini.&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%2Fbrwwkfuwoisjwi5n6dfd.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%2Fbrwwkfuwoisjwi5n6dfd.png" alt="error penggunaan entrypoint command" width="800" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;mengapa hal ini bisa terjadi? karena main command dari ENTRYPOINT tersebut tidak bisa ter-override dan akan diterjemahkan menjadi ENTRYPOINT [”echo”, “Hello World”, “new message”].&lt;/p&gt;

&lt;p&gt;Bagaimana jika kita ingin menjalankan command &lt;code&gt;docker run &amp;lt;docker-image-name:tag&amp;gt; "new message"&lt;/code&gt; tetapi output yang diharapkan adalah “new message”? jawabannya kombinasikan keduanya antara CMD dengan ENTRYPOINT. Berikut adalah contoh Dockerfile hasil modufikasi kombinasi keduanya.&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; alpine:3.21.3&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["echo"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["Hello World"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build menjadi docker image dengan command &lt;code&gt;docker build -t hello-world:combined .&lt;/code&gt; lalu jalankan command &lt;code&gt;docker run hello-world:combined "new message"&lt;/code&gt; maka output yang akan dihasilkan akan seperti ini.&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%2F35rqvboikyphb0fty4b3.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%2F35rqvboikyphb0fty4b3.png" alt="hasil koreksi penggunaan entrypoint command" width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;output sudah seperti yang diharapkan kan?&lt;/p&gt;

&lt;h2&gt;
  
  
  Kesimpulan
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Gunakan &lt;strong&gt;CMD&lt;/strong&gt; jika ingin &lt;strong&gt;default command bisa digantikan&lt;/strong&gt; dengan command yang diberikan ketika menjalankan container.&lt;/li&gt;
&lt;li&gt;Gunakan &lt;strong&gt;ENTRYPOINT&lt;/strong&gt; jika ingin &lt;strong&gt;main command tidak bisa digantikan&lt;/strong&gt; dengan command yang diberikan ketika menjalankan container.&lt;/li&gt;
&lt;li&gt;Jika ingin kombinasi antara main command dan default command, maka gunakan keduanya.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
    </item>
    <item>
      <title>From Bloated to Lean: Optimizing Docker Image with Multi-Stage Builds</title>
      <dc:creator>Ra</dc:creator>
      <pubDate>Wed, 19 Feb 2025 07:55:16 +0000</pubDate>
      <link>https://dev.to/astrearider/from-bloated-to-lean-optimizing-docker-image-with-multi-stage-builds-9km</link>
      <guid>https://dev.to/astrearider/from-bloated-to-lean-optimizing-docker-image-with-multi-stage-builds-9km</guid>
      <description>&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%2Fiejmyktww04pqdnbgura.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%2Fiejmyktww04pqdnbgura.png" alt="Impact of Multi-stage builds" width="800" height="392"&gt;&lt;/a&gt;&lt;br&gt;
Nowadays, containers have become the standard for ensuring portability and consistency across environments. However, without the right approach, container images can become excessively large, slow, and insecure.&lt;/p&gt;

&lt;p&gt;One of the biggest culprits behind slow container performance and bloated images is an inefficient build process. When dependencies required only during the build stage are included in the final container image, it results in unnecessary files and tools being carried over. This not only increases security risks but also leads to larger image sizes and slower application performance.&lt;/p&gt;

&lt;p&gt;With the multi-stage build technique, applications can be built in separate stages, ensuring that only the necessary artifacts are included in the final image. This approach offers several key benefits in terms of efficiency, image size, and security.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Multi-Stage Build Works
&lt;/h2&gt;

&lt;p&gt;Multi-stage builds divide the build process into multiple stages within a single Dockerfile, ensuring that only the necessary components are included in the final docker image.&lt;/p&gt;

&lt;p&gt;Let me explain this with an analogy. Imagine preparing a dinner for someone special ❤️&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Preparation Stage (Build Stage 1)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;You chop vegetables, meat, and other ingredients.&lt;/li&gt;
&lt;li&gt;Tools like a knife, cutting board, and any other stuff are used at this stage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cooking Stage (Build Stage 2)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;With all the ingredients ready, you start cooking the dish in a big pan.&lt;/li&gt;
&lt;li&gt;You no longer need the knife and cutting board at this point.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serving Stage (Final Stage)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;You plate the dish and serve it to your special someone.&lt;/li&gt;
&lt;li&gt;All the cooking tools and raw ingredients are no longer needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without multi-stage builds, it’s like serving your special dish along with the big pan, knife, cutting board, and all the leftover ingredients. Definitely not the best dinner experience! 💔&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation in Dockerfile
&lt;/h2&gt;

&lt;p&gt;In the context of the containers, multi-stage builds follow the same concept.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build Stages (Intermediate Stages)&lt;/strong&gt; → These stages handle the application build process, such as installing dependencies, compiling code, and preparing the necessary artifacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final Stage&lt;/strong&gt; → This stage contains only what is needed to run the application, excluding all build dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simple “Hello world” program written in Go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example of building the application without using multi-stage builds.&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="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.21.4-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; main.go .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; hello-world main.go
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./hello-world"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The final image includes the entire Go build environment, making it much larger than necessary.&lt;/li&gt;
&lt;li&gt;Unused build tools and dependencies remain in the image, increasing security risks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without multi-stage builds, the Docker image size reaches 249MB!&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%2Fjo6rzb75e8ql43bdpu1w.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%2Fjo6rzb75e8ql43bdpu1w.png" alt="Before the implementation of multi-stage builds" width="800" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's implement multi-stage builds to build the application.&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="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.21.4-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; main.go .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; hello-world main.go

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.21 &lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/hello-world .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./hello-world"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Improvements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The final image only contains the compiled binary (&lt;code&gt;hello-world&lt;/code&gt;), reducing the image size significantly.&lt;/li&gt;
&lt;li&gt;The Go build environment is no longer included, improving security and efficiency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, the docker image is now only 9.64MB, a massive reduction in size!&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%2Fpmrajnnjwapsoj9likh3.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%2Fpmrajnnjwapsoj9likh3.png" alt="After the implementation of multi-stage builds" width="800" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Multi-stage builds ensure that only the necessary components are included in the final Docker image, just like serving a meal without the cooking tools and leftover ingredients. This approach offers several key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smaller Docker Image Size&lt;/strong&gt; → Reduces storage needs and speeds up image pull and push operations to the container registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Security&lt;/strong&gt; → Eliminates unnecessary tools and dependencies, minimizing attack surfaces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Build Times&lt;/strong&gt; → Optimized caching improves efficiency, especially in CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>container</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Implement HashiCorp Vault SSH Secrets Engine for securing SSH access</title>
      <dc:creator>Ra</dc:creator>
      <pubDate>Mon, 12 Aug 2024 10:29:55 +0000</pubDate>
      <link>https://dev.to/astrearider/how-to-implement-hashicorp-vault-ssh-secrets-engine-for-securing-ssh-access-4if3</link>
      <guid>https://dev.to/astrearider/how-to-implement-hashicorp-vault-ssh-secrets-engine-for-securing-ssh-access-4if3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Many companies still used traditional username and password auth to connect our remote servers. Using passwords, no matter how complex it is, are vulnurable to various forms of attack, such as brute-force attempts or simply forgotten. In modern infrastructure, SSH is widely used for managing servers remotely. Unlike password-based authentication, SSH uses cryptographic keys to establish a secure connection, which offers a much higher level of security. As more SSH keys are generated and distributed accross different systems and users, it becomes increase and difficult to keep track of them all (key sprawl). There’s also the risk of unauthorized access, granting access to critical systems without detection, especially when former employees retain access they should no longer have. In this article, I’m going to walk through setting up how to implement HashiCorp Vault SSH Secrets Engine for securing SSH access.&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%2F0zq99u9dv5677b8du8va.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%2F0zq99u9dv5677b8du8va.png" alt="Image description" width="719" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Administrator authenticates to vault, enables SSH secrets engine, and generates a public key.&lt;/li&gt;
&lt;li&gt;Administrator adds public key from vault to remote servers.&lt;/li&gt;
&lt;li&gt;User sends the public key to an administrator for signing.&lt;/li&gt;
&lt;li&gt;An administrator creates a certificate using the user’s public key.&lt;/li&gt;
&lt;li&gt;The administrator sends the signed certificate to the user.&lt;/li&gt;
&lt;li&gt;User using private key and signed certificate to connect to remote servers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install Vault
&lt;/h3&gt;

&lt;p&gt;You can read and follow &lt;a href="https://developer.hashicorp.com/vault/docs/install" rel="noopener noreferrer"&gt;these docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup SSH secrets engine
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable SSH Secrets Engine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# dont forget to export your token by run this command 
export VAULT_TOKEN="&amp;lt;your token&amp;gt;"
vault secrets enable -path=ssh-creds ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you are using UI, you can go through secrets engines tab → click enable new engines → click ssh → fill path with ssh-creds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Vault with CA
&lt;/h3&gt;

&lt;p&gt;Configure Vault with a CA for signing client keys using the &lt;code&gt;/config/ca&lt;/code&gt; endpoint. If you do not have an internal CA, Vault can generate a keypair for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write ssh-creds/config/ca generate_signing_key=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you already have a keypair, specify the public and private key parts as part of the payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write ssh-creds/config/ca \
    private_key="..." \
    public_key="..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you are using UI, on secrets engines tab, you can click ssh-creds → go to configuration tab → click configure → fill with your public and private key, or you can leave blank.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get public key and save it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault read -field=public_key ssh-creds/config/ca &amp;gt; trusted-user-ca-keys.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add the public key to all target host's SSH configuration
&lt;/h2&gt;

&lt;p&gt;Copy &lt;code&gt;trusted-user-ca-keys.pem&lt;/code&gt; to &lt;code&gt;/etc/ssh&lt;/code&gt; directory in remote server&lt;br&gt;
Add the path where the public key contents are stored to the SSH configuration file as the &lt;code&gt;TrustedUserCAKeys&lt;/code&gt; option.&lt;br&gt;
Go to &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and then add this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process can be manual or automated using a configuration management tool such as Ansible. &lt;/p&gt;

&lt;h2&gt;
  
  
  Create roles
&lt;/h2&gt;

&lt;p&gt;Let's say we need to give access to developers, so we need to create a role for developers.&lt;br&gt;
You can run this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write ssh-creds/roles/developers -&amp;lt;&amp;lt;"EOH"
{
  "algorithm_signer": "rsa-sha2-256",
  "allow_user_certificates": true,
  "allowed_users": "*",
  "allowed_extensions": "permit-pty,permit-port-forwarding",
  "default_extensions": {
    "permit-pty": ""
  },
  "key_type": "ca",
  "default_user": "ubuntu",
  "ttl": "10m0s" 
}
EOH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;“algorithm_signer” : "rsa-sha2-256"&lt;/code&gt; is a signing algorithm for the key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"allow_user_certificates": true&lt;/code&gt; is allowed to be signed for us as user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"allowed_users": "*"&lt;/code&gt; is list of vault user who are allowed to use this key, &lt;code&gt;"*"&lt;/code&gt; means allow all the vault users.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"allowed_extensions": "permit-pty,permit-port-forwarding"&lt;/code&gt; for &lt;strong&gt;&lt;code&gt;permit-pty&lt;/code&gt;&lt;/strong&gt; is necessary for allowing interactive SSH sessions, where users need to interact directly with the server’s shell.  &lt;strong&gt;&lt;code&gt;permit-port-forwarding&lt;/code&gt;&lt;/strong&gt; is used to enable secure tunneling of ports, allowing users to access remote services securely through SSH.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"default_extensions"&lt;/code&gt; with &lt;code&gt;"permit-pty": ""&lt;/code&gt; are included for clarity, consistency, and to ensure that role behavior is explicitly defined.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"key_type": "ca"&lt;/code&gt; is the type of key used to log in to hosts. It can be either “OTP” or “ca”. In this example, using ca.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"default_user": "ubuntu&lt;/code&gt;" is the default host username when one isn’t specified.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"ttl": "10m0s"&lt;/code&gt; is where certificate expiry is set when signing an SSH key, in this example 10 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are using UI, on secrets engines tab, you can click ssh-creds → on roles tab, click create role → fill role name with developers → checklist allow user certificate → fill default username, with ubuntu → fill allowed user with * → select ttl and then fill with 10 minutes → fill allowed extensions with permit-pty,permit-port-forwarding → fill default extensions with "permit-pty": ""  → select signing algorithm with rsa-sha2-256.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign Public Key
&lt;/h2&gt;

&lt;p&gt;Ask Vault to sign developer's public key. This file usually ends in &lt;code&gt;.pub&lt;/code&gt; and the contents begin with &lt;code&gt;ssh-rsa ...&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write ssh-creds/sign/developers \
    public_key=&amp;lt;developer's public key&amp;gt; &amp;gt; signed-cert.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using UI, on secrets engines tab, click ssh-creds → on roles tabs, click developers → paste public key, then save.&lt;/p&gt;

&lt;p&gt;SSH into the host machine using the signed key. You must supply both the signed public key from Vault and the corresponding private key as authentication to the SSH call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -i signed-cert.pub -i &amp;lt;developer's private key&amp;gt; ubuntu@&amp;lt;ip host&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://support.cloudways.com/en/articles/5120579-why-should-you-set-up-ssh-keys" rel="noopener noreferrer"&gt;https://support.cloudways.com/en/articles/5120579-why-should-you-set-up-ssh-keys&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/vault/docs/install" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/vault/docs/install&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/vault/docs/secrets/ssh/signed-ssh-certificates" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/vault/docs/secrets/ssh/signed-ssh-certificates&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vault</category>
      <category>security</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
