<?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: Juan Luis Sanz</title>
    <description>The latest articles on Forem by Juan Luis Sanz (@juanlu_sanz).</description>
    <link>https://forem.com/juanlu_sanz</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%2F751611%2Fe209faf8-2fbb-45e7-8b07-60f22c86d015.jpeg</url>
      <title>Forem: Juan Luis Sanz</title>
      <link>https://forem.com/juanlu_sanz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/juanlu_sanz"/>
    <language>en</language>
    <item>
      <title>Notes: Distributed Systems in One Lesson by Tim Berglund</title>
      <dc:creator>Juan Luis Sanz</dc:creator>
      <pubDate>Tue, 13 Sep 2022 15:54:00 +0000</pubDate>
      <link>https://forem.com/juanlu_sanz/notes-distributed-systems-in-one-lesson-by-tim-berglund-4ea5</link>
      <guid>https://forem.com/juanlu_sanz/notes-distributed-systems-in-one-lesson-by-tim-berglund-4ea5</guid>
      <description>&lt;p&gt;Here are some notes on &lt;a href="https://www.youtube.com/watch?v=Y6Ev8GIlbxc"&gt;this&lt;/a&gt; original video from Tim Berglund. Check it out!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Y6Ev8GIlbxc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As far as the client goes, a distributed system is a set of computers that act as a single one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Characteristics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;They operate concurrently&lt;/li&gt;
&lt;li&gt;Fail independently&lt;/li&gt;
&lt;li&gt;They don't share a global clock&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three topics
&lt;/h2&gt;




&lt;h2&gt;
  
  
  1) Storage
&lt;/h2&gt;

&lt;p&gt;Start off with a single relational database, you usually get more reads than writes&lt;/p&gt;

&lt;h3&gt;
  
  
  Read replication
&lt;/h3&gt;

&lt;p&gt;To mitigate the excessive load a DB might be exposed to when the reads become too much, you can use &lt;code&gt;read replication&lt;/code&gt;. One master node for writes and several for reads. This is fine until the master node saturates (usually on writes, as reads can scale)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More info on read replication &lt;a href="https://docs.microsoft.com/en-us/azure/mysql/single-server/concepts-read-replicas"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sharding
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Sharding&lt;/code&gt;. You split the read replication scheme by a key (say First name, so from A-F is one shart, from F-N, then N-Z)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U9Ch_wpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3lpwvhgjdd5y7l283ht9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U9Ch_wpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3lpwvhgjdd5y7l283ht9.png" alt="Basic sharding" width="880" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Solves&lt;/th&gt;
&lt;th&gt;❌ Creates problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;- You now have more room to distribute your writes&lt;/td&gt;
&lt;td&gt;- Consistency is broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;- Data model is broken (no &lt;code&gt;JOIN&lt;/code&gt; across shards)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To solve latency issues, you may consider denormalizing your DB (aka removing relations between tables). Please don't.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More info on sharding &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/sharding"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Consistent hashing
&lt;/h3&gt;

&lt;p&gt;Say you have a key/value database. You hash the key and store it according to the shards you have determined. In this example&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oVQthhXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54e7uzvolfdf8boafhkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oVQthhXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54e7uzvolfdf8boafhkm.png" alt="Consistent hashing sharding" width="880" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each shard indexes a 1/8th of the content, indexed by the hash's first number. If I want to store&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;key:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Americano"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd hash the key first&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;hashedKey:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9F72"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Americano"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;find the corresponding node (in this case &lt;code&gt;A000&lt;/code&gt; since &lt;code&gt;A000&lt;/code&gt; &amp;gt; &lt;code&gt;9F72&lt;/code&gt;  &amp;gt; &lt;code&gt;8000&lt;/code&gt;) and store it there.&lt;/p&gt;

&lt;h4&gt;
  
  
  Replication
&lt;/h4&gt;

&lt;p&gt;We'd need to ensure there are no problems with system failure (aka one node going down), you can replicate the data in other nodes (say in the next two nodes as well).&lt;br&gt;
❌ The problem that comes with this is inconsistency, since you are now writing to more than one node. What if you want to write to 3 nodes but one is down? It'll be inconsistent when it's back online.&lt;/p&gt;
&lt;h4&gt;
  
  
  Consistency or R+W&amp;gt;N
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;If the number of nodes I write + the number of nodes I read is greater than the number of replicas, you have strong consistency&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  CAP theorem
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R6Fgkmar--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rmqm2v7mft1nd681yea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R6Fgkmar--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rmqm2v7mft1nd681yea.png" alt="CAP theorem" width="880" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistent&lt;/strong&gt;: When I read something I want it to be the latest version of it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Available&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition tolerant&lt;/strong&gt;: If a part of the system falls, the entire system must hold.
The theorem basically states that sometimes you must give up one of the three parts in real life applications.
Example: If a partition goes down and a read request comes in, you can either:&lt;/li&gt;
&lt;li&gt;a) Give the best answer you can, although it may not be consistent&lt;/li&gt;
&lt;li&gt;b) Sacrifice availability and return an error to ensure that no inconsistent reply is sent.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  2) Computation
&lt;/h2&gt;

&lt;p&gt;Distributed computation is basically like moving from a single threaded computation to a multithreaded one, but several times worse.&lt;/p&gt;

&lt;p&gt;Imagine you have a bunch of data sitting in a lot of computers and you need to do stuff to it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Old way: MapReduce
&lt;/h3&gt;

&lt;p&gt;Pushes all your data through two functions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Map&lt;/li&gt;
&lt;li&gt;Reduce&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: Say you have the sentence&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to eat two burgers with fries
and drink two sodas with ice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Map
&lt;/h4&gt;

&lt;p&gt;When you push it throug a &lt;code&gt;map&lt;/code&gt; function, it basically makes a "stateless" count of each word (since it won't have any memory yet, so all numbers will effectively be 1)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{I, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{want, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{to, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{eat, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{two, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{burgers, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{with, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{and, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{drink, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{two, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{sodas, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{with, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ice, 1}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Shuffle
&lt;/h4&gt;

&lt;p&gt;Since this is a distributed computation, sometimes you may need to move these words across the network, write them next to each other and so on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reduce
&lt;/h4&gt;

&lt;p&gt;The reduce function in this case will count properly all usages of a single word&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{I, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{want, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{to, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{eat, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{two, 2}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{burgers, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{with, 2}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{and, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{drink, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{sodas, 1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ice, 1}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have now managed to split the work of map and the work of reducing among many computers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More info on map-reduce &lt;a href="https://en.wikipedia.org/wiki/MapReduce"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Spark
&lt;/h3&gt;

&lt;p&gt;map-reduce becomes transform-action. Same scatter/gather paradigm.&lt;br&gt;
Spark is also storage agnostic, compared to Hadoop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kafka
&lt;/h3&gt;

&lt;p&gt;In kafka everything is a stream, in real time.&lt;/p&gt;




&lt;h2&gt;
  
  
  3) Messaging
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Systems can talk to each other instead of talking to each other via shared databases.&lt;/li&gt;
&lt;li&gt;Messages are consumed by subscribers&lt;/li&gt;
&lt;li&gt;Created by producers&lt;/li&gt;
&lt;li&gt;Organized into topics&lt;/li&gt;
&lt;li&gt;Processed by brokers&lt;/li&gt;
&lt;li&gt;Usually persistent in the long term, but you can change the retention period to whatever you want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌  Problems you may encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A topic gets too big (huge retention period, messages are huge, read and writes are too fast...)&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Guarantee delivery even if a computer is down&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Apache Kafka
&lt;/h3&gt;

&lt;p&gt;⚠ When scaling, you lose system-wide order. You still have partition-wide order.&lt;/p&gt;

&lt;p&gt;Producers will create events and randomly assign it to partitions (not really randomly, but for the sake of the argument let's assume so). These partitions will have a consumer on the other side processing whichever event is first on the queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8cNd17ZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48fi7tu3vqqh9vztha0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8cNd17ZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48fi7tu3vqqh9vztha0d.png" alt="Kafka partitions example" width="880" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The great thing is that, if after the event is created you want to store it, you can! Anyone can subscribe to the topic and from the event generation, you may have a consumer that stores on a MySQL DB the information and another that does some form of real-time processing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More info on kafka &lt;a href="https://www.confluent.io/what-is-apache-kafka/"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Done!
&lt;/h2&gt;

&lt;p&gt;I hope these short notes help you! Don't forget to check the original video and share it!&lt;/p&gt;

&lt;p&gt;Have a wonderful day!&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>architecture</category>
      <category>kafka</category>
      <category>database</category>
    </item>
    <item>
      <title>Install letsecrypt certificates on all your docker containers</title>
      <dc:creator>Juan Luis Sanz</dc:creator>
      <pubDate>Wed, 09 Feb 2022 18:27:00 +0000</pubDate>
      <link>https://forem.com/juanlu_sanz/install-letsecrypt-certificates-on-all-your-docker-containers-3ogb</link>
      <guid>https://forem.com/juanlu_sanz/install-letsecrypt-certificates-on-all-your-docker-containers-3ogb</guid>
      <description>&lt;p&gt;When dealing with containers running on a same server, you may sometimes run into the following problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I give each of the containers its own domain/subdomain? and how do I do it securely?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First of all, this problem can be addressed in several ways, and neither is more correct than another. I will simply show you a quick and easy way to do this using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Traefik&lt;/li&gt;
&lt;li&gt;Letsencrypt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and doing so automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Let's pretend we want to setup two services:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;domain&lt;/th&gt;
&lt;th&gt;port&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;grafana.mydomain.com&lt;/td&gt;
&lt;td&gt;80, 443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InfluxDB&lt;/td&gt;
&lt;td&gt;influx.mydomain.com&lt;/td&gt;
&lt;td&gt;8086&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠ This setup is just for testing purposes: any docker container will work with this method (wordpress, mariaDB, mongoDB, ELK stack, Minecraft server, custom images...)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Grafana: an amazing monitoring tool which can display data from an impressive array of sources, including influxDB. Let's say we want to monitor some service we have setup elsewhere!&lt;/li&gt;
&lt;li&gt;InfluxDB: a time-based DB, in which we can store time events (for example, the temperature of the CPU every n seconds or the number of IO operations on our drives)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do I give each container its own domain/subdomain?
&lt;/h2&gt;

&lt;p&gt;Well that depends on the setup you are running:&lt;/p&gt;

&lt;h3&gt;
  
  
  a) I run it all from a server with a public, static IP like a VPS
&lt;/h3&gt;

&lt;p&gt;Easy! just head over to your DNS settings, and create an &lt;code&gt;A record&lt;/code&gt; for each domain/subdomain. In this case both will point to the same IP address.&lt;/p&gt;

&lt;p&gt;A slightly less repetitive approach would be to point one of the domain to the IP address via an &lt;code&gt;A record&lt;/code&gt; and any other domains point towards that domain via &lt;code&gt;CNAME records&lt;/code&gt;. That way if the IP address changes, you only have to do it once!&lt;/p&gt;

&lt;h3&gt;
  
  
  b) I run it from a public IP, but it changes every now and then
&lt;/h3&gt;

&lt;p&gt;This scenario would apply if for instance you want to host from your home and the public IP address your ISP assigns you changes periodically. To circumvent this there are several possibilities which I won't go into detail, but they're easy to follow. IT boils down to having your server at home communicating with your DNS provider, telling them your public IP each time it changes, so the DNS entry can be changed accordingly.&lt;/p&gt;

&lt;p&gt;Don't worry no need to do this by hand, just look this up online for a quickstart.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a service like duckdns&lt;/li&gt;
&lt;li&gt;Cloudfare dynamic IP service&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How would my server know to which container to point to, if all DNS entries resolve to the same IP address?
&lt;/h2&gt;

&lt;p&gt;That's where &lt;code&gt;traefik&lt;/code&gt; comes in!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1) Create a docker-compose.yml file for traefik
&lt;/h3&gt;

&lt;p&gt;Here a small template, change parameters accordingly!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;         &lt;span class="c1"&gt;# HTTP port for grafana&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;       &lt;span class="c1"&gt;# HTTPS port for grafana&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8086:8086"&lt;/span&gt;     &lt;span class="c1"&gt;# Exposed port for influxDB&lt;/span&gt;

      &lt;span class="c1"&gt;# expose port below only if you need access to the Traefik API&lt;/span&gt;
      &lt;span class="c1"&gt;#- "8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;#- "--log.level=DEBUG"&lt;/span&gt;
      &lt;span class="c1"&gt;#- "--api=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entryPoints.port443.address=:443"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entryPoints.port80.address=:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entryPoints.port8086.address=:8086"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.le-ssl.acme.tlsChallenge=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.le-ssl.acme.email=myemail@mydomain.com"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesResolvers.le-ssl.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik-data:/letsencrypt/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Simple, isn't it? if you run it with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will notice not a lot has changed, and that's because we need to set the subdomains themselves on the container definitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2) Add each container's particular configuration
&lt;/h3&gt;

&lt;p&gt;So, for example, grafana and influxDB could look like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;influx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;influxdb:1.7&lt;/span&gt; &lt;span class="c1"&gt;# or any other recent version&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# SSL endpoint&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.influx-ssl.entryPoints=port8086"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.influx-ssl.rule=host(`influx.mydomain.com`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.influx-ssl.tls=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.influx-ssl.tls.certResolver=le-ssl"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.influx-ssl.service=influx-ssl"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.influx-ssl.loadBalancer.server.port=8086"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;influxdb&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;influx-data:/var/lib/influxdb&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# - INFLUXDB_DB=&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;INFLUXDB_HTTP_ENABLED=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;INFLUXDB_HTTP_AUTH_ENABLED=true&lt;/span&gt;

      &lt;span class="c1"&gt;# - INFLUXDB_ADMIN_USER=&lt;/span&gt;
      &lt;span class="c1"&gt;# - INFLUXDB_ADMIN_PASSWORD=&lt;/span&gt;

  &lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# SSL redirect requires a separate router (https://github.com/containous/traefik/issues/4688#issuecomment-477800500)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana.entryPoints=port80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana.rule=host(`grafana.mydomain.com`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.grafana-redirect.redirectScheme.scheme=https"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.grafana-redirect.redirectScheme.permanent=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana.middlewares=grafana-redirect"&lt;/span&gt;
      &lt;span class="c1"&gt;# SSL endpoint&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana-ssl.entryPoints=port443"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana-ssl.rule=host(`grafana.mydomain.com`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana-ssl.tls=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana-ssl.tls.certResolver=le-ssl"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.grafana-ssl.service=grafana-ssl"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.grafana-ssl.loadBalancer.server.port=3000"&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grafana-data:/var/lib/grafana&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GF_SERVER_ROOT_URL=https://grafana.mydomain.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GF_SERVER_DOMAIN=grafana.mydomain.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GF_USERS_ALLOW_SIGN_UP=false&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;influx-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;grafana-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Notice the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have added several labels to the usual configuration for grafana and influxDB.&lt;/li&gt;
&lt;li&gt;Port 80 on grafana will redirect to HTTPS port 443.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traefik-data&lt;/code&gt; volume has been included as well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running this will still not work because letsencrypt demands one last thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating certificates
&lt;/h2&gt;

&lt;p&gt;For letsencrypt to generate the required certificates, it must make sure that the domains we claim here are ours. To do so we have to add one more DNS entry for each subdomain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F34rp8hcvsw5gy31zr27a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F34rp8hcvsw5gy31zr27a.png" alt="Example on cloudfare"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each entry must be&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: CAA&lt;/li&gt;
&lt;li&gt;Name: Subdomain name, not the entire FQDN (in this case just &lt;code&gt;grafana&lt;/code&gt; and &lt;code&gt;influx&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Flags: 0&lt;/li&gt;
&lt;li&gt;TTL: Auto&lt;/li&gt;
&lt;li&gt;Tag: Only allow specific hostnames&lt;/li&gt;
&lt;li&gt;CA domain name: &lt;code&gt;letsencrypt.org&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Done!
&lt;/h2&gt;

&lt;p&gt;That's it! run all containers via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(add &lt;code&gt;-d&lt;/code&gt; for detacched mode)&lt;/p&gt;

&lt;p&gt;and head over to &lt;code&gt;https://grafana.mydomain.com&lt;/code&gt; to see your working grafana with custom domain and TLS!&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;Have a lovely day!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>devops</category>
      <category>docker</category>
      <category>traefik</category>
    </item>
  </channel>
</rss>
