<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Fossian]]></title><description><![CDATA[Your gateway to Cloud, Linux and Open Source!]]></description><link>https://fossian.com/</link><image><url>https://fossian.com/favicon.png</url><title>Fossian</title><link>https://fossian.com/</link></image><generator>Ghost 5.33</generator><lastBuildDate>Sat, 04 Apr 2026 05:58:45 GMT</lastBuildDate><atom:link href="https://fossian.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Docker-in-Docker: Building Images inside a Running Container]]></title><description><![CDATA[<p>Imagine you are heading Project X in your friend&apos;s start-up. Your team has 5 people and you sat up yesterday, night to build the application, and it&apos;s finally done and running within your docker container. Your teammates come to work today, and you want them to</p>]]></description><link>https://fossian.com/docker-in-docker-building-images-inside-a-running-container/</link><guid isPermaLink="false">63e9f2b41f02a9599e5746c5</guid><category><![CDATA[Cloud]]></category><category><![CDATA[DevOps]]></category><dc:creator><![CDATA[Amresh Sinha]]></dc:creator><pubDate>Fri, 30 Jun 2023 08:58:30 GMT</pubDate><media:content url="https://fossian.com/content/images/2023/06/Docker-in-Docker.png" medium="image"/><content:encoded><![CDATA[<img src="https://fossian.com/content/images/2023/06/Docker-in-Docker.png" alt="Docker-in-Docker: Building Images inside a Running Container"><p>Imagine you are heading Project X in your friend&apos;s start-up. Your team has 5 people and you sat up yesterday, night to build the application, and it&apos;s finally done and running within your docker container. Your teammates come to work today, and you want them to be able to run it too, but they seem to be running into all kinds of dependency problems. You&apos;re frustrated because this background work takes too much of your time, and the release is next week. What can you do?</p><p>You finally came up with a solution - docker-in-docker!</p><p>This is not the only situation where it helps; there are many other situations too, where you might need docker-in-docker; a few are:</p><ul><li>Testing: You can ensure everything works by testing your application and its dependencies within a container. Then, you can save the configuration as an image that can be used by other team members or even deployed to production.</li><li>Running CI/CD tools like Jenkins.</li></ul><h2 id="requirements">Requirements:</h2><!--kg-card-begin: markdown--><ul>
<li>A running container with docker installed in it (Method-1)</li>
<li>Access to host <code>docker.sock</code> (Method-2)
<ul>
<li>Attach a volume: <code>/var/run/docker.sock:/var/run/docker.sock</code></li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><h2 id="method-1-docker-in-docker-with-docker-installed">Method-1 (Docker-in-Docker with Docker installed)</h2><h2 id="step-1">Step-1</h2><p>Run the particular container and make sure it has docker installed. For the sake of simplicity, I am using the official <a href="https://hub.docker.com/_/docker">Docker</a> image here.</p><pre><code class="language-bash">docker run -it --name build-container docker sh</code></pre><!--kg-card-begin: markdown--><p>Here,</p>
<ul>
<li><code>t</code> allocates a tty</li>
<li><code>i</code> keeps it interactive</li>
</ul>
<!--kg-card-end: markdown--><h2 id="step-2">Step-2</h2><p>Import your code. I am using this <a href="https://github.com/thejungwon/docker-reactjs">sample crypto price monitor</a> react app for this post.</p><p>Source Code: <a href="https://github.com/thejungwon/docker-reactjs">https://github.com/thejungwon/docker-reactjs</a></p><!--kg-card-begin: html--><pre class="command-line language-bash" data-prompt="/ #"><code>git clone https://github.com/thejungwon/docker-reactjs
cd docker-reactjs
docker build -t docker-reactjs:latest .</code></pre><!--kg-card-end: html--><h2 id="step-3">Step-3</h2><p>Once the image building finishes, you can export it as a tar file.</p><!--kg-card-begin: html--><pre class="command-line language-bash" data-prompt="/ #"><code>docker save docker-reactjs:latest &gt; /tmp/docker-reactjs.tar</code></pre><!--kg-card-end: html--><h2 id="step-4">Step-4</h2><p>Now exit out of the container and run the <code>docker cp</code> command for copying the tar image file.</p><!--kg-card-begin: html--><pre style="display: flex; flex-direction: column; gap: 0;"><pre class="command-line language-bash" data-prompt="/ #" style="margin-bottom:0 !important; border-radius: 5px 5px 0 0 !important;"><code>exit</code></pre>
<pre class="command-line language-bash" style="margin-top: 0 !important; margin-bottom:0 !important; border-radius: 0 0 5px 5px !important;"><code>docker cp build-container:/tmp/docker-reactjs.tar .</code></pre></pre><!--kg-card-end: html--><h2 id="step-5">Step-5</h2><p>Now import the image from the tar file using <code>docker load</code> command.</p><pre><code class="language-bash">docker load -i docker-reactjs.tar</code></pre><p>After loading the image, you can run the container as usual.</p><pre><code class="language-bash">docker run docker-reactjs:latest</code></pre><h2 id="method-2-docker-in-docker-using-hosts-varrundockersock">Method-2 (Docker-in-Docker using Host&apos;s <code>/var/run/docker.sock</code>)</h2><h2 id="step-1-1">Step-1</h2><p>Make sure you can make requests to the socket. Run the following command in the host machine to check that.</p><pre><code class="language-bash">curl --unix-socket /var/run/docker.sock http://localhost/version</code></pre><!--kg-card-begin: markdown--><p>Here, <code>--unix-socket</code> helps you connect through the Unix domain socket (HTTP).</p>
<!--kg-card-end: markdown--><h2 id="step-2-1">Step-2</h2><p>To run docker inside docker attach the socket as a volume to the container. &#xA0;For example:</p><pre><code class="language-bash">docker run -v /var/run/docker.sock:/var/run/docker.sock \
    -ti docker</code></pre><!--kg-card-begin: markdown--><p>Here,</p>
<ul>
<li><code>-v</code> represents volume</li>
<li><code>t</code> allocates a tty</li>
<li><code>i</code> keeps it interactive</li>
</ul>
<!--kg-card-end: markdown--><div class="kg-card kg-callout-card kg-callout-card-red"><div class="kg-callout-emoji">&#x2757;</div><div class="kg-callout-text"><strong>Caution: </strong>Play safe when giving the host&apos;s docker socket connection to a container. The container will have more privileges over your docker daemon.</div></div><p>Now you can run docker commands inside the container which will run on the host machine.</p><h2 id="step-3-1">Step-3</h2><p>After running the previous command, you should be inside the container. Once inside, you can run docker commands which run on host machine.</p><pre><code class="language-bash">docker run hello-world</code></pre><p>Let&apos;s try it out with a custom image. Create a <code>Dockerfile</code> with the following content in it.</p><pre><code class="language-YAML">FROM node:20-slim

LABEL maintainer=&quot;AmreshSinha &lt;AmreshSinha@users.noreply.github.com&gt;&quot;

WORKDIR /usr/node
WORKDIR app

RUN pwd

# command executable and version
ENTRYPOINT [&quot;node&quot;]</code></pre><p>Now build the app.</p><pre><code class="language-bash">docker build -t test-image .</code></pre><h2 id="conclusion">Conclusion</h2><p>There are many more ways to go the docker-in-docker road other than the methods we discussed above. For example, dind or using another runtime like sysbox runtime for better security, etc. </p><p>We will discuss them in future posts. Till then... &#x1F44B;</p>]]></content:encoded></item><item><title><![CDATA[Self-Host Ghost Blog with Mysql and Traefik]]></title><description><![CDATA[<p>Ghost is a very popular open-source CMS. It is a great alternative to WordPress and Substack because its easy-to-use and also has several membership options. One can easily start a blog or a membership-based newsletter with a few clicks from the Ghost admin dashboard.</p><p>There are many managed Ghost hosting</p>]]></description><link>https://fossian.com/self-host-ghost-blog-mysql-traefik/</link><guid isPermaLink="false">62b99f9202f649c7182512b7</guid><category><![CDATA[DevOps]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Amresh Sinha]]></dc:creator><pubDate>Sat, 02 Jul 2022 06:30:45 GMT</pubDate><media:content url="https://fossian.com/content/images/2022/07/cue--4--1.png" medium="image"/><content:encoded><![CDATA[<img src="https://fossian.com/content/images/2022/07/cue--4--1.png" alt="Self-Host Ghost Blog with Mysql and Traefik"><p>Ghost is a very popular open-source CMS. It is a great alternative to WordPress and Substack because its easy-to-use and also has several membership options. One can easily start a blog or a membership-based newsletter with a few clicks from the Ghost admin dashboard.</p><p>There are many managed Ghost hosting providers out there which manage the hosting, backup, SMTP and other backend stuff for you. But it may not be very budget-friendly for everyone.</p><p>In this small post, we are going to self-host our Ghost blog with Mysql in Docker and reverse proxy it using Traefik.</p><h2 id="prerequisites">Prerequisites</h2><ul><li>A domain name for your blog. Try to go with <code>.com</code> and make sure it&apos;s professional and suits your needs.</li><li>(Optional) An SMTP service for setting up email and newsletter. You can try <a href="https://www.mailgun.com/pricing/">Mailgun</a> as recommended by Ghost (Not free).</li><li>A server with Docker and Traefik installed.</li></ul><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x2757;</div><div class="kg-callout-text">If you haven&apos;t gone through the installation of Traefik, I would recommend that you follow this guide before reading further.</div></div><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fossian.com/simplify-deployment-traefik-walk-through/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Simplify your deployment with Traefik - A comprehensive walk-through</div><div class="kg-bookmark-description">Deploying docker containers, managing routes, generating TLS certificates, and load balancing can be a hassle sometimes. It may become more difficult to manage all the stuff if you want to run multiple docker containers on the same host!</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fossian.com/content/images/size/w256h256/2022/06/White-Pink-Kitty-Shop-Logo.png" alt="Self-Host Ghost Blog with Mysql and Traefik"><span class="kg-bookmark-author">Fossian</span><span class="kg-bookmark-publisher">Amresh Sinha</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://fossian.com/content/images/2022/06/Docker-and-Traefik--1-.png" alt="Self-Host Ghost Blog with Mysql and Traefik"></div></a></figure><h2 id="step-1-creating-config-files-for-ghost">Step 1: Creating Config files for Ghost</h2><p>Create a directory for storing our <code>docker-compose.yml</code> file and create a sub-directory <code>blog</code> for storing Ghost configuration file as well as a directory <code>content</code> under <code>blog</code> for storing images and logging info.</p><pre><code class="language-bash">mkdir -p ghost-blog/blog/content &amp;&amp; \
  cd ghost-blog &amp;&amp; \
  touch ./blog/config.production.json &amp;&amp; \
  nano ./docker-compose.yml</code></pre><p>This should be the final tree:</p><pre><code>&#x251C;&#x2500;&#x2500; ghost-blog
&#x2502;   &#x251C;&#x2500;&#x2500; docker-compose.yml
&#x2502;   &#x2514;&#x2500;&#x2500; blog
&#x2502;       &#x251C;&#x2500;&#x2500; config.production.json
&#x2502;       &#x2514;&#x2500;&#x2500; content
 </code></pre><!--kg-card-begin: html--><pre class="line-numbers language-yaml"><code>version: &apos;3.7&apos;

services:
  ghost:
    image: ghost:5.2.3
    restart: always
    depends_on:
      - db
    environment:
      NODE_ENV: production
    networks:
      - default
      - web
    volumes:
      - ./blog/content:/var/lib/ghost/content
      - ./blog/config.production.json:/var/lib/ghost/config.production.json
    labels:
      - &quot;traefik.enable=true&quot;
      - &quot;traefik.docker.network=web&quot;
      - &quot;traefik.http.routers.ghost-secure.entrypoints=websecure&quot;
      - &quot;traefik.http.routers.ghost-secure.rule=Host(`fossian.com`)&quot;
      - &quot;traefik.http.routers.ghost-secure.service=ghost-service&quot;
      - &quot;traefik.http.services.ghost-service.loadbalancer.server.port=2368&quot;

  db:
    image: mysql:oracle
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: your_mysql_root_password
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ghostdbpass
      MYSQL_DATABASE: ghostdb
    networks:
      - default
    volumes:
      - ./data:/var/lib/mysql
      
networks:
  web:
    external: true</code></pre><!--kg-card-end: html--><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Official Docker images of Mysql now support multi-architecture (<code>amd64</code> &amp; <code>arm64</code>) with the tag <code>oracle</code>.</div></div><p>Don&apos;t forget to change <code>fossian.com</code> to your domain on line 21. Also, change the database user and passwords under <code>db</code> service (line 29 - 32).</p><p>Now edit the main Ghost configuration file <code>config.production.json</code>.</p><pre><code class="language-bash">nano ./blog/config.production.json</code></pre><!--kg-card-begin: html--><pre class="line-numbers language-json"><code> {
  &quot;url&quot;: &quot;https://fossian.com&quot;,
  &quot;server&quot;: {
    &quot;port&quot;: 2368,
    &quot;host&quot;: &quot;0.0.0.0&quot;
  },
  &quot;database&quot;: {
    &quot;client&quot;: &quot;mysql&quot;,
    &quot;connection&quot;: {
      &quot;host&quot;: &quot;db&quot;,
      &quot;port&quot;: 3306,
      &quot;user&quot;: &quot;ghost&quot;,
      &quot;password&quot;: &quot;ghostdbpass&quot;,
      &quot;database&quot;: &quot;ghostdb&quot;
    }
  },
  &quot;mail&quot;: {
    &quot;transport&quot;: &quot;SMTP&quot;,
    &quot;options&quot;: {
      &quot;service&quot;: &quot;Mailgun&quot;,
      &quot;auth&quot;: {
      	&quot;user&quot;: &quot;postmaster@example.mailgun.org&quot;,
      	&quot;pass&quot;: &quot;1234567890&quot;
      }
    }
  },
  &quot;logging&quot;: {
    &quot;path&quot;: &quot;/var/lib/ghost/content/logs/&quot;,
    &quot;level&quot;: &quot;info&quot;,
    &quot;rotation&quot;: {
      &quot;enabled&quot;: true,
      &quot;count&quot;: 15,
      &quot;period&quot;: &quot;1d&quot;
    },
    &quot;transports&quot;: [&quot;stdout&quot;, &quot;file&quot;]
  },
  &quot;paths&quot;: {
    &quot;contentPath&quot;: &quot;/var/lib/ghost/content&quot;
  }
}</code></pre><!--kg-card-end: html--><ul><li>Change the URL on line 2 to your Ghost blog URL.</li><li>Change the database credentials according to what you filled in <code>docker-compose.yml</code> (line 12 - 14).</li><li>If you want to enable Membership and Newsletter functionality then make sure you have an SMTP server. I recommend using <a href="https://www.mailgun.com/">mailgun</a> as it supports the bulk transfer feature. Change the SMTP credentials on lines 18 to 25 to yours.<br>If you don&apos;t want to enable it then simply change it as shown below.</li></ul><!--kg-card-begin: html--><pre class="line-numbers language-json"><code>&quot;mail&quot;: {
    &quot;transport&quot;: &quot;Direct&quot;,
},</code></pre><!--kg-card-end: html--><p>You can check out Ghost&apos;s official <a href="https://ghost.org/docs/config/#mail">Configuration page</a> for more info.</p><h2 id="step-2-deploying">Step 2: Deploying</h2><p>After setting up everything, simply use <code>docker-compose</code> &#xA0;to deploy your Ghost blog.</p><pre><code class="language-bash">docker-compose up -d</code></pre><figure class="kg-card kg-image-card"><img src="https://fossian.com/content/images/2022/07/image.png" class="kg-image" alt="Self-Host Ghost Blog with Mysql and Traefik" loading="lazy" width="1536" height="864" srcset="https://fossian.com/content/images/size/w600/2022/07/image.png 600w, https://fossian.com/content/images/size/w1000/2022/07/image.png 1000w, https://fossian.com/content/images/2022/07/image.png 1536w" sizes="(min-width: 720px) 720px"></figure><p><strong>That&apos;s it!</strong> You have a fully functional Ghost blog running in Docker and proxying with Traefik. Make sure to log in to your admin dashboard at <code>https://yourdomain.tld/ghost</code>. </p><p>Feel free to comment below if you have any issues regarding the installation!</p><p></p>]]></content:encoded></item><item><title><![CDATA[Simplify your deployment with Traefik - A comprehensive walk-through]]></title><description><![CDATA[Deploying docker containers, managing routes, generating TLS certificates, and load balancing can be a hassle sometimes. It may become more difficult to manage all the stuff if you want to run multiple docker containers on the same host!]]></description><link>https://fossian.com/simplify-deployment-traefik-walk-through/</link><guid isPermaLink="false">62b455acde5af374d3690dca</guid><category><![CDATA[DevOps]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Amresh Sinha]]></dc:creator><pubDate>Sun, 26 Jun 2022 12:30:40 GMT</pubDate><media:content url="https://fossian.com/content/images/2022/06/Docker-and-Traefik--1-.png" medium="image"/><content:encoded><![CDATA[<img src="https://fossian.com/content/images/2022/06/Docker-and-Traefik--1-.png" alt="Simplify your deployment with Traefik - A comprehensive walk-through"><p>Deploying docker containers, managing routes, generating TLS certificates, and load balancing can be a hassle sometimes. It may become more difficult to manage all of this if you want to run multiple docker containers on the same host!</p><p>Solution? For running multiple services on the same host you can use a reverse proxy. A reverse proxy is a service running on your host machine which maps each public request to its corresponding backend service running on the same host. There are many reverse proxies out there. One of which is <a href="https://doc.traefik.io/traefik/">Traefik</a>!</p><h2 id="traefik">Traefik</h2><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://doc.traefik.io/traefik/assets/img/traefik-architecture.png" class="kg-image" alt="Simplify your deployment with Traefik - A comprehensive walk-through" loading="lazy"><figcaption>Traefik 2 Docs</figcaption></figure><p>Traefik is an open-source router which takes care of reverse proxying requests, load balancing, TLS certificate generation (let&apos;s encrypt), etc. It supports HTTP, HTTP/2, TCP, UDP, Websockets, and gRPC protocols. It also has a clean and modern dashboard for monitoring. And adding to all these, it is way easier to use as compared to Nginx!</p><p>For more details: <a href="https://traefik.io/traefik/">https://traefik.io/traefik/</a></p><p>In this post, I will guide you through setting up Traefik 2 and running a simple example Node.js app.</p><h2 id="prerequisites">Prerequisites</h2><p>For the sake of this post, I am going to use a VM instance from <a href="https://www.oracle.com/in/cloud/free/">Oracle Cloud</a> free tier (2 vCPU arm64, 8GB ram, Ubuntu 22.04). You are free to use whatever host you want.</p><h3 id="requirements">Requirements:</h3><ul><li>A Linux server with Docker installed. Having at least 2 GB of RAM will be better for running multiple containers. Though you can run it on an even smaller server.</li><li><code>docker-compose</code> should also be installed.</li><li>A domain name with a wildcard record pointing towards your server IP<br>Here is a sample DNS record which you can use to set up for your domain.</li></ul><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Content</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>*</td>
<td>server.ip</td>
<td>Auto</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>This will make <code>*.example.com</code> available for Traefik. This means you can route your apps and services with names like blog.example.com, forum.example.com, dashboard.example.com, etc. </p><p>If you don&apos;t want to make a wildcard record for the main domain name but instead for a subdomain then you can change the value under Name to <code>*.subdomain</code> . This will result in names like xyz.subdomain.example.com, dashboard.subdomain.example.com, etc.</p><h2 id="step-1">Step-1</h2><p>Before going any further we need to update our system.</p><pre><code class="language-bash">sudo apt-get update</code></pre><p>Now, you can install docker and docker-compose.</p><pre><code class="language-bash">sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release &amp;&amp; \
    sudo mkdir -p /etc/apt/keyrings &amp;&amp; \
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg &amp;&amp; \
    echo \
    &quot;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) stable&quot; | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null &amp;&amp; \
    sudo apt-get update &amp;&amp; \
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-compose &amp;&amp; \
    sudo groupadd docker || sudo usermod -aG docker $USER &amp;&amp; \
    newgrp docker &amp;&amp; \
    sudo systemctl enable docker.service &amp;&amp; sudo systemctl enable containerd.service</code></pre><p>(I have munched up all the commands in one single just to save some time. It even includes some post-installation steps)</p><p>Verify your docker installation by running the famous <code>hello-world</code> image</p><pre><code class="language-bash">docker run hello-world</code></pre><h2 id="step-2">Step-2</h2><p>Let&apos;s start with Traefik. Create a data folder for storing Traefik configuration file <code>traefik.yml</code>, <code>acme.json</code> for storing certificates and a configurations folder for storing dynamic configurations file <code>dynamic.yml</code>.</p><pre><code class="language-bash">mkdir -p data/configurations &amp;&amp; \
  touch data/traefik.yml &amp;&amp; \
  touch data/acme.json &amp;&amp; \
  touch data/configurations/dynamic.yml &amp;&amp; \
  chmod 600 data/acme.json</code></pre><p>Now create a <code>docker-compose.yml</code> for Traefik.</p><pre><code class="language-bash">nano docker-compose.yml</code></pre><p>and add the following lines to <code>docker-compose.yml</code></p><p>Replace <code>traefik.yourdomain</code> on line 25 with your dashboard URL.<br>Example: <code>traefik.fossian.com</code> or <code>dash.fossian.com</code></p><!--kg-card-begin: html--><pre class="line-numbers language-yaml"><code>version: &apos;3.7&apos;

services:
  traefik:
    image: traefik:v2.7
    container_name: traefik
    restart: always
    security_opt:
      - no-new-privileges:true
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
      - ./data/configurations:/configurations
    networks:
      - web
    labels:
      - &quot;traefik.enable=true&quot;
      - &quot;traefik.docker.network=web&quot;
      - &quot;traefik.http.routers.traefik-secure.entrypoints=websecure&quot;
      - &quot;traefik.http.routers.traefik-secure.rule=Host(`traefik.yourdomain`)&quot;
      - &quot;traefik.http.routers.traefik-secure.middlewares=user-auth@file&quot;
      - &quot;traefik.http.routers.traefik-secure.service=api@internal&quot;

networks:
  web:
    external: true
</code></pre><!--kg-card-end: html--><p>There&apos;s a lot of stuff going on here so let&apos;s understand some part of it.</p><p>We have passed <code>no-new-privileges</code> as true to avoid container processes from gaining additional privileges.</p><p>We have added <code>docker.sock</code> file in volumes so that Traefik can listen to all the changes happening to the containers. We have also added our configuration file <code>traefik.yml</code> and <code>acme.json</code>. We also added the path to our dynamic configuration file.</p><p>We have also set the network to <code>web</code>.</p><p>Then comes the labels. We have defined <code>traefik.docker.network</code> to <code>web</code>. We have defined <a href="https://doc.traefik.io/traefik/routing/entrypoints/">entrypoint</a> to <code>websecure</code> which listens on port 443. Then we have defined a <a href="https://doc.traefik.io/traefik/routing/routers/#rule">rule</a> for our Traefik dashboard.</p><p>Now let&apos;s edit Traefik configuration file <code>traefik.yml</code>.</p><pre><code class="language-bash">nano ./data/traefik.yml</code></pre><p>and add the following to it</p><p>Replace <code>admin@yourdomain</code> on lines 34 and 42 with your personal or website email. It will be used by Let&apos;s Encrypt to notify you regarding the expiration of TLS certificates. Nonetheless, you don&apos;t have to worry about regenerating certificates as Traefik will do it for you automatically.</p><!--kg-card-begin: html--><pre class="line-numbers language-yaml"><code>api:
  dashboard: true

entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure

  websecure:
    address: :443
    http:
      middlewares:
        - secureHeaders@file
        - nofloc@file
      tls:
        certResolver: letsencrypt

pilot:
  dashboard: false

providers:
  docker:
    endpoint: &quot;unix:///var/run/docker.sock&quot;
    exposedByDefault: false
  file:
    filename: /configurations/dynamic.yml

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@yourdomain
      storage: acme.json
      keyType: EC384
      httpChallenge:
        entryPoint: web

  buypass:
    acme:
      email: admin@yourdomain
      storage: acme.json
      caServer: https://api.buypass.com/acme/directory
      keyType: EC256
      httpChallenge:
        entryPoint: web
</code></pre><!--kg-card-end: html--><p>Now edit <code>dynamic.yml</code> </p><pre><code class="language-bash">nano ./data/configurations/dynamic.yml</code></pre><p>and add the following to it</p><!--kg-card-begin: html--><pre class="line-numbers language-yaml"><code>http:
  middlewares:
    nofloc:
      headers:
        customResponseHeaders:
          Permissions-Policy: &quot;interest-cohort=()&quot;
    secureHeaders:
      headers:
        sslRedirect: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000   
        
    # Generate yours using htpasswd
    # UserName : admin
    # Password : fossian    
    user-auth:
      basicAuth:
        users:
          # Generate using &quot;htpasswd -nb admin secure_password&quot;
          - &quot;admin:$apr1$B5scLRiX$ivpndRS3XuqZ05oaDgHql0&quot;

tls:
  options:
    default:
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
      minVersion: VersionTLS12
</code></pre><!--kg-card-end: html--><p>To change the password install <code>htpasswd</code> which is a part of <code>apache2-utils</code> package.</p><pre><code class="language-bash">sudo apt-get install apache2-utils</code></pre><p>Then run this (make sure you replace secure_password with your password)</p><pre><code class="language-bash">htpasswd -nb admin secure_password</code></pre><p>This will return you something like this: <code>admin:$apr1$L0VB4It1$Yx4tfFO26W68ikz/6VZOG1</code></p><p>You can now replace the old one (line 22) with your newly generated one.</p><h2 id="step-3">Step-3</h2><p>Now all the things are set. Just one more thing to do before running your shiny Traefik container, i.e., Creating the docker network <code>web</code> which we provided in the <code>docker-compose.yml</code>.</p><p>First, check if the network <code>web</code> is there or not.</p><pre><code class="language-bash">docker network ls</code></pre><p>If there is a network named <code>web</code> then you can skip the below command otherwise run it to make one.</p><pre><code class="language-bash">docker network create web</code></pre><p>When we will run other containers we can add them to this network so that Traefik can proxy them.</p><p>Now everything is set.</p><p>Run the Traefik container using docker-compose</p><pre><code class="language-bash">docker-compose up -d</code></pre><p>If everything goes well you can access your Traefik dashboard at <code>traefik.yourdomain</code> or the URL which you added in place of that.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://fossian.com/content/images/2022/06/image-3.png" class="kg-image" alt="Simplify your deployment with Traefik - A comprehensive walk-through" loading="lazy" width="1536" height="754" srcset="https://fossian.com/content/images/size/w600/2022/06/image-3.png 600w, https://fossian.com/content/images/size/w1000/2022/06/image-3.png 1000w, https://fossian.com/content/images/2022/06/image-3.png 1536w" sizes="(min-width: 1200px) 1200px"><figcaption>Traefik Dashboard</figcaption></figure><p>There you have your own reverse proxy setup and running. </p><p>After setting Traefik, adding new docker containers is easy. You just have to define some labels and you are good to go.</p><h2 id="step-4-hosting-a-nodejs-express-webapp">Step-4: Hosting a node.js express webapp</h2><p>We are going to deploy a simple node.js express web app.<br>Github: <a href="https://github.com/fossian-com/sample-webapp-nodejs">https://github.com/fossian-com/sample-webapp-nodejs</a></p><p>First, clone it to your server.</p><pre><code class="language-bash">git clone https://github.com/fossian-com/sample-webapp-nodejs</code></pre><p>Now, cd into the directory <code>sample-webapp-nodejs</code> and create a <code>docker-compose.yml</code> file.</p><pre><code class="language-bash">cd sample-webapp-nodejs &amp;&amp; nano docker-compose.yml</code></pre><p>and add the following</p><p>Make sure you replace <code>nodeapp.yourdomain</code> at line 21 with the URL you want.<br>Example: <code>nodeapp.fossian.com</code> or <code>express.fossian.com</code></p><!--kg-card-begin: html--><pre class="line-numbers language-yaml"><code>version: &apos;3&apos;

services:
  nodejsapp:
    # image: image-name:tag
    build: .
    container_name: sample-webapp-nodejs
    environment:
      PORT: 8080
    networks:
      - web
    restart: always
    labels:
      # Allow Traefik to access the container
      - &quot;traefik.enable=true&quot;
      # Tells Traefik to look in web to find the internal IP of the container
      - &quot;traefik.docker.network=web&quot;
      # Entrypoints to websecure i.e., port 443
      - &quot;traefik.http.routers.ghost-secure.entrypoints=websecure&quot;
      # Rule for the url for webapp container
      - &quot;traefik.http.routers.ghost-secure.rule=Host(`nodeapp.yourdomain`)&quot;

networks:
  web:
    external: true</code></pre><!--kg-card-end: html--><p>Now simply run</p><pre><code class="language-bash">docker-compose up -d</code></pre><p>and after successful deployment access your app at <code>nodeapp.yourdomain</code> or the URL which you provided.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://fossian.com/content/images/2022/06/image-4.png" class="kg-image" alt="Simplify your deployment with Traefik - A comprehensive walk-through" loading="lazy" width="1536" height="864" srcset="https://fossian.com/content/images/size/w600/2022/06/image-4.png 600w, https://fossian.com/content/images/size/w1000/2022/06/image-4.png 1000w, https://fossian.com/content/images/2022/06/image-4.png 1536w" sizes="(min-width: 720px) 720px"><figcaption>Sample Nodejs Express WebApp</figcaption></figure><p>You can also see on your Traefik dashboard that a new route has been added.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://fossian.com/content/images/2022/06/image-5.png" class="kg-image" alt="Simplify your deployment with Traefik - A comprehensive walk-through" loading="lazy" width="1536" height="864" srcset="https://fossian.com/content/images/size/w600/2022/06/image-5.png 600w, https://fossian.com/content/images/size/w1000/2022/06/image-5.png 1000w, https://fossian.com/content/images/2022/06/image-5.png 1536w" sizes="(min-width: 720px) 720px"><figcaption>sample-webapp-secure has been added</figcaption></figure><p>If you plan to run a full-fledge Nodejs app with database support then make sure you add a <code>db</code> service in docker-compose under the same network with the Nodejs app.</p><p>For example:</p><!--kg-card-begin: html--><pre class="line-numbers language-yaml"><code>version: &apos;3&apos;

services:
  db:
    image: mariadb
    container_name: webapp-db
    volumes:
      - db-data:/var/lib/mysql
    networks:
      # For accepting communication from the webapp
      - default
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: db
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbpassword

  nodejsapp:
    depends_on:
      - db
    # image: image-name:tag
    build: .
    container_name: sample-webapp-nodejs
    environment:
      PORT: 8080
      DB_HOST: db:3306
      DB_NAME: db
      DB_USER: dbuser
      DB_PASSWORD: dbpassword
    networks:
      # For communicating with the db
      - default
      - web
    restart: always
    labels:
      # Allow Traefik to access the container
      - &quot;traefik.enable=true&quot;
      # Tells Traefik to look in web to find the internal IP of the container
      - &quot;traefik.docker.network=web&quot;
      # Entrypoints to websecure i.e., port 443
      - &quot;traefik.http.routers.ghost-secure.entrypoints=websecure&quot;
      # Rule for the url for webapp container
      - &quot;traefik.http.routers.ghost-secure.rule=Host(`nodeapp.yourdomain`)&quot;

volumes:
  db-data:
    # Naming the database volume
    name: nodejs-webapp-db-data
networks:
  web:
    external: true</code></pre><!--kg-card-end: html--><h2 id="conclusion">Conclusion</h2><p>In this tutorial, we installed Traefik, ran a simple web app container and learnt how to reverse proxy it with Traefik using a few lines of code.</p><p>You could have done the same with Nginx but it wouldn&apos;t be this easy! Traefik handles everything on its own and you don&apos;t have to restart it every time you deploy or make any changes to any running container. With just a few lines in <code>docker-compose.yml</code>, we were able to reverse proxy the web app and even secure it with a TLS certificate.</p><p>You can learn more about Traefik here:<a href="https://doc.traefik.io/traefik/"> Traefik Proxy Documentation</a>.</p>]]></content:encoded></item></channel></rss>