<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>The blog of Pierre-Gilles Leymarie</title>
 <link href="https://pierregillesleymarie.com/atom.xml" rel="self"/>
 <link href="https://pierregillesleymarie.com"/>
 <updated>2021-02-20T10:29:12+00:00</updated>
 <id>https://pierregillesleymarie.com</id>
 <author>
   <name>Pierre-Gilles Leymarie</name>
   <email></email>
 </author>

 
 <entry>
   <title>Living in Greece as a Digital Nomad</title>
   <link href="https://pierregillesleymarie.com/blog/2021/02/17/living-in-greece-as-a-digital-nomad.html"/>
   <updated>2021-02-17T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2021/02/17/living-in-greece-as-a-digital-nomad</id>
   <content type="html">&lt;p&gt;After spending summer 2020 in Barcelona (Spain) with my girlfriend, we decided to move to Greece for the end of the year. We were expecting to go to Bali at first, but we couldn’t go there due to coronavirus. So we stayed in Europe, in the only country with a great temperature during fall.&lt;/p&gt;

&lt;p&gt;We started with 2 weeks of travelling, going from Athens, to Mykonos, Paros, Naxos, Santorini, and finally Crete, our final destination.&lt;/p&gt;

&lt;p&gt;We didn’t work in the islands, it was just holidays, and I don’t think it would have been easy to work: wifi is not that great in the islands, and all those places are designed for tourists.&lt;/p&gt;

&lt;h2 id=&quot;our-holidays-in-athens--the-greek-islands&quot;&gt;Our holidays in Athens &amp;amp; the Greek Islands&lt;/h2&gt;

&lt;h3 id=&quot;athens&quot;&gt;Athens&lt;/h3&gt;

&lt;p&gt;We landed in Athens end of September 2020, and we stayed a few days there. We visited as much as we could, and enjoyed our first bites of Greek food.&lt;/p&gt;

&lt;p&gt;In only three days we were able to visit most of Athens historical sites such as the famous Acropolis, the ancient agora, the temple of Hephaestus and many more. Mostly everything is at a walking distance from the city center.&lt;/p&gt;

&lt;p&gt;We were amazed how cheap the prices are in Athens, and how good is the food. It seemed very authentic and very local to us, even more when we compared to the Island that are very touristy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7169.jpeg&quot; alt=&quot;Athens&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7143.jpeg&quot; alt=&quot;Athens&quot; /&gt;&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/CFcZSeKiCd4/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; data-instgrm-version=&quot;13&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/CFcZSeKiCd4/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; Voir cette publication sur Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/p/CFcZSeKiCd4/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;Une publication partagée par Pierre-Gilles Leymarie (@pierregilles.leymarie)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;mykonos&quot;&gt;Mykonos&lt;/h3&gt;

&lt;p&gt;After our discovery of the capital we took the ferry to Mykonos.&lt;/p&gt;

&lt;p&gt;Mykonos is a very beautiful Island, it looks like the typical greek island cliché.
The city center is a maze of narrow white streets, small gift shop and restaurants on the edge of the water.
On the south coast of the Island you’ll find most of the beautiful beaches and their famous beach club (Cavo Paradision, Super Paradise Beach Club, Scorpios…).&lt;/p&gt;

&lt;p&gt;The only downfall is that prices are really expensive, it’s probably the most expensive greek island with Santorini, and it looks a little bit too touristy.&lt;/p&gt;

&lt;p&gt;With Covid 19, we didn’t see the nightlife as most of the clubs were empty, but at least we had the beaches for ourselves!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7298.jpeg&quot; alt=&quot;Mykonos&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7206.jpeg&quot; alt=&quot;Mykonos&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;paros&quot;&gt;Paros&lt;/h2&gt;

&lt;p&gt;From Mykonos we went to Paros, a calmer island known to be great for couples: and it was!&lt;/p&gt;

&lt;p&gt;We had an amazing time here: it’s really calm, cosy, and with nice restaurants.
The main activity is to wander in the white streets covered with pink bougainvillea of Paros city and Naoussa (in the north of the island).
While in Naoussa we went to Moraitis Winery to taste some good wine and discover their fabrication.&lt;/p&gt;

&lt;p&gt;We spent an afternoon in Antiparos, a really small island 10 minutes away from Paros with hidden beach and warm water.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7612.jpeg&quot; alt=&quot;Paros&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_3347.jpeg&quot; alt=&quot;Paros food&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;naxos&quot;&gt;Naxos&lt;/h2&gt;

&lt;p&gt;After visiting Paros we headed to Naxos.&lt;/p&gt;

&lt;p&gt;Naxos is a great island for hiking, their is a lot of trek trails between all the mountains villages and the main attraction : Zeus Mount, the highest points of the Cyclades.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_3605.jpeg&quot; alt=&quot;Naxos&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s a nice hike to go to the top where we enjoyed a beautiful view of all the greeks Islands.&lt;/p&gt;

&lt;p&gt;In the city center, the beautiful Apollo Temple is a magical place to catch the sunset.&lt;/p&gt;

&lt;h2 id=&quot;santorini&quot;&gt;Santorini&lt;/h2&gt;

&lt;p&gt;Last but not least : Santorini.
We had high expectations when we arrived in Santorini, and we had a really great stay.&lt;/p&gt;

&lt;p&gt;We stayed in Oia at the top of the island with an amazing Caldera view, and with Covid, it was really quite and just for us !&lt;/p&gt;

&lt;p&gt;A walking path (2 hours one way) allow to join the two main city Fira to Oia on one of the most beautiful hike with a permanent view on the sea and the caldera.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_1010.jpeg&quot; alt=&quot;Santorini&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7714.jpeg&quot; alt=&quot;Santorini&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We went on a daily catamaran trip, where we had a barbecue, went swimming with the fishes and enjoyed an amazing sunset from the water.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_7944.jpeg&quot; alt=&quot;Santorini&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At the end of this trip, we went to Chania, Crete, to stay a month there while working.&lt;/p&gt;

&lt;h2 id=&quot;life-in-chania-crete&quot;&gt;Life in Chania, Crete&lt;/h2&gt;

&lt;h3 id=&quot;rent&quot;&gt;Rent&lt;/h3&gt;

&lt;p&gt;We found a small house on AirBnb in the center of Chania. We paid 10.95€/night/person.&lt;/p&gt;

&lt;p&gt;The house was ok: well located, with enough space to live, but it was a bit old &amp;amp; wifi was not amazing (10Mb/s).&lt;/p&gt;

&lt;p&gt;Thankfully, there was a brand new coworking space just the street next to it. It was great for days when we needed a great wifi.&lt;/p&gt;

&lt;h3 id=&quot;coworking-space&quot;&gt;Coworking space&lt;/h3&gt;

&lt;p&gt;The coworking space is Workhub Chania, and it was great.&lt;/p&gt;

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

&lt;ul&gt;
  &lt;li&gt;The wifi was fast (optic fibre)&lt;/li&gt;
  &lt;li&gt;There was phone booth for calls&lt;/li&gt;
  &lt;li&gt;You could plug your laptop in Ethernet for even faster speeds&lt;/li&gt;
  &lt;li&gt;Nice &amp;amp; helpful owner&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;The price was a little bit high compared to the cost of life in Greece (67€/week &amp;amp; 265€/month), so I only went there when I needed good internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_3774.jpeg&quot; alt=&quot;Chania crete coworking price&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;food&quot;&gt;Food&lt;/h3&gt;

&lt;p&gt;Chania is a touristic city with lots of restaurant in front of the sea. We tried many of them during our stay, and loved the quality of the food.&lt;/p&gt;

&lt;p&gt;Prices were average for Europe: not expensive but not cheap either.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_4024.jpeg&quot; alt=&quot;Crete food&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;fun--visits&quot;&gt;Fun &amp;amp; visits&lt;/h3&gt;

&lt;p&gt;Crete is full of nice places to visit, and you have beaches &amp;amp; mountain, at the same place.&lt;/p&gt;

&lt;p&gt;We went to Elafonissi (in mid-October), and we enjoyed it like it was the middle of summer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_3859.jpeg&quot; alt=&quot;Elafonissi&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We went hiking in the Samarias Gorge, a 14km hike in the Mountain.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_3904.jpeg&quot; alt=&quot;Samarias Gorge&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;life-in-athens-greece&quot;&gt;Life in Athens, Greece&lt;/h2&gt;

&lt;p&gt;After spending one month in Crete, we decided to move to Athens for 1.5 month before going back to France for Christmas.&lt;/p&gt;

&lt;h3 id=&quot;rent-1&quot;&gt;Rent&lt;/h3&gt;

&lt;p&gt;We found a nice 2 bedrooms suite in a hotel that we shared with another digital nomad couple, the price was just amazing: 200€/month/person = 6,66€ per night in a 4 star all furnished hotel !!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_5356.jpeg&quot; alt=&quot;Rent in Athens&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;coworking-space-1&quot;&gt;Coworking space&lt;/h3&gt;

&lt;p&gt;The hotel Wi-Fi was ok, but not good enough to be 4 working on it all day.&lt;/p&gt;

&lt;p&gt;We found a brand having several coworking spaces in the center of Athens, and we had quite a mixed experience with it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-greece/IMG_5342.jpeg&quot; alt=&quot;Coworking Athens&quot; /&gt;&lt;/p&gt;

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

&lt;ul&gt;
  &lt;li&gt;Value for money was good. The price was 110€/month.&lt;/li&gt;
  &lt;li&gt;One of their location was in the center in a beautiful building. It was a really great place to work.&lt;/li&gt;
  &lt;li&gt;In one of the location, the staff was nice.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;In the closest location to our place, the staff was not friendly and not helpful at all.&lt;/li&gt;
  &lt;li&gt;In the closest location to our place, their was one phone booth for the whole coworking space. Of course it was always busy!&lt;/li&gt;
  &lt;li&gt;They didn’t tell us that the coworking space business model was a subscription, and that we had to cancel the subscription 30 days before leaving. As we were staying like 40 days there, it means that we should have cancelled our subscription right when we arrived. They never told us of course, and they tried to charge us an extra month (despite us being back in France), and were really rude by email.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I just think it’s the kind of shady company that don’t care and don’t respect their users. Their only goal is to make short-term money, without caring about our experience and our need. Why would I pay one more month if I’m not even in the country anymore?&lt;/p&gt;

&lt;h3 id=&quot;food-1&quot;&gt;Food&lt;/h3&gt;

&lt;p&gt;We loved the food and all the local restaurants when we went to Athens in September, but this time everything was closed due to Covid lockdown. We still ordered food online with Wolt (Uber Eats in Greece), and enjoyed cheap Athens prices!&lt;/p&gt;

&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;

&lt;p&gt;We loved our stay in Greece. It’s one of the only country in Europe that’s still hot in fall/beginning of winter, so if you want to escape winter while staying in Europe, it’s definitely a great place to be!&lt;/p&gt;

&lt;p&gt;Food is good, Wine is good, rent is affordable, and there are so many great things to do.&lt;/p&gt;

&lt;p&gt;With coronavirus, we enjoyed empty touristic places. It was great, but sometimes a little sad to see those places empty. I’m sure Greece is even better when bars are full and people are out !&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Living in Barcelona as a Digital Nomad</title>
   <link href="https://pierregillesleymarie.com/blog/2020/09/17/living-in-barcelona-as-a-digital-nomad.html"/>
   <updated>2020-09-17T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2020/09/17/living-in-barcelona-as-a-digital-nomad</id>
   <content type="html">&lt;p&gt;What a strange year, 2020 😅&lt;/p&gt;

&lt;p&gt;We were supposed to go to Bali with my girlfriend on the 15th of April, but coronavirus decided that we had to wait before being able to travel again.&lt;/p&gt;

&lt;p&gt;We had almost everything scheduled for 2020: flight to Bali, 1st visa run to Singapore, 2nd visa run to Kuala Lumpur. We had to cancel everything unfortunately 😪&lt;/p&gt;

&lt;p&gt;Seeing that the situation was not getting any better, we decided to stay in Europe for the rest of the year, and chose Barcelona, Spain as our summer destination.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-barcelona/cover.jpg&quot; alt=&quot;Barcelona&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;rent&quot;&gt;Rent&lt;/h2&gt;

&lt;p&gt;Renting is not expensive in Barcelona, but don’t expect Bali-like prices.&lt;/p&gt;

&lt;p&gt;We picked a great shared appartment in the center of the city, near the port. We found the appartment on Airbnb, paid the first month online, and then negociated via What’s App with the landlord to pay him directly without Airbnb’s fees.&lt;/p&gt;

&lt;p&gt;This is how the appartment was looking like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-barcelona/airbnb.jpg&quot; alt=&quot;Airbnb Barcelona&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this appartment, we had an amazing optic fiber with 450Mbps down &amp;amp; 590Mbps up. Perfect for working.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-barcelona/airbnb-speed-test.jpg&quot; alt=&quot;Airbnb Barcelona&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The living room was spacious and well furnished: Washing machine, dishwasher, oven, 4K TV with Netflix/YouTube, and even a gigantic &lt;strong&gt;ELECTRIC SOFA&lt;/strong&gt;. Yes, you read right, you could move your position in the sofa with the touch of a button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-barcelona/airbnb-2.jpg&quot; alt=&quot;Airbnb Barcelona&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We paid 21€/night (630€/month) for 2 peoples, which is &lt;strong&gt;315€/month/person&lt;/strong&gt;. Not that bad!&lt;/p&gt;

&lt;h2 id=&quot;food&quot;&gt;Food&lt;/h2&gt;

&lt;p&gt;Tapas, Sangria, pintxos: food is great in Spain 🇪🇸&lt;/p&gt;

&lt;p&gt;We mostly ate at home, but were going out a few times per week to have a drink &amp;amp; enjoy the spanish food.&lt;/p&gt;

&lt;p&gt;A few prices:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Pinxtos are small snacks you can eat in bars. It’s a piece of bread with cheese/vegetables/ham on top, spiked with a toothpick. You usually pay for the number of toothpick you have in your plate at the end of the meal, and it costs 1€/pintxos for the basic ones, and 1€50 for more complex pintxos. Best Pinxtos are in &lt;a href=&quot;https://www.google.com/maps/place/Carrer+de+Blai,+08004+Barcelona&quot;&gt;Carrer de Blai&lt;/a&gt;, it’s a street full of Pinxtos bar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-barcelona/pinxtos.jpg&quot; alt=&quot;Pinxtos&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A glass of Sangria (0.5L) costs 2.90€ in non-touristic bars.&lt;/li&gt;
  &lt;li&gt;A pint of beer cost between 2 and 3€.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most bars around the beach are just touristic places owned by foreigners. Prefer local spanish bars, where the menu is barely translated: you’ll get authentic food at a cheaper price.&lt;/p&gt;

&lt;h2 id=&quot;fun&quot;&gt;Fun&lt;/h2&gt;

&lt;h3 id=&quot;beach-life&quot;&gt;Beach Life&lt;/h3&gt;

&lt;p&gt;Barcelona is one of the rare “big modern city” in Europe which has a beach. It’s not the best beach in the world, but &lt;strong&gt;it’s a beach you can walk to after your day of work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every day, it was our afterwork ritual: chilling at the beach 🏖️&lt;/p&gt;

&lt;p&gt;If you want a better beach, you can take a train to Castelldefels, a small city close to Barcelona with a beautiful, fine sand beach. The train is cheap (2€80) and it’s quite fast to go there. Great for a day at the beach!&lt;/p&gt;

&lt;h3 id=&quot;visiting&quot;&gt;Visiting&lt;/h3&gt;

&lt;p&gt;Gaudi’s work is everywhere in Barcelona.&lt;/p&gt;

&lt;p&gt;The famous Sagrada Familia is probably one of the most beautiful church you’ll see in your life. It’s big, and so colorful inside. It’s a bit pricey to visit (30€/person), but the architecture inside is unique.&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/CFKeZGiim0v/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; data-instgrm-version=&quot;12&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/CFKeZGiim0v/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; Voir cette publication sur Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/CFKeZGiim0v/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Last days in Barcelona 🇪🇸 Next: Greece 🇬🇷&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;Une publication partagée par &lt;a href=&quot;https://www.instagram.com/pierregilles.leymarie/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Pierre-Gilles Leymarie&lt;/a&gt; (@pierregilles.leymarie) le &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2020-09-15T16:20:25+00:00&quot;&gt;15 Sept. 2020 à 9 :20 PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;The Park Güell is a garden overlooking the city, 40 minutes from the center of Barcelona. It’s where most Instagram pictures of Barcelona are taken so if you’re here for the gram go for it 😁&lt;/p&gt;

&lt;h3 id=&quot;port-aventura&quot;&gt;Port Aventura&lt;/h3&gt;

&lt;p&gt;If you are into amusement parks, Port Aventura is one of the biggest in Europe.&lt;/p&gt;

&lt;p&gt;You can go there for the weekend, and enjoy some of the biggest rollercoaster in Europe/World.&lt;/p&gt;

&lt;h2 id=&quot;budget&quot;&gt;Budget&lt;/h2&gt;

&lt;p&gt;This is how much I spent in August in Barcelona:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2020-barcelona/budget.jpg&quot; alt=&quot;Budget&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Most of it was for the rent (315€), then restaurants, groceries &amp;amp; transports.&lt;/p&gt;

&lt;h2 id=&quot;to-conclude&quot;&gt;To conclude&lt;/h2&gt;

&lt;p&gt;We had an amazing time this summer living in Barcelona. It’s a city which offer the active life of a big city with the chill spirit of a beach city. A great balance between fun and work 🙂&lt;/p&gt;

&lt;p&gt;We are now going to Greece 🇬🇷&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Benchmarking Vultr High Frequency Compute Instances</title>
   <link href="https://pierregillesleymarie.com/blog/2020/04/06/benchmarking-vultr-high-frequency-compute-instances.html"/>
   <updated>2020-04-06T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2020/04/06/benchmarking-vultr-high-frequency-compute-instances</id>
   <content type="html">&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;As you know, I’m working on &lt;a href=&quot;https://gladysassistant.com&quot;&gt;Gladys Assistant&lt;/a&gt;, an open-source home automation software.&lt;/p&gt;

&lt;p&gt;To control their home when they are away, Gladys users use the &lt;a href=&quot;https://gladysassistant.com/pricing&quot;&gt;Gladys Gateway&lt;/a&gt;, an online proxy service I run, which allows them to access their local instance from anywhere in the world, all that &lt;strong&gt;end-to-end encrypted&lt;/strong&gt; to respect their privacy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/gladys-gateway-schema.png&quot; alt=&quot;gladys Gateway Schema&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;current-hosting&quot;&gt;Current hosting&lt;/h2&gt;

&lt;p&gt;Currently, the Gladys Gateway is hosted on 2 DigitalOcean droplets in Frankfurt datacenter.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;First droplet is running the PostgreSQL database.&lt;/li&gt;
  &lt;li&gt;Second droplet is running 4 Docker containers:
    &lt;ol&gt;
      &lt;li&gt;Redis&lt;/li&gt;
      &lt;li&gt;Gladys Gateway server&lt;/li&gt;
      &lt;li&gt;Nginx-proxy for SSL&lt;/li&gt;
      &lt;li&gt;Let’s Encrypt companion to renew SSL certificate automatically&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;As we have more and more users using the Gladys Gateway + more and more users having cameras in Gladys, the usage is growing and our infrastructure needs to evolve.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/gladys-gateway-usage-running-high.png&quot; alt=&quot;gladys-gateway-usage-running-high&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;

&lt;p&gt;The server is mainly a websocket proxy, so what we want is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Located in France&lt;/strong&gt;: For minimul latency, close to my users. I’ll had more locations if I have more users in other countries in the future, but that’s not the case right now. Frankfurt was already close, but we want closer.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lots of bandwidth&lt;/strong&gt;: As we have camera image going through the gateway, we need &amp;gt; 1Gps port.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fast CPU&lt;/strong&gt;: To handle lots of websockets messages in a short amount of time&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;More RAMs&lt;/strong&gt;: Websockets sessions are stored in Redis, in RAM for performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I looked at many providers :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;DigitalOcean&lt;/strong&gt;: Their prices are great, performances are reliable, and the UI is just amazing. But they are not available in France (yet?) sadly.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scaleway&lt;/strong&gt; : General purpose instances looked promising, but were expensive (Starting at 42$/month + VAT) + a quick twitter search showed me that Scaleway had reliability issue, not well handled… They don’t seem to care about uptime, which is very important for critical application like this. I want guarantees on SLA + automatic refund with a multiplier when the server is down.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;OVH&lt;/strong&gt;: I read only bad feedbacks about them. Reliability &amp;amp; support is not their thing…&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Vultr&lt;/strong&gt;: A great DigitalOcean-like provider, with a datacenter in France 😍 I saw that they launched a High Frequency compute offer recently, and this is what I’m going to benchmark here!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;vultr-high-frequency-compute-benchmark&quot;&gt;Vultr High Frequency Compute Benchmark&lt;/h2&gt;

&lt;p&gt;This is what they offer on their website (&lt;a href=&quot;https://www.vultr.com/products/high-frequency-compute/?ref=8527763-6G&quot;&gt;See Vultr High Compute Instances&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/vultr-high-frequency-offer.png&quot; alt=&quot;vultr-high-frequency-offer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/vultr-fast-cpu.png&quot; alt=&quot;vultr-fast-cpu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Exactly what we need for a high-usage websocket server!&lt;/p&gt;

&lt;h3 id=&quot;the-server&quot;&gt;The server&lt;/h3&gt;

&lt;p&gt;The server is a 2 vCPU + 4 Gb RAM Vultr High Compute Instance.&lt;/p&gt;

&lt;p&gt;It started in 30 seconds, and I destroyed the server in 1 click after the tests. Running this server during tests cost me $0.04.&lt;/p&gt;

&lt;p&gt;A few generic informations about the server :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/generic-bench.png&quot; alt=&quot;generic-bench&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;network-speed&quot;&gt;Network speed&lt;/h3&gt;

&lt;p&gt;A little speedtest.net:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/network-speed.png&quot; alt=&quot;network-speed&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Download&lt;/strong&gt;: 3.5 Gb/s&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Upload&lt;/strong&gt;: 4.3 Gb/s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not bad !! 🚀&lt;/p&gt;

&lt;h3 id=&quot;ping&quot;&gt;Ping&lt;/h3&gt;

&lt;p&gt;A ping from my place near Paris took 5ms on average:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/ping.png&quot; alt=&quot;ping&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In comparaison, a DigitalOcean instance in Franfkurt gives me a 20ms ping:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/ping-fra.png&quot; alt=&quot;ping-fra&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is not that different if it was just a web server, but for real-time home automation, this is really better ! 🙂&lt;/p&gt;

&lt;h3 id=&quot;disk-write-performance&quot;&gt;Disk write performance&lt;/h3&gt;

&lt;p&gt;A simple dd of a 1 Gb file gives us some pretty decent performance :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/write-perf.png&quot; alt=&quot;write-perf&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;disk-read-performance&quot;&gt;Disk read performance&lt;/h3&gt;

&lt;p&gt;Reading the same file we just wrote gives us a 1.6 GB/s speed. Not bad !&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/benchmarking-vultr-high-frequency-compute-instances/read-perf.png&quot; alt=&quot;read-perf&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;cpu&quot;&gt;CPU&lt;/h3&gt;

&lt;p&gt;I didn’t run any CPU tests, because there are plenty of them online (On &lt;a href=&quot;https://www.vpsbenchmarks.com/compare/vultr&quot;&gt;VPSBenchmarks&lt;/a&gt; for example). Feedbacks on the High Frequency Compute are really great.&lt;/p&gt;

&lt;h2 id=&quot;to-conclude&quot;&gt;To Conclude&lt;/h2&gt;

&lt;p&gt;I already use Vultr to host Gladys Assistant community (with an amazing reliability over years), and I have to say that once again, they proved that they can provide performant cloud instances.&lt;/p&gt;

&lt;p&gt;I would love your feedback on this, but I think I’ll migrate Gladys Gateway servers to Vultr soon.&lt;/p&gt;

&lt;p&gt;I want to improve latency and speed for my users, and I’m convinced that this change will help us in this way !&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Philippines: crystal clear water, turtles and tons of rice</title>
   <link href="https://pierregillesleymarie.com/blog/2019/04/12/philippines-crystal-clear-water-turtles-and-rice.html"/>
   <updated>2019-04-12T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2019/04/12/philippines-crystal-clear-water-turtles-and-rice</id>
   <content type="html">&lt;p&gt;After spending 2 months in Koh Lanta, I decided to move to the Philippines for 1 month to discover this amazing country.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/coconut-el-nido.jpg&quot; alt=&quot;Coconut El Nido&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Philippines is an island country composed of more than 7 000 islands. It’s is known for its incredible crystal clear water, blue lagoon, and white sand beaches.&lt;/p&gt;

&lt;p&gt;As a digital nomad, it’s not always the most recommended destination because the internet speed can vary a lot due to the geography of the country.&lt;/p&gt;

&lt;p&gt;I’ll explain in this article how I organized myself to have a good balance between work and staying in paradise location.&lt;/p&gt;

&lt;p&gt;Here is the tour I did in the Philippines. I spent most of my time in Cebu City (I’ll explain why just later), and just did 2 weekends on paradise places.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/philippines-map.jpg&quot; alt=&quot;My tour in the Philippines&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-little-word-on-my-digital-nomad-philosophy&quot;&gt;A little word on my digital nomad philosophy&lt;/h2&gt;

&lt;p&gt;Being digital nomad is &lt;strong&gt;totally different&lt;/strong&gt; from being a traveler.&lt;/p&gt;

&lt;p&gt;I’m not in holidays. I’m not looking for moving every 2 days, eating at the airport and waking up at 3 AM to catch a boat. (I do like that, but only when I’m in holidays with my friends, it’s not sustainable on a daily basis).&lt;/p&gt;

&lt;p&gt;When I’m in my daily life, I’m looking to find as much stability as possible so I can build a routine and be productive at work.&lt;/p&gt;

&lt;p&gt;To achieve that, I stay in general between 2 and 4 months in a country.&lt;/p&gt;

&lt;p&gt;This time was a little different because I planned a long time ago to see a friend in India in April, so I decided to stay only 1 month in the Philippines.&lt;/p&gt;

&lt;p&gt;Still, in one month I managed to find my routine.&lt;/p&gt;

&lt;h2 id=&quot;first-a-few-days-in-manila&quot;&gt;First, a few days in Manila&lt;/h2&gt;

&lt;p&gt;I landed first in Manila and worked 2 days from there. Manila was quite crowded, but I managed to be productive.&lt;/p&gt;

&lt;p&gt;I first worked in an internet cafe full of people playing fortnite (interesting atmosphere 🤔). This was super cheap (50 PHP/0.85€ per hour of internet), but not as professional as I’m used to. Internet speed was really fast.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/internet-cafe-manila.jpg&quot; alt=&quot;Internet cafe Manila&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The second day I went to real coworking space, Acceler8. This time the atmosphere was better but prices were a little higher (660 PHP/11.26€ for the day). Internet speed was fast but not as fast as in the gamer cafe. I guess people playing fortnite have higher latency standard than pro 😂&lt;/p&gt;

&lt;h2 id=&quot;a-long-weekend-in-beautiful-palawan&quot;&gt;A long weekend in beautiful Palawan&lt;/h2&gt;

&lt;p&gt;Then, I moved to Palawan for a long weekend. I took a 30€ flight to Puerto Princesa (thanks Cebu Pacific), then a 5 hour bus to El Nido.&lt;/p&gt;

&lt;p&gt;There I did the usual snorkelling tour, spent time on the beach and went kayaking.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/el-nido.jpg&quot; alt=&quot;El Nido&quot; /&gt;&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/Bu_K6UXlqDZ/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; data-instgrm-version=&quot;12&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bu_K6UXlqDZ/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bu_K6UXlqDZ/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;A perfect kayak trip wouldn’t be perfect without a bottle of red wine 🍷 #merlot #philippines #palawan #elnido #islands #weekend&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A post shared by &lt;a href=&quot;https://www.instagram.com/pierregilles.leymarie/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Pierre-Gilles Leymarie&lt;/a&gt; (@pierregilles.leymarie) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2019-03-14T11:27:52+00:00&quot;&gt;Mar 14, 2019 at 4:27am PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Finally, I took a flight to Cebu City (for 30€ as well), and settled there for the following 3 weeks.&lt;/p&gt;

&lt;h2 id=&quot;living-in-cebu-city&quot;&gt;Living in Cebu City&lt;/h2&gt;

&lt;p&gt;Cebu city is like a smaller Manila. Don’t expect to live in paradise, it’s the stereotype of the big city: lots of cars everywhere, really crowded, western fast-food everywhere, and lots of poverty. Still, the city has everything for you to be productive at work.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/cebu-city.jpg&quot; alt=&quot;Cebu City&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first days, I just slept in a hostel (6€/night) and worked in a great coworking space which was amazingly cheap.&lt;/p&gt;

&lt;p&gt;The space is named &lt;a href=&quot;https://www.google.com/maps/place/Workplace+Cafe/@10.3055823,123.89556,17z/data=!3m1!4b1!4m5!3m4!1s0x33a9995a9694f34d:0xb1c27bfa0541456f!8m2!3d10.305577!4d123.8977487&quot;&gt;Workplace cafe&lt;/a&gt;, cost 280 PHP per day (4,78€/day), including a welcome drink, optic fiber, working chair and stays open until 6 AM.&lt;/p&gt;

&lt;p&gt;I managed to find a nice apartment in a tower on Airbnb.&lt;/p&gt;

&lt;p&gt;Big bedroom, kitchen, optic fiber, couch, TV, swimming pool and gym.&lt;/p&gt;

&lt;p&gt;It was 18€/night, which is more expensive that what I’m used to in Asia (my goal is max 11€/night).&lt;/p&gt;

&lt;p&gt;But here, considering the fact that it was my apartment + my working place + my gym, it removed costs that I usually pay separately, so 18€/night was in the budget.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/appartment.jpg&quot; alt=&quot;Appartment in Cebu City&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I had a great time in this apartment, but I experienced something that I didn’t experience before: loneliness.&lt;/p&gt;

&lt;p&gt;In Cebu City, I didn’t find a nomad community like I experienced in Bali or Koh Lanta. No beach &amp;amp; beers after work, no sunset with the friends from the coworking space. The first coworking space I found was just full of Filipinos student, no nomads.&lt;/p&gt;

&lt;p&gt;Maybe that’s just the beginning of the nomad scene in the Philippines. I know some people have been working there for years, but it’s not mainstream yet. Giving the potential of the Philippines, I’m sure it’s going to change in the coming years.&lt;/p&gt;

&lt;h2 id=&quot;swimming-with-the-turtles-in-bohol&quot;&gt;Swimming with the turtles in Bohol&lt;/h2&gt;

&lt;p&gt;For my last weekend in the Philippines, I went to Bohol. It took 2 hours of boat from Cebu City + 30 min of taxi.&lt;/p&gt;

&lt;p&gt;I experienced the same level of paradise as El Nido.&lt;/p&gt;

&lt;p&gt;I went on a snorkelling tour to see turtles and drove on a scooter to the famous Chocolate Hills.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/chocolate-hills.jpg&quot; alt=&quot;Chocolat Hills&quot; /&gt;&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/BvtGiEwF3q_/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; data-instgrm-version=&quot;12&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/BvtGiEwF3q_/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/BvtGiEwF3q_/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Amazing time swimming with the turtles 🐢 this weekend on Bohol Island, Philippines 🌴&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A post shared by &lt;a href=&quot;https://www.instagram.com/pierregilles.leymarie/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Pierre-Gilles Leymarie&lt;/a&gt; (@pierregilles.leymarie) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2019-04-01T07:34:40+00:00&quot;&gt;Apr 1, 2019 at 12:34am PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;food&quot;&gt;Food&lt;/h2&gt;

&lt;p&gt;I’m quite balanced on food in the Philippines.&lt;/p&gt;

&lt;p&gt;On the one hand, it’s the cheapest food I’ve seen in my entire life.&lt;/p&gt;

&lt;p&gt;In Cebu City, a muffin in a bakery is between 2 pesos and 5 pesos (0.03-0.08€).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/puto-plain.jpg&quot; alt=&quot;Puto Plain&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/bakery.jpg&quot; alt=&quot;Bakery Philippines&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And it’s really good! (Don’t eat too much of them. It’s really sweet 😁)&lt;/p&gt;

&lt;p&gt;Eating rice + meat in the street is 40-50 PHP (0,68-0.85€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/rice-meat.jpg&quot; alt=&quot;Street Food Philippines&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A bao at 7/11 in 29 PHP (0,49€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/bao.jpg&quot; alt=&quot;Bao&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A banana is 10 pesos (0.17€)&lt;/p&gt;

&lt;p&gt;The usual Filipino breakfast for 50-100 PHP (0.85€-1,7€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/filipino-breakfast.jpg&quot; alt=&quot;Filipino Breakfast&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But on the other hand, it’s always the same thing.&lt;/p&gt;

&lt;p&gt;White rice + meat, 3 times a day. Where are the vegetables ?! The greens? 😅&lt;/p&gt;

&lt;p&gt;I have to say I struggled to eat healthily in the Philippines. I cooked vegetables myself when I was in my apartment, but otherwise it was almost impossible to get healthy food outside. Everything is fried, and they put tons of sugar in everything, way more than in Thailand.&lt;/p&gt;

&lt;p&gt;I even finished by eating in an expensive Thai restaurant in Panglao, as I was dying to eat a salad and vegetables 😅&lt;/p&gt;

&lt;p&gt;You could say that eating rice is the basis in Asia, but eating just plain white rice without vegetables is not ^^ All countries I’ve been to before have great vegetables on the side of the rice, and in Thailand you have all their great sauce (green curry, coconut milk soup).&lt;/p&gt;

&lt;h2 id=&quot;budget&quot;&gt;Budget&lt;/h2&gt;

&lt;p&gt;The budget side was really easy.&lt;/p&gt;

&lt;p&gt;Even when you take into account fancy activities on weekends, Philippines is a really cheap place, cheaper than Thailand.&lt;/p&gt;

&lt;p&gt;This is my budget for this month:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-philippines/budget.jpg&quot; alt=&quot;Philippines Budget&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;to-conclude&quot;&gt;To conclude&lt;/h2&gt;

&lt;p&gt;I have to say that I absolutely loved my stay in the Philippines.&lt;/p&gt;

&lt;p&gt;Landscapes were stunning.&lt;/p&gt;

&lt;p&gt;I’ve seen the most beautiful beaches I’ve seen in my entire life.&lt;/p&gt;

&lt;p&gt;The water is crystal clear on the islands.&lt;/p&gt;

&lt;p&gt;I still managed to work hard, and get proper internet.&lt;/p&gt;

&lt;p&gt;The only downside of the Philippines is that you can’t have both paradise places and internet at the same time right now. In Koh Lanta, I could work hard during the day and chill in the evening on the beach with a coconut. Same in Bali.&lt;/p&gt;

&lt;p&gt;I didn’t manage to have this lifestyle in the Philippines.&lt;/p&gt;

&lt;p&gt;But this is just the beginning of the Digital Nomad era in the Philippines, and I’m convinced that in the coming years it’s going to become easier and easier to get the best of both worlds: working close to the beach!&lt;/p&gt;

&lt;p&gt;I’m now in India for a month. I’ll write about my experience there in one month.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Living 2 months in Koh Lanta</title>
   <link href="https://pierregillesleymarie.com/blog/2019/03/02/living-2-months-in-koh-lanta.html"/>
   <updated>2019-03-02T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2019/03/02/living-2-months-in-koh-lanta</id>
   <content type="html">&lt;p&gt;After &lt;a href=&quot;/blog/2019/02/10/10-days-in-bangkok.html&quot;&gt;10 days in Bangkok&lt;/a&gt;, I spent 2 months living on Koh Lanta Island, in Thailand.&lt;/p&gt;

&lt;p&gt;In this blog post I will explain everything: What I loved, what I didn’t like, the cost of my stay and what I visited on this Island.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/jump-koh-lanta.jpg&quot; alt=&quot;Wonderful Resort&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;rent&quot;&gt;Rent&lt;/h2&gt;

&lt;p&gt;Compared to Bali, it was a little harder to find a very cheap accommodation. My goal is always to rent something under 330€/month (max 11€/night). While in Bali you can get something amazing starting at 200€/month with a swimming pool, proximity from the beach, and a great double bedroom, here it’s a little harder.&lt;/p&gt;

&lt;p&gt;But I succeeded! 😄&lt;/p&gt;

&lt;p&gt;On my first day on the Island, I walked on the side of the road and asked every locals, every resort, basically everyone: Do you have a room to rent and if yes, what’s the price?&lt;/p&gt;

&lt;p&gt;At the end, I found an amazing resort in front of the beach, renting houses for 12k Baht per month (that’s 330€!).&lt;/p&gt;

&lt;p&gt;Welcome to Wonderful Resort 👋🏨&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/wonderful-resort.jpg&quot; alt=&quot;Wonderful Resort&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Their house are super clean, they got AC, a fridge and your room get cleaned once a week + they give you fresh towels. Not that bad for this price!&lt;/p&gt;

&lt;p&gt;And the best: It’s &lt;strong&gt;perfectly located&lt;/strong&gt;. 2 minutes walk from Kohub, the coworking space in Koh Lanta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus point&lt;/strong&gt;: They have optic fiber, like 150Mb/s, just perfect when you want to watch a movie at night.&lt;/p&gt;

&lt;h2 id=&quot;food&quot;&gt;Food&lt;/h2&gt;

&lt;p&gt;As I was saying in my previous article, food in Thailand is just amazing.&lt;/p&gt;

&lt;p&gt;From the famous Pad Thai, to green/massamam curry, noodle soup, chicken in coconut milk: it’s always good 😋&lt;/p&gt;

&lt;p&gt;Here are some typical meals I was having in Koh Lanta.&lt;/p&gt;

&lt;p&gt;Chicken Vegetables, 70 baht (1,93€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/chicken-vegetables.jpg&quot; alt=&quot;Chicken Vegetables&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Coconut soup with chicken and rice, 120 baht (3,22€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/coconut-soup.jpg&quot; alt=&quot;Coconut Soup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sweet and Sour with rice, 70 baht.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/sweet-and-sour.jpg&quot; alt=&quot;Sweet and Sour&quot; /&gt;&lt;/p&gt;

&lt;p&gt;About the price, it’s a little bit more expensive than Indonesia, but it’s still quite cheap.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A Pad Thai is usually 60-80 baht (1,66-2,21€).&lt;/li&gt;
  &lt;li&gt;Noodle soup is usually 60 baht.&lt;/li&gt;
  &lt;li&gt;A more complex meal can easily be 120-150 baht (3,32-4,15€).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-best-pad-thai-in-koh-lanta&quot;&gt;The best Pad Thai in Koh Lanta&lt;/h3&gt;

&lt;p&gt;If you want to have the best Pad Thai of your life, go to Mr Pad Thai (just type &lt;a href=&quot;https://www.google.com/maps/place/Aleena+Minimart/@7.6173396,99.0290629,17z/data=!4m12!1m6!3m5!1s0x304e03430a8af1e1:0xc90c8c5584212630!2sLanta+Panda+bungalows+%26+restaurant!8m2!3d7.6173343!4d99.0312516!3m4!1s0x0:0x5034316c4ea86e44!8m2!3d7.616879!4d99.0308375&quot;&gt;Aleena minimart&lt;/a&gt; on Google Maps, it’s in front of it.&lt;/p&gt;

&lt;p&gt;The owner is really nice, doing everything by himself, and portion are really big!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/mr-pad-thai.jpg&quot; alt=&quot;Mr Pad Thai&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A must do!&lt;/p&gt;

&lt;h3 id=&quot;the-best-chicken-massamam-curry-in-koh-lanta&quot;&gt;The best Chicken Massamam Curry in Koh Lanta&lt;/h3&gt;

&lt;p&gt;You can find it at &lt;a href=&quot;https://www.google.com/maps/place/Lanta+Panda+bungalows+%26+restaurant/@7.6173396,99.0290629,17z/data=!3m1!4b1!4m5!3m4!1s0x304e03430a8af1e1:0xc90c8c5584212630!8m2!3d7.6173343!4d99.0312516&quot;&gt;Lanta Panda restaurant&lt;/a&gt;, quite near Mr Pad Thai.&lt;/p&gt;

&lt;p&gt;I found this place only at the end of my stay, but it’s definitely the best Massamam Curry I ate in Thailand.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/massamam-curry.jpg&quot; alt=&quot;Chicken massamam curry&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In general a chicken massamam curry is easily 120 baht, but here it’s only 60 baht! Really good address.&lt;/p&gt;

&lt;h2 id=&quot;working-in-koh-lanta&quot;&gt;Working in Koh Lanta&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://kohub.org/&quot;&gt;Kohub&lt;/a&gt; is a great coworking space. You have plenty of space and can work inside with AC or outside to enjoy the open air/jungle view.&lt;/p&gt;

&lt;p&gt;Internet speed is like in Thailand in general: really fast.&lt;/p&gt;

&lt;p&gt;The community is welcoming and from all other the world.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Let’s start a new week of work on Gladys 4 at &lt;a href=&quot;https://twitter.com/kohuborg?ref_src=twsrc%5Etfw&quot;&gt;@kohuborg&lt;/a&gt; 🏝 &lt;a href=&quot;https://t.co/c1vAzl6nOy&quot;&gt;pic.twitter.com/c1vAzl6nOy&lt;/a&gt;&lt;/p&gt;&amp;mdash; Pierre-Gilles Leymarie ✈️ (@pierregillesl) &lt;a href=&quot;https://twitter.com/pierregillesl/status/1094788374982844416?ref_src=twsrc%5Etfw&quot;&gt;February 11, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;tourism&quot;&gt;Tourism&lt;/h2&gt;

&lt;p&gt;Koh Lanta is a small island. You can go from the top to the bottom in 50 minutes by car.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/map-island.jpg&quot; alt=&quot;Koh Lanta Map&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can still do plenty of things during your weekends!&lt;/p&gt;

&lt;p&gt;You can go on a boat tour and visit several islands, go snorkeling.&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/Bt-r_s-lLij/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; data-instgrm-version=&quot;12&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bt-r_s-lLij/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bt-r_s-lLij/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Exploring the waters of Koh Lanta Island 🌴Show me the fishes!! 🐠 #kohlanta #thailand #digitalnomad&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A post shared by &lt;a href=&quot;https://www.instagram.com/pierregilles.leymarie/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Pierre-Gilles Leymarie&lt;/a&gt; (@pierregilles.leymarie) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2019-02-17T10:26:20+00:00&quot;&gt;Feb 17, 2019 at 2:26am PST&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;There is a national park in the south of the island, it’s not really worth it in my opinion.&lt;/p&gt;

&lt;p&gt;And you can find a great waterfall in the middle of the jungle, and this one is a great tour.&lt;/p&gt;

&lt;p&gt;Finally, you can just chill by the pool with a drink and a good book 🍹&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/swimming-pool.jpg&quot; alt=&quot;Chill by the pool&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;visa&quot;&gt;Visa&lt;/h2&gt;

&lt;p&gt;To stay in Thailand I got the tourist 30 days visa when entering the country, and I renewed this visa in Phuket immigration office for 52€.&lt;/p&gt;

&lt;h2 id=&quot;my-budget&quot;&gt;My budget&lt;/h2&gt;

&lt;p&gt;This is not exactly my two months in Koh Lanta in this budget because I was in Lanta from the middle of January to the beginning of March, but you get a general idea of my spendings in Thailand.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-koh-lanta/budget-koh-lanta.jpg&quot; alt=&quot;Budget Koh Lanta&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In January you can notice my 300€ flight from Paris to Bangkok, and my 33€ flight from Bangkok to Phuket.&lt;/p&gt;

&lt;p&gt;This explains why this month is slightly more expensive.&lt;/p&gt;

&lt;p&gt;My rent in Koh Lanta was just for the end of January + February + beginning of March. At the beginning of January I was sleeping in an hostel in Bangkok (see the “hotel” row in the budget).&lt;/p&gt;

&lt;p&gt;In general I try to keep my spending around 700€ spent/month.&lt;/p&gt;

&lt;h2 id=&quot;my-feedback&quot;&gt;My feedback&lt;/h2&gt;

&lt;p&gt;My feedback about Koh Lanta is really positive.&lt;/p&gt;

&lt;p&gt;You can easily work, have fun and stay on a budget.&lt;/p&gt;

&lt;p&gt;I had an &lt;strong&gt;amazing time&lt;/strong&gt; here!&lt;/p&gt;

&lt;p&gt;I still have two negative feedbacks about Koh Lanta:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The road was in construction while I was there, and it was really painful to walk/drive. Some days were really a nightmare in terms of traffic, dust and construction noise. But this is just a temporary issue, nothing related to Koh Lanta :)&lt;/li&gt;
  &lt;li&gt;A few people got dengue fever when I was there, and it became a recurrent topic with my coworking space colleagues. We were all scared of getting dengue 😅 But it’s like everywhere in tropical countries, mosquittos are everywhere. You just have to use repellent and be cautious.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;whats-next-for-me&quot;&gt;What’s next for me&lt;/h2&gt;

&lt;p&gt;I’m flying next week to the Philippines 🇵🇭&lt;/p&gt;

&lt;p&gt;I can’t wait to discover this amazing country, and will share all my trip on this blog.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 days in Bangkok</title>
   <link href="https://pierregillesleymarie.com/blog/2019/02/10/10-days-in-bangkok.html"/>
   <updated>2019-02-10T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2019/02/10/10-days-in-bangkok</id>
   <content type="html">&lt;p&gt;After &lt;a href=&quot;/blog/2018/10/07/my-first-month-as-a-digital-nomad-in-bali.html&quot;&gt;4 months living in Canggu&lt;/a&gt;, Bali, I decided to move to Thailand to discover this amazing country.&lt;/p&gt;

&lt;p&gt;I landed in Bangkok and started my Thai life by living 10 days there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/bangkok-city-3.jpg&quot; alt=&quot;Lumphini Park&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;rent&quot;&gt;Rent&lt;/h2&gt;

&lt;p&gt;For short term stay, staying in a hotel/Airbnb in the center of Bangkok was too expensive for me so I just picked a nice hostel.&lt;/p&gt;

&lt;p&gt;I chose &lt;a href=&quot;https://www.booking.com/hotel/th/vivit-hostel-bangkok.html&quot;&gt;Vivit Hostel&lt;/a&gt; located near the Grand Palace.&lt;/p&gt;

&lt;p&gt;I paid 3 134 baht for 8 nights, that’s 88€ or 11€/night.&lt;/p&gt;

&lt;p&gt;The hostel was great, and I had an included breakfast which was really good (juices, bread with butter and jam, coffee/tea, bananas)&lt;/p&gt;

&lt;p&gt;Vivit hostel rooms are for 4 people, and beds are clearly delimited with proper curtains, so you feel you’re in your own space. There is AC so you don’t sweat the whole night because of the outside temperature/humidity.&lt;/p&gt;

&lt;p&gt;There is a lobby with medium quality internet if you want to check things on your laptop, and machines for your laundry.&lt;/p&gt;

&lt;p&gt;Wi-Fi is ok for basic tasks but not enough for all-day work.&lt;/p&gt;

&lt;p&gt;I met very nice people in this hostel, and never had any issues with them in the room, I was able to sleep when I wanted.&lt;/p&gt;

&lt;p&gt;The only downside of this hostel is that it’s a little far from any BTS station (Bangkok Skytrain), so if you want to get somewhere you have to either walk 40 minutes to the first station, or get a Grab (local Uber).&lt;/p&gt;

&lt;h2 id=&quot;coworking-space&quot;&gt;Coworking space&lt;/h2&gt;

&lt;p&gt;As I’m working on &lt;a href=&quot;https://gladysassistant.com&quot;&gt;Gladys Assistant&lt;/a&gt; 3 days/week and working 2 days/week as a Software Engineer contractor, I always need to find coworking spaces with proper internet.&lt;/p&gt;

&lt;p&gt;I found online that Hubba Coworking space is well known in Bangkok, and tried 2 of their offices: Hubba Siam Discovery and Hubba Silom.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Current view from Hubba coworking space in Bangkok 👌 &lt;a href=&quot;https://t.co/ztkWd6q0kL&quot;&gt;pic.twitter.com/ztkWd6q0kL&lt;/a&gt;&lt;/p&gt;&amp;mdash; Pierre-Gilles Leymarie ✈️ (@pierregillesl) &lt;a href=&quot;https://twitter.com/pierregillesl/status/1085738290882342912?ref_src=twsrc%5Etfw&quot;&gt;January 17, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;Hubba Siam Discovery was 390 baht/day (11€/day) and Hubba Silom was 290 baht/day (8€/day).&lt;/p&gt;

&lt;p&gt;Hubba Siam Discovery was more recent with really good working chair, whereas Hubba Silom had more basic furnitures.&lt;/p&gt;

&lt;p&gt;Still, both were great and had amazing internet speed, so I spent most days at Hubba Silom to get the cheapest price.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/internet-speed.png&quot; alt=&quot;Hubba Bangkok internet speed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In terms of community, I was amazed to see that both coworking space were… empty!&lt;/p&gt;

&lt;p&gt;I was literally the only one working here! It was great to focus, but I felt lonely in those big spaces and I wouldn’t spend a full month there working alone.&lt;/p&gt;

&lt;h2 id=&quot;food&quot;&gt;Food&lt;/h2&gt;

&lt;p&gt;People told me that food in Thailand is amazing, and I have to say that it’s true.&lt;/p&gt;

&lt;p&gt;I was eating mostly street food, like the famous Pad Thaï for 50 baht (1,40€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/pad-thai.jpg&quot; alt=&quot;Pad Thai Bangkok&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Or a duck soup for 60 baht (1,68€)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/duck-soup.jpg&quot; alt=&quot;Duck soup Bangkok&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Or chicken vegetables with Rice for 60 baht:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/vegetables-chicken.jpg&quot; alt=&quot;Chicken Vegetables&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My rules when traveling is to eat where locals are eating.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;I try to be in restaurants where I’m the only westerner eating&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s the only way to get real local food and real local prices!&lt;/p&gt;

&lt;p&gt;To compare with my previous experience in Bali, I would say that food is a little more expensive in Thailand.&lt;/p&gt;

&lt;p&gt;In Bali I could easily eat under 1€, but in Thailand it’s just impossible.&lt;/p&gt;

&lt;p&gt;You can say “sure, but still it’s super cheap compared to European restaurant”.&lt;/p&gt;

&lt;p&gt;It’s true, but the key difference here is that you don’t eat 3 times a day at the restaurant in Europe, you just go to the restaurant once in a while.&lt;/p&gt;

&lt;p&gt;Thai people eat way more outside (street food) and as a digital nomad I almost eat 100% outside. I’m not in holiday, this is my daily life!&lt;/p&gt;

&lt;p&gt;So compare those prices to the cost of making food at home in Europe. Pad Thai is basically pasta, and I’m sure you can do pasta at the same price at home in Europe. Keep that in mind when comparing!&lt;/p&gt;

&lt;h2 id=&quot;night-life&quot;&gt;Night Life&lt;/h2&gt;

&lt;p&gt;Night Life in Bangkok is really active and you’ll always find people partying at any day of the week.&lt;/p&gt;

&lt;p&gt;I would recommended staying not too close from partying area if you want to sleep well (trust me, sound level is one of the loudest level I’ve ever heard), but not too far if you want to go party and come back home by foot.&lt;/p&gt;

&lt;p&gt;My hostel was 5 min walk from Koah San Road, so it was perfect to go there after work to eat something outside and meet people.&lt;/p&gt;

&lt;h2 id=&quot;massage&quot;&gt;Massage&lt;/h2&gt;

&lt;p&gt;How can I talk about Thailand without talking about Thai massage?!&lt;/p&gt;

&lt;p&gt;Thai massage is a very specific type of massage without any oil. Like Wikipedia says, “rather than rubbing on muscles, the body is compressed, pulled, stretched and rocked”.&lt;/p&gt;

&lt;p&gt;I tried Thai massage in Bangkok and really enjoyed it. I actually went several times for massage in 10 days 😁&lt;/p&gt;

&lt;p&gt;In terms of pricing, it’s a little more expensive that Bali (250/300 baht for a one hour massage, that’s 7/8€), but it still really cheap.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/massage-price.jpg&quot; alt=&quot;Massage prices&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The quality of the massage is probably a little better (hard to compare as it’s totally different kind of massage), but as always, it really depends where you’re going for the massage, and who’s the therapist.&lt;/p&gt;

&lt;h2 id=&quot;visiting-bangkok&quot;&gt;Visiting Bangkok&lt;/h2&gt;

&lt;p&gt;Bangkok city is really beautiful: you have lots of temple to see, and Thai culture is really visible, it’s not just a business center.&lt;/p&gt;

&lt;p&gt;Here are a few pictures I took in Bangkok:&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/Bsmud9qlN6S/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; data-instgrm-version=&quot;12&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bsmud9qlN6S/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bsmud9qlN6S/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Finally back in Asia after Christmas in France with the family 🎄 This time I’m in Thailand for 2 months. First time here for me 🙂 Can’t wait to learn more about this amazing country!&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A post shared by &lt;a href=&quot;https://www.instagram.com/pierregilles.leymarie/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Pierre-Gilles Leymarie&lt;/a&gt; (@pierregilles.leymarie) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2019-01-14T06:34:46+00:00&quot;&gt;Jan 13, 2019 at 10:34pm PST&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Wat Arun Ratchawararam&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/bangkok-city-1.jpg&quot; alt=&quot;Wat Arun Ratchawararam&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Lots of Buddha!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2019-bangkok/bangkok-city-2.jpg&quot; alt=&quot;Buddha&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;pollution&quot;&gt;Pollution&lt;/h2&gt;

&lt;p&gt;Unfortunately this is for me the biggest downside of Bangkok.&lt;/p&gt;

&lt;p&gt;When I was there, there was a big pollution spike and air quality was one of the worst in the world.&lt;/p&gt;

&lt;p&gt;It was so polluted that the government had to  deploy rain-making planes over the city to seed clouds and create a fake rain.&lt;/p&gt;

&lt;p&gt;So we were in the middle of the dry season and it was raining in Bangkok!&lt;/p&gt;

&lt;p&gt;When you see the traffic in Bangkok, you easily understand where the pollution comes from. When I left Bangkok, pollution was so terrible that they had to close school… Not fun!&lt;/p&gt;

&lt;h2 id=&quot;to-conclude&quot;&gt;To conclude&lt;/h2&gt;

&lt;p&gt;I loved my stay in Bangkok, I have met a lot of nice people and this was a really good introduction to the Thai culture.&lt;/p&gt;

&lt;p&gt;If Bangkok was not that polluted, I would actually love to stay more time in Bangkok.&lt;/p&gt;

&lt;p&gt;The city feels safe, the BTS is awesome to move around the city, food is great, and internet is amazing.&lt;/p&gt;

&lt;p&gt;This was definitely not my last time in Bangkok 🤙&lt;/p&gt;

&lt;p&gt;I’m now staying in Koh Lanta for 1.5 months here. I’ll write an article about it after my stay with my feedback on this island.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>How I saved 875$ per year on email marketing</title>
   <link href="https://pierregillesleymarie.com/blog/2018/10/14/how-i-saved-875-dollar-per-year-on-email-marketing.html"/>
   <updated>2018-10-14T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2018/10/14/how-i-saved-875-dollar-per-year-on-email-marketing</id>
   <content type="html">&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;As you know, I founded an open-source project, &lt;a href=&quot;https://gladysassistant.com&quot;&gt;Gladys&lt;/a&gt;, which is a smart home assistant based on a Raspberry Pi.&lt;/p&gt;

&lt;p&gt;I founded the project 5 years ago, and now my email list is bigger than ever: more than 3 000 subscribers on it! 🍾&lt;/p&gt;

&lt;p&gt;I’m sending regularly a newsletter (never more than once a week) about my work on the project to all my subscribers, and for that I’m using usual email marketing tools that many people use.&lt;/p&gt;

&lt;h2 id=&quot;my-previous-email-marketing-stack&quot;&gt;My previous email marketing stack&lt;/h2&gt;

&lt;p&gt;I started with MailChimp. Their service is great, and when I reached 2 000 subscribers I became a happy paying customer.&lt;/p&gt;

&lt;p&gt;I was paying around 50$ per month for their service.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/email-marketing/mailchimp-pricing.jpg&quot; alt=&quot;MailChimp pricing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But something I didn’t like with Mailchimp, is that it made me look like a big corporation.&lt;/p&gt;

&lt;p&gt;Their emails, forms, templates look highly professionnal, which is great for most business.&lt;/p&gt;

&lt;p&gt;But as an indie maker, it looked too “marketing” for me, and people were seeing my emails the same way they were seeing mass marketing emailing.&lt;/p&gt;

&lt;p&gt;So last year, I switched to Convertkit, an email marketing platform for blogger!&lt;/p&gt;

&lt;p&gt;The tool is really great: I was able to send plain-text email, so it’s more personal, and my users loved it!&lt;/p&gt;

&lt;p&gt;The only problem was: the price!&lt;/p&gt;

&lt;p&gt;Don’t misread what I’m saying, &lt;strong&gt;I’m happy to pay for a good service&lt;/strong&gt;, but when it’s too expensive, it’s just not possible.&lt;/p&gt;

&lt;p&gt;When I reached 3 000 suscribers, my pricing bumped to 79$/month.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/email-marketing/convertkit-pricing.jpg&quot; alt=&quot;Convertkit pricing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;79$/month is 948$ per year.&lt;/p&gt;

&lt;p&gt;For an open-source project, 948$ per year is &lt;strong&gt;a lot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even if Gladys is making revenue, putting close to 1k$ per year in email marketing alone was way too high for me.&lt;/p&gt;

&lt;h2 id=&quot;my-new-email-marketing-stack&quot;&gt;My new email marketing stack&lt;/h2&gt;

&lt;p&gt;So I looked on the internet what other people were doing, and I read this article:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.freecodecamp.org/our-nonprofit-needed-a-cheaper-way-to-send-email-blasts-so-we-engineered-one-167322e3f28e&quot;&gt;Our nonprofit needed a cheaper way to send email blasts. So we engineered one.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, Quincy Larson from freeCodeCamp explained that their non-profit have to send a weekly newsletter to millions of people. They did a few cost estimate, and saw that MailChimp would charge them 4 200$ per month to send emails, 50k$ a year!! Way too much for a non-profit.&lt;/p&gt;

&lt;p&gt;So they coded an open-source tool for creating email campaigns that would send emails threw Amazon SES, so they don’t have to deal with IP reputation (because all the email reputation work is done by AWS).&lt;/p&gt;

&lt;p&gt;Amazon SES is super cheap: $0.0001 per email sent (that’s 1$ for 10k emails!).&lt;/p&gt;

&lt;p&gt;I tried their tool, but it seems that it’s currently unmaintained and I struggled to make it work.&lt;/p&gt;

&lt;p&gt;So I looked at the open-source email marketing ecosystem, and found &lt;a href=&quot;https://mailtrain.org/&quot;&gt;MailTrain&lt;/a&gt;. It’s exactly like Convertkit, but it’s free &amp;amp; open-source 😄&lt;/p&gt;

&lt;p&gt;So I installed Mailtrain on a 5$ &lt;a href=&quot;https://m.do.co/c/871afdf5c23d&quot;&gt;DigitalOcean&lt;/a&gt; VPS thanks to Docker, and boom: I canceled my Convertkit account!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s now make a quick calcul&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every month, I’m sending between 3-4 newsletters to 3 100 subscribers.&lt;/p&gt;

&lt;p&gt;Let’s say I’m sending 12k emails a month.&lt;/p&gt;

&lt;p&gt;12k * $0.0001 = $1,20/month&lt;/p&gt;

&lt;p&gt;So if we add the DigitalOcean VPS:&lt;/p&gt;

&lt;p&gt;$1,20 + $5 = $6,20/month&lt;/p&gt;

&lt;p&gt;That’s 74,40$ per YEAR 😍&lt;/p&gt;

&lt;p&gt;Now let’s compare:&lt;/p&gt;

&lt;p&gt;$948 - $74,40 = $873,6/year saved!&lt;/p&gt;

&lt;h2 id=&quot;mailtrain-an-open-source-email-marketing-tool&quot;&gt;Mailtrain, an open-source email marketing tool&lt;/h2&gt;

&lt;p&gt;So let’s show how my mailtrain installation looks like.&lt;/p&gt;

&lt;p&gt;It’s simple, and it just works:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/email-marketing/mailtrain-overview.jpg&quot; alt=&quot;Mailtrain Overview&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;little-tip-to-install-mailtrain&quot;&gt;Little tip to install Mailtrain&lt;/h3&gt;

&lt;p&gt;To install it on your server, just follow the instructions on MailTrain repository with the Docker install (it’s the easiest way).&lt;/p&gt;

&lt;p&gt;The only thing I changed is that I added a nginx-proxy container in front of Mailtrain to enable HTTPS.&lt;/p&gt;

&lt;p&gt;I changed the docker-compose.override.yml with the following configuration:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/Pierre-Gilles/2d3f6a7ca7fe4531730856fe7485a8a1.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;You just have to change the domain/email informations with your configuration and it’ll work 🙂&lt;/p&gt;

&lt;h2 id=&quot;email-provider&quot;&gt;Email provider&lt;/h2&gt;

&lt;p&gt;You can use any email provider you want, as soon as they provide SMTP credentials. I’m currently using &lt;a href=&quot;https://www.mailgun.com/&quot;&gt;Mailgun&lt;/a&gt; while my Amazon SES account is being activated by Amazon, but I’ll switch to Amazon SES as soon as my account is ready.&lt;/p&gt;

&lt;p&gt;Mailgun is free for the first 10k emails/month so don’t hesitate to use them, the setup is dead easy!&lt;/p&gt;

&lt;p&gt;Amazon SES is more complicated to setup, but way cheaper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You probably wonder why I don’t use Mailgun if the 10k first emails per month are free. Well, I also have a Gladys Discourse Community which is sending 50/60k emails per month, so it’s beginning to cost me a lot. Amazon is the best option for me.&lt;/p&gt;

&lt;h3 id=&quot;suscription-forms&quot;&gt;Suscription forms&lt;/h3&gt;

&lt;p&gt;Mailtrain provide you default subscription forms hosted on your domain and they are really great.&lt;/p&gt;

&lt;p&gt;You can even design your own if you want to adapt it to your identity.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/email-marketing/mailtrain-form.jpg&quot; alt=&quot;Subscription pricing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There is even a REST API if you want to plug any service (Zapier for example) on it, or do any automated work on it. The best part is that as it’s open-source you can do anything you want and modify the software!&lt;/p&gt;

&lt;h3 id=&quot;automation&quot;&gt;Automation&lt;/h3&gt;

&lt;p&gt;I personaly never use automation as I find it too “marketing” (users hate automated emails), but Mailtrain provide a tool for automation if you need it 😉&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;I hope this article will inspire you and make you want to switch to an open-source alternative to send emails 🙂&lt;/p&gt;

&lt;p&gt;Sometimes it’s great to pay for paid services: they manage everything for you, the service is great, provide tons of value, and you’re happy to support their development.&lt;/p&gt;

&lt;p&gt;But sometimes, you have to look around for alternatives, and see if paying that much money is a good investment for your business.&lt;/p&gt;

&lt;p&gt;Here, this was not the case for me. Close to 1k$ a year saved can change a lot for my business, and I’m happy I cut this cost.&lt;/p&gt;

&lt;p&gt;Open-Source, I love you 🤟❤️&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My first month as a Digital Nomad in Bali</title>
   <link href="https://pierregillesleymarie.com/blog/2018/10/07/my-first-month-as-a-digital-nomad-in-bali.html"/>
   <updated>2018-10-07T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2018/10/07/my-first-month-as-a-digital-nomad-in-bali</id>
   <content type="html">&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;It’s already been one month since I’m working from Canggu, Bali. I wanted to write a blog post to give you a feedback about my life here. It may make you want you to come here, and that’s the goal 😄 Bali is amazing!&lt;/p&gt;

&lt;h2 id=&quot;holidays&quot;&gt;Holidays&lt;/h2&gt;

&lt;p&gt;I started this trip by 10 days of holidays with my friends.&lt;/p&gt;

&lt;p&gt;We landed in Jakarta, the capital of Indonesia, which is located on Java Island.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/indonesia-map.jpg&quot; alt=&quot;Indonesia Map&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here we immediately took a flight to Yogakartha, in order to visit Borobudur temple! We woke up at 2 AM to see the sunrise, and we enjoyed the beautiful view of this temple:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/borobudur-jump.jpg&quot; alt=&quot;Borobudur temple&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then we took another flight to go to Bromo Volcano, a volcano at the east of Java Island. Same story here, we woke up at 2 AM to go at the top of a mountain in front of the volcano to see the sunrise on it. The view is really worth it!&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/Bm3QWu5h2Ej/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; data-instgrm-version=&quot;12&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bm3QWu5h2Ej/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Bm3QWu5h2Ej/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Amazing sunrise 🌅 at Bromo Volcano 🌋, Indonesia 🇮🇩👌&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A post shared by &lt;a href=&quot;https://www.instagram.com/pierregilles.leymarie/?utm_source=ig_embed&amp;amp;utm_medium=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Pierre-Gilles Leymarie&lt;/a&gt; (@pierregilles.leymarie) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2018-08-24T13:30:22+00:00&quot;&gt;Aug 24, 2018 at 6:30am PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; defer=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Finally, we took another flight to Denpasar, Bali.&lt;/p&gt;

&lt;p&gt;In Bali, we did Ubud, to see Tegallalang Rice Terraces:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/rice-fields.jpg&quot; alt=&quot;Tegallalang Rice Terraces&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And went to Amed on the north-east of the Island to go snorkeling:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/snorkeling.jpg&quot; alt=&quot;Snorkeling in Amed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And finally, my friend left me in Canggu, and went back to France! Canggu is located on the west coast of Bali:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/canggu-map.jpg&quot; alt=&quot;Canggu map&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;rent&quot;&gt;Rent&lt;/h2&gt;

&lt;p&gt;When I arrived in Canggu, I needed to find a room I can rent monthly. My criteria were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Not too expensive (Max 350€ per month)&lt;/li&gt;
  &lt;li&gt;Walking distance from the beach and Dojo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So on the first day, I just went at Old’s Man to take a beer on the beach. In about 5 minutes, I met some guys, and they proposed me to go to their residence for a party there. I followed them, and discovered the owner still had one room available 😄 So I visited the room, and took it!&lt;/p&gt;

&lt;p&gt;Here is the residence, and the private pool:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/gita.jpg&quot; alt=&quot;Gita Homestay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here is my room!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/room.jpg&quot; alt=&quot;Gita Homestay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And here is my street&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/street-gita.jpg&quot; alt=&quot;Street Gita&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For that room, I pay around 318€ per month (5.5 millions IDR).&lt;/p&gt;

&lt;p&gt;They told me that next month I’m going to be kicked out of this room, but they’ll give me a cheaper one in this residence (😁), that will cost me only 200€ per month, so that’s really great.&lt;/p&gt;

&lt;p&gt;I’m exactly 8 minutes walk from Batu Bolong Beach, and 5 minutes walk from Dojo Bali, the coworking space where I work.&lt;/p&gt;

&lt;p&gt;My room is cleaned everyday, and they provide water, toilet paper, towel, it’s like a hotel that I pay monthly 😉&lt;/p&gt;

&lt;h2 id=&quot;food&quot;&gt;Food&lt;/h2&gt;

&lt;p&gt;No debate here, the food is amazing in Bali, and crazy cheap! I eat at the restaurant all the time, and never ate at home yet. Depending where you go, the food cost:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;5k for a street food seller on a motorbike (0,28€)&lt;/li&gt;
  &lt;li&gt;15k/30k for a local Indonesian Warung (90cts/1€60)&lt;/li&gt;
  &lt;li&gt;50k/100k if you go to more “hipster/touristic” restaurant (2€85/5€70)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m going most of the time to little indonesian Warung because I like the Indonesian food and i’s cheap!&lt;/p&gt;

&lt;p&gt;I’m happy to live just next to Warung Sika, a really good Warung with prices starting at 14k.&lt;/p&gt;

&lt;p&gt;Here are a few pictures of the food.&lt;/p&gt;

&lt;p&gt;A Smoothie Bowl with Bali’s fruits at Cocomo Canggu:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/smoothie-bowl.jpg&quot; alt=&quot;Smoothie Bowl&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Warung Sika, great Nasi Campur for 24k:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/warung-sika.jpg&quot; alt=&quot;Warung Sika&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Healthy Avocado Salad in Alter Ego in front of Dojo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/avocado.jpg&quot; alt=&quot;Avocado&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;money&quot;&gt;Money&lt;/h2&gt;

&lt;p&gt;The currency here is the Indonesian Roupiah (IDR), and today 1€ = 17 000 IDR.&lt;/p&gt;

&lt;p&gt;You can often pay by card in Bali, but most places will add a 3% fee on top of that, so I just pay everything by cash.&lt;/p&gt;

&lt;p&gt;I use &lt;a href=&quot;https://www.revolut.com&quot;&gt;Revolut&lt;/a&gt; to withdraw money here (Revolut is life here, get one card if you don’t have one already), and it gaves me the best rate and no fees.&lt;/p&gt;

&lt;p&gt;ATM skimming are frequent here, so by paying with Revolut I can keep my card “frozen” most of the time, and I just unblock it when I need it. So even if my card is copied, the guy won’t be able to use it.&lt;/p&gt;

&lt;p&gt;In 1.5 months in Indonesia, I haven’t been skimmed (yet) 🙂&lt;/p&gt;

&lt;h2 id=&quot;work--internet&quot;&gt;Work &amp;amp; Internet&lt;/h2&gt;

&lt;p&gt;As I told you, I’m working from Dojo Bali, a coworking space located in Canggu, just near Echo beach.&lt;/p&gt;

&lt;p&gt;This is a typical morning for me at Dojo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/morning-dojo.jpg&quot; alt=&quot;Morning at Dojo Bali&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Internet is powered by 6 optic fiber, but the speed depends of the moment of the day. Dojo often get crowded in the afternoon and the speed goes down as more people are on the Wi-Fi. But on most of the day internet is just great!&lt;/p&gt;

&lt;p&gt;In Skype meeting rooms, you have Ethernet cables so the speed is just perfect for calls. Don’t forget your Ethernet adapter if you have a Mac 😉&lt;/p&gt;

&lt;p&gt;You can work in Café as well, there are many great cafe in Canggu and they all have Wi-Fi. I discovered recently the app “Wifi Map” that crowdsource every Wi-Fi on earth, share passwords and display Wi-Fi speed. Download the app and you’ll see that many cafe have awesome Wi-Fi! For example, I discovered recently that “Avocado Factory” just near where I live has a Wi-Fi with 40Mbps, sweet!&lt;/p&gt;

&lt;h2 id=&quot;mobile-internet&quot;&gt;Mobile Internet&lt;/h2&gt;

&lt;p&gt;Mobile internet in Indonesia is really great, and super cheap.&lt;/p&gt;

&lt;p&gt;The best carrier is Telkomsel, and you have LTE all around Bali with really great speed (I have 40Mbps in 4G most of the time).&lt;/p&gt;

&lt;p&gt;Now, the price: I paid 100k (5,70€) at the airport for 1 SIM card + 20gb of data all around Indonesia for 1 month. Super deal!&lt;/p&gt;

&lt;p&gt;This month I upgraded to 40gb a month for 10€/month, but I think I’m going to downgrade because it’s too much.&lt;/p&gt;

&lt;p&gt;The fun thing is that it’s not like in Europe/US, they’re not connected to your card, and you have to recharge every month by going to a store selling internet package 😛 I find it great because you are more flexible, and can reduce/upgrade your mobile data at any time.&lt;/p&gt;

&lt;h2 id=&quot;visa&quot;&gt;Visa&lt;/h2&gt;

&lt;p&gt;You have different options. If you stay 6 months in Bali without living the country, you can apply for a 6 months social visa. This is not what I did!&lt;/p&gt;

&lt;p&gt;I took a simple Visa On Arrival (VOA) at the airport for 35$, that allows me to stay 1 month in the country. Then I did a visa extension with an agent, Visa4Bali, for 650k (37€). I had to go one morning to Jimbaran to give my fingerprints, and now I’m able to stay 1 month more in Bali.&lt;/p&gt;

&lt;p&gt;With the VOA + extension, I’m able to stay 60 days in a row in Bali, then I have to do a Visa run.&lt;/p&gt;

&lt;p&gt;So I’m going 4 days in Singapore in 2 week to renew my Visa. Some people go to Kuala Lumpur, that’s another option.&lt;/p&gt;

&lt;h2 id=&quot;meeting-people--going-out&quot;&gt;Meeting people &amp;amp; Going out&lt;/h2&gt;

&lt;p&gt;The really good thing about Dojo Bali is that the community is amazing! Every month, you have Dojo’s BBQ, a party at the coworking place with a great barbecue (the meat is just amazing) and open drinks (beers, cocktails, shots!).&lt;/p&gt;

&lt;p&gt;People here are all welcoming and open-minded, and you make many friends super easily.&lt;/p&gt;

&lt;p&gt;Every friday night, there is Dojo “Beach &amp;amp; beers”, where you can meet Dojo members. Always a great night!&lt;/p&gt;

&lt;p&gt;Even outside Dojo community, you meet many people in Bali because there are lots of tourist and long term traveler here. Just go at Old’s Man on Wednesday night and you’ll understand 😛 There are many beach club here as well, like “The Lawn” just next to Old’s Man.&lt;/p&gt;

&lt;p&gt;Most parties ends at the Sand Bar (directly on Batu Bolong Beach) that plays music until 4 AM.&lt;/p&gt;

&lt;h2 id=&quot;massage&quot;&gt;Massage&lt;/h2&gt;

&lt;p&gt;If you like massage, Bali is a great place to go! The cheapest massage I had was “60k for 60minutes”, which is 3€80 for 1 hour of massage in a Spa, not bad! 😄&lt;/p&gt;

&lt;p&gt;But you can get more fancy massages: 4 hands massage, deep tissue, aromatherapy, foot massage.&lt;/p&gt;

&lt;p&gt;The best massage I had was in Amed, in a Spa on top of a hill, it was 130k (7,5€) for more than one hour masssage in a really great environnement. The spa was kind of open so we had the lights of the sunset 🌄&lt;/p&gt;

&lt;h2 id=&quot;traffic&quot;&gt;Traffic&lt;/h2&gt;

&lt;p&gt;This is the only point I don’t like in Bali. The traffic when you want to move from one city to another is really bad. There are hundreds of scooters everywhere, and many people have accident here.&lt;/p&gt;

&lt;p&gt;That’s why I tried to be at walking distance from everything.&lt;/p&gt;

&lt;p&gt;But sometimes I need to ride a scooter, so I just rent one for a day (50k a day/2,85€) and try to drive carefully.&lt;/p&gt;

&lt;h2 id=&quot;my-budget&quot;&gt;My budget&lt;/h2&gt;

&lt;p&gt;This is my budget for this first 4 months in Bali. It includes everything I pay, not just my spending in Bali. This means there are all my online subscriptions (iCloud, Revolut, mobile phone FR &amp;amp; indonesian).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/budget.jpg&quot; alt=&quot;Budget Bali&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;October is a little bit more expensive because I’m going to Singapore for my Visa run so I pay 109€ of flight (two-way) + 88€ for 3 days of hotel here (and I’m going &lt;a href=&quot;https://twitter.com/pierregillesl/status/1045108572239978497&quot;&gt;to sleep in a spaceship&lt;/a&gt;, yes)&lt;/li&gt;
  &lt;li&gt;I’m doing my laundry every week, and it costs me 35k (for my clothes cleaned + ironed!)&lt;/li&gt;
  &lt;li&gt;I’m back in France in December to get my Engineer Diploma + Christmas&lt;/li&gt;
  &lt;li&gt;“Education” is my budget for books, online courses or movies. I read on my Kindle because books are too heavy for a minimalist traveler 😄&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;my-feedback-about-bali&quot;&gt;My feedback about Bali&lt;/h2&gt;

&lt;p&gt;My feedback is really simple: Life is great here! 🏖️ Food is amazing, weather is amazing, people are great!&lt;/p&gt;

&lt;p&gt;Before going to Bali, I was living in Paris and was commuting hours per day in the metro.&lt;/p&gt;

&lt;p&gt;Now, my day starts by waking up with the sun ☀️ at 6 AM, walking 5 minutes to work, working the whole morning in a nice environnement near the beach.&lt;/p&gt;

&lt;p&gt;Around 11:30, I go out and eat at the restaurant with my friends of Dojo, and after I usually have a nap at my pool.&lt;/p&gt;

&lt;p&gt;I go back to work after my nap, work until 5 PM and then go swimming/surfing at the beach until sunset around 6:15 PM.&lt;/p&gt;

&lt;p&gt;After that I go eating outside/have a drink/just chill on the beach!&lt;/p&gt;

&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;/h2&gt;

&lt;p&gt;If you have any questions, don’t hesitate to ask them in the comments below 🙂&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I'm moving to Bali!</title>
   <link href="https://pierregillesleymarie.com/blog/2018/08/14/I-am-moving-to-bali.html"/>
   <updated>2018-08-14T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2018/08/14/I-am-moving-to-bali</id>
   <content type="html">&lt;p&gt;It’s now official, I’m moving to Bali the 20th of August, 2018 ✈️&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/bali-1.jpg&quot; alt=&quot;Bali Forest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As I announced recently, I decided to &lt;a href=&quot;https://www.patreon.com/gladysassistant/overview&quot;&gt;work part-time on my open-source project Gladys&lt;/a&gt; 👨🏼‍💻. In the other part-time, I’m going to work as a freelancer as a back-end Engineer (&lt;a href=&quot;https://twitter.com/pierregillesl&quot;&gt;hire me&lt;/a&gt;!). This new life allows me to be location independant, and to choose where I want to live.&lt;/p&gt;

&lt;h2 id=&quot;my-story&quot;&gt;My story&lt;/h2&gt;

&lt;p&gt;When I was studying engineering, I was in a small city located 1h in the north of Paris, Compiègne. This city is small, and have lots of advantages when you are a student:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rent is cheap: I was paying 300€/month for a crazy 90m2 appartment with 2 roomates, center of the city, big rooms with queen bed, big living rooms with 4 sofas, two bathrooms, 2 balcony so you can eat outside, inside car park, elevator, all included!&lt;/li&gt;
  &lt;li&gt;Cost of life is cheap: With our engineering school, we had some pretty good deals, like 33cl of Belgium beer for 1€50, or vegetables food pack for 35€/5 weeks of organic food coming from local producers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I moved to Paris for work after that, I realized that life is not really the same as in Compiègne…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Renting an appartement is more like 1000€/month for something decent&lt;/li&gt;
  &lt;li&gt;A bad beer is 5/7€, and a good belgium beer is more something like 6/10€. Yes, 10€ for a beer!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that, life is not that much more fun. You need to commute to go to work. Often, the metro is broken and you get delayed. Spending 45 minutes 2 times a day in a metro full of people sweating is not something fun, you are just wasting your time.&lt;/p&gt;

&lt;p&gt;So I decided that Paris was not the city I wanted to live in today 😄&lt;/p&gt;

&lt;h2 id=&quot;the-nomad-movement&quot;&gt;The Nomad movement&lt;/h2&gt;

&lt;p&gt;Since a couple of years, I’m following the &lt;a href=&quot;https://nomadlist.com/help&quot;&gt;Nomad movement&lt;/a&gt; and some famous Nomads like &lt;a href=&quot;https://twitter.com/levelsio&quot;&gt;@levelsio&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/JohnONolan&quot;&gt;@JohnONolan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Being nomad means being location independant and living from where you want in the world!&lt;/p&gt;

&lt;p&gt;Pieter Levels created a website, &lt;a href=&quot;https://nomadlist.com/&quot;&gt;NomadList.com&lt;/a&gt;, that allows you to compare lots of cities around the world.  You can compare cost of life, weather, air quality, proximity of beach, safety, fun, internet quality, and hundreds of other criterias.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/nomadlist-screenshot.jpg&quot; alt=&quot;Nomadlist screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I compared a few cities, did some research, and…. found these awesome pictures of Bali 😄&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/bali-2.jpg&quot; alt=&quot;Bali beach&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Crazy, no ? 😀&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2018-bali/bali-3.jpg&quot; alt=&quot;Bali forest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is where I want to go for my first destination 😍&lt;/p&gt;

&lt;p&gt;Cost of life is definitely cheaper than in France, and there are some crazy coworking space where you can work from, like Dojo Bali:&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/BB2D5gjQfnL/?utm_source=ig_embed&quot; data-instgrm-version=&quot;9&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:8px;&quot;&gt; &lt;div style=&quot; background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;&quot;&gt; &lt;div style=&quot; background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;&quot;&gt;&lt;/div&gt;&lt;/div&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/BB2D5gjQfnL/?utm_source=ig_embed&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Good to see these guys getting productive and focused today! #coworking #digitalnomad #workation #bali #collaboration #canggu #echobeach #meeting #cafe #dojobali #bali&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A post shared by &lt;a href=&quot;https://www.instagram.com/dojobali/?utm_source=ig_embed&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;&quot; target=&quot;_blank&quot;&gt; Dojo Bali Coworking&lt;/a&gt; (@dojobali) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2016-02-16T11:00:42+00:00&quot;&gt;Feb 16, 2016 at 3:00am PST&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; defer=&quot;&quot; src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Dojo Bali have six optic fiber lines for redundancy, and their website claims 300Mbps fast internet with 99,98% uptime. It may not be that in real life as you are not alone on the Wi-Fi, but still it’s pretty good for a developing country.&lt;/p&gt;

&lt;p&gt;And Dojo Bali is located in Canggu, just near the beach 🏝&lt;/p&gt;

&lt;p&gt;Perfect!&lt;/p&gt;

&lt;h2 id=&quot;whats-next-for-me&quot;&gt;What’s next for me&lt;/h2&gt;

&lt;p&gt;As I was saying in my article on &lt;a href=&quot;https://www.patreon.com/gladysassistant/overview&quot;&gt;Gladys Patreon&lt;/a&gt;, my current contract as Lead Backend Engineer at BulldozAIR ends the 16th of August 2018.&lt;/p&gt;

&lt;p&gt;The 20th of August, I’m flying to Indonesia ✈️&lt;/p&gt;

&lt;p&gt;I’m going to take at least 3 weeks of holidays at first, to take the time to breathe after these crazy years of work. In the last 3 years, the longest holidays I had was 10 days off, and even, I was not fully disconnecting each time with my work on Gladys. Off-time is really important for both mental and physical health, so here I’ll be fully disconnected during my holidays to relax and empty my brain 😊 My friends are even coming with me the first 2 weeks to visit Indonesia with me!&lt;/p&gt;

&lt;p&gt;I know this time away from my computer will boost my creativity and my energy, and I can’t wait to be back at work on Gladys from Bali after holidays 🤘&lt;/p&gt;

&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;

&lt;h3 id=&quot;how-do-you-handle-visa-&quot;&gt;How do you handle Visa ?&lt;/h3&gt;

&lt;p&gt;When you arrive at the airport in Indonesia, you can register for a 30 days Visa On Arrival (VOA) renewable one time.&lt;/p&gt;

&lt;p&gt;So, after 60 days in the country, you have to leave the country and come back.&lt;/p&gt;

&lt;p&gt;So I’ll just have to travel to other places at least once every 2 months.&lt;/p&gt;

&lt;p&gt;My first visa-run is going to be in Singapor, I will be there 4 days at the end of October.&lt;/p&gt;

&lt;h3 id=&quot;and-your-home-automation-setup-&quot;&gt;And your home automation setup ?&lt;/h3&gt;

&lt;p&gt;This is the first question I get when I’m talking about this project of moving to Asia! 😛&lt;/p&gt;

&lt;p&gt;Of course I’m bringing my Raspberry Pi, some sensors and some connected stuff!&lt;/p&gt;

&lt;p&gt;Don’t worry!&lt;/p&gt;

&lt;h3 id=&quot;no-more-gladys-talks-in-france-&quot;&gt;No more Gladys talks in France ?&lt;/h3&gt;

&lt;p&gt;Most of my communication is online on Twitter/Mailing list/Gladys community/Discord, so on that point it won’t change. I’m even doing more and more calls with the community, and being remote won’t change that.&lt;/p&gt;

&lt;p&gt;But sometimes, true, doing physical events is better.&lt;/p&gt;

&lt;p&gt;The world is small, flights are not so expensive when you have no constraints and when you schedule your flights in advances!&lt;/p&gt;

&lt;p&gt;Sooo… I already have my ticket back to France for Christmas 2018!&lt;/p&gt;

&lt;p&gt;I’m definitely planning to do some Gladys talks in France in end 2018, if I find some schools/Fablabs/companies to host the events of course!&lt;/p&gt;

&lt;p&gt;My goal is really to group Gladys events when I’m back in France to see my family or do some administrative stuff.&lt;/p&gt;

&lt;h3 id=&quot;are-you-escaping-french-taxes-&quot;&gt;Are you escaping French taxes ?&lt;/h3&gt;

&lt;p&gt;Noooo 😂&lt;/p&gt;

&lt;p&gt;My company is a french company and all taxes I pay are paid in France.&lt;/p&gt;

&lt;p&gt;Traveling around the world doesn’t change anything about that.&lt;/p&gt;

&lt;h3 id=&quot;how-long-are-you-going-to-stay-in-bali-&quot;&gt;How long are you going to stay in Bali ?&lt;/h3&gt;

&lt;p&gt;I’m going to stay at least 4 months in Bali, then in December I’ll be back in France a couple of weeks.&lt;/p&gt;

&lt;p&gt;In January, I’m planning to move to Thailand. Bangkok &amp;amp; Chiang Mai seems to be nice cities for digital nomads and it’s the best season in Thailand to travel here.&lt;/p&gt;

&lt;p&gt;My nomad lifestyle has no scheduled end 😉&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;It’s always been a dream for me to go traveling full-time, and it’s now a reality!&lt;/p&gt;

&lt;p&gt;I’m going to share as much as I can on this blog about my journey as a nomad!&lt;/p&gt;

&lt;p&gt;I’m convinced that traveling, discovering new cultures and meeting new people is going to be good for my creativity and will improve my productivity.&lt;/p&gt;

&lt;p&gt;I can’t wait to be on the plane to Indonesia 😱&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I replaced my MacBook Pro by a Raspberry Pi during 1 week</title>
   <link href="https://pierregillesleymarie.com/blog/2017/07/03/how-i-replaced-my-macbook-pro-by-a-raspberry-pi.html"/>
   <updated>2017-07-03T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2017/07/03/how-i-replaced-my-macbook-pro-by-a-raspberry-pi</id>
   <content type="html">&lt;p&gt;Last week, I forgot my bag in a G7 Taxi in Paris. Inside was the MacBook Pro I’m using every day. This day was probably not a good day.&lt;/p&gt;

&lt;p&gt;The problem with losing your computer in a Taxi in Paris is that you can’t contact the driver like you would do with an Uber. If you don’t remember the Taxi numberplate, you have no chance to see the driver again. The only chance you have to get your bag back is to go to the “Lost and Found” Police department of Paris, and hope the driver has been honest.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2017-07-03-how-i-replaced-my-macbook-pro-by-a-raspberry-pi/my_good_old_macbook_pro.jpeg&quot; alt=&quot;I miss you, MacBook Pro!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I miss you, Macbook Pro!&lt;/p&gt;

&lt;p&gt;As the driver won’t drop my bag immediately, I decided to wait one week before considering my bag as completely lost. What can I do during one week without any computers at home?
Wait… that’s not true, I have a computer at home! What about using my Raspberry Pi 3?&lt;/p&gt;

&lt;p&gt;The Raspberry Pi is clearly a good idea: It’s powerful, it runs Linux, it’s silent.  Let’s go for one week on this!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/how-i-replaced-my-mabook-pro-by-a-raspberry-pi.png&quot; alt=&quot;My Raspberry Pi 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Raspberry Pi 3&lt;/p&gt;

&lt;h2 id=&quot;softwares-i-was-using-on-my-mac&quot;&gt;Softwares I was using on my Mac&lt;/h2&gt;

&lt;p&gt;I’m software engineer, and use this personal Mac for lots of stuff:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Coding on the opens-source project Gladys I founded. &lt;a href=&quot;https://gladysassistant.com&quot;&gt;Gladys&lt;/a&gt; is a home assistant based on a Raspberry Pi (like a kind of Jarvis), written in Node.js so it’s basically some Node.js development. For this I’m using VS Code + Node.js + MySQL.&lt;/li&gt;
  &lt;li&gt;Writing on Gladys blog. I’m writing my articles in Markdown using Macdown on my Mac. I’m editing images with Photoshop.&lt;/li&gt;
  &lt;li&gt;Answering messages on Gladys community (it’s basically a Discourse forum)&lt;/li&gt;
  &lt;li&gt;Video editing on Final Cut Pro (outch, this is going to be hard to replace)&lt;/li&gt;
  &lt;li&gt;Web browsing (News reading, YouTube, Twitter)&lt;/li&gt;
  &lt;li&gt;Terminal, mostly for dev tools &amp;amp; SSH (that’s where I spend most of my day, and should be the easiest thing to replace)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;setting-up-the-raspberry-pi&quot;&gt;Setting up the Raspberry Pi&lt;/h2&gt;

&lt;p&gt;For those who don’t know the Raspberry Pi, it’s a tiny Linux computer powered by a quad core ARM CPU and 1Gb of RAM. It’s used in a lot of DIY project, because it only costs &lt;a href=&quot;https://www.amazon.com/gp/product/B01C6Q2GSY/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=gladproj-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B01C6Q2GSY&amp;amp;linkId=0837cd1b3cc2b715934805ef5eb11723&quot;&gt;35$ on average&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have severals of them at home for my Home Automation project Gladys, so I took one to convert it into a full Desktop PC.&lt;/p&gt;

&lt;p&gt;First step was to download Raspbian Jessie. I used my parents computer, and cloned the image on a 16Gb micro-SD card.&lt;/p&gt;

&lt;p&gt;I plugged an old Wireless Mouse, a really old USB keyboard, a 22” HDMI screen into the Raspberry Pi, and turned everything on.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2017-07-03-how-i-replaced-my-macbook-pro-by-a-raspberry-pi/desktop_setup.jpeg&quot; alt=&quot;Raspberry Pi Desktop Setup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first good impression when turning the Pi on is the noise: The Raspberry Pi doesn’t have any fans, so it’s perfectly silent. My Mac was quite silent too, but not that silent.&lt;/p&gt;

&lt;h2 id=&quot;working-on-the-raspberry-pi&quot;&gt;Working on the Raspberry Pi&lt;/h2&gt;

&lt;h3 id=&quot;web-browsing&quot;&gt;Web browsing&lt;/h3&gt;

&lt;p&gt;Raspbian Jessie has Chromium included. I decided to go with it, and the result is quite good. Browsing is nearly as fast as on a classical computer, and as it’s Chromium, Javascript is perfectly executed. The only problem is that each tab in Chromium is a separate process.. Each chromium process takes 150/200Mb of RAM, and since the Raspberry Pi has 1Gb of RAM, having 3/4 tabs opened is really the limit before having a laggy system. And don’t ever try to swap on the SD card, it’s really slow!&lt;/p&gt;

&lt;p&gt;Ok, so lesson learned: Only keep 3 tabs opens. I just need to forget about “right-click” -&amp;gt; “Open in a new tab”, and I’ll be good.&lt;/p&gt;

&lt;h3 id=&quot;writing&quot;&gt;Writing&lt;/h3&gt;

&lt;p&gt;As a power-user of Evernote, I decided to try the Evernote web app inside Chromium. And it works great! This article is currently written inside Evernote for web, and it’s almost as good as the mac app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2017-07-03-how-i-replaced-my-macbook-pro-by-a-raspberry-pi/evernote_web_raspberry_pi.png&quot; alt=&quot;Using Evernote Web on a Raspberry Pi 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I haven’t found any great Markdown writing app available on Linux and compiled for ARM, so I’ll just copy paste the note content inside the default text-editor, add markups, and git push the article to my github page repository to publish it.&lt;/p&gt;

&lt;h3 id=&quot;development&quot;&gt;Development&lt;/h3&gt;

&lt;p&gt;To develop, I’m using VS Code, and spend most of my remaining time in the terminal.&lt;/p&gt;

&lt;p&gt;For the terminal, the Raspberry Pi is just perfect. Nothing change, I’m at home.&lt;/p&gt;

&lt;p&gt;For VS Code, whereas it’s possible to compile it on ARM, I was more skeptical. VS Code is an electron based app, so it starts a full instance of Chromium. It may be too heavy to run it next to Chromium.&lt;/p&gt;

&lt;p&gt;I decided to use vim with the Javascript plugin.&lt;/p&gt;

&lt;p&gt;The coding process itself is quite good. The only problem is that most tools you use on your dev machine are really slow on a Rasperry Pi. Webpack, uglifyJS, Babel: My good old Mac was really better at this.&lt;/p&gt;

&lt;h3 id=&quot;image-editing&quot;&gt;Image editing&lt;/h3&gt;

&lt;p&gt;I forgot about Photoshop, and first tried Canva on the web. It was way too slow to really use it. And since I’m taking my pictures with my camera/phone, and usually transfer them using the SD port of the Mac, the best solution I found was connecting my camera using Wi-Fi to my iPhone.&lt;/p&gt;

&lt;p&gt;Then, using the Canon app, I downloaded the pictures I wanted, edited the picture inside Canva iOS app, and uploaded the images to Google Drive. Not the most efficient, but it works!&lt;/p&gt;

&lt;p&gt;Then, to resize the image so it’s not too heavy in an article, I simply used an online resizer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/2017-07-03-how-i-replaced-my-macbook-pro-by-a-raspberry-pi/resize_image_min.png&quot; alt=&quot;Resizing image Raspberry Pi 3 web&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note: Google Drive was really hard to use on the Raspberry Pi.&lt;/p&gt;

&lt;h3 id=&quot;video-editing&quot;&gt;Video editing&lt;/h3&gt;

&lt;p&gt;Let’s just forgot this part :D
I won’t edit any videos this week.
If I needed to, I would have used the iOS iMovie app. Since my Mac was already struggling with video editing, the Raspberry Pi would have died.&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;I’ve been using the Raspberry Pi since the first model as a home automation server, but I’ve never really used the desktop version in the past.&lt;/p&gt;

&lt;p&gt;And I was impressed by the speed and usability of the Raspberry Pi 3 in desktop mode. It’s definitely not a tool for compiling heavy software or rendering 4K videos, but for basic blogging, browsing and simple development, it just works.&lt;/p&gt;

&lt;p&gt;For the price it costs, you definitely have a nice machine.&lt;/p&gt;

&lt;p&gt;I really hope I’ll get my mac back, (finger crossed) but this was a good opportunity to test the Raspberry Pi in Desktop mode!&lt;/p&gt;

&lt;p&gt;PS: For all users on &lt;a href=&quot;https://gladysassistant.com&quot;&gt;Gladys&lt;/a&gt;, sorry for not being that present last week, it was not an easy week. Without my Mac, it was hard to publish big changes on the project, especially on the video pack I’m working on. But I’ll be back soon!&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Building a powerful REST API with Node.js (Part 3)</title>
   <link href="https://pierregillesleymarie.com/blog/gladys/2017/04/30/building-rest-api-using-node-js.html"/>
   <updated>2017-04-30T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/gladys/2017/04/30/building-rest-api-using-node-js</id>
   <content type="html">&lt;p&gt;Hi, everyone!&lt;/p&gt;

&lt;p&gt;This article is the third article of my series “Refactoring Gladys Developer Platform”. Last time, I was explaining how &lt;a href=&quot;/blog/gladys/2017/04/25/database-design-with-postgresql.html&quot;&gt;I designed the PostgreSQL database of the new platform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Today, I’m going to talk about Node.js development. For our new platform we need a fast REST API, and we are going to use Node.js to build it.&lt;/p&gt;

&lt;h2 id=&quot;why-nodejs-&quot;&gt;Why Node.js ?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Node.js is lightning fast. It’s built on top of Google V8 engine, which compile JavaScript to machine code at execution.&lt;/li&gt;
  &lt;li&gt;Node.js I/O are Asynchronous and Event Driven. It means that all input/output operation (database call, file read, HTTP request, etc..) are not blocking your process, and you can still do other things when an I/O operation is performed. If you send an HTTP request to a third-party API (ex: Sending an email to your email provider), it will probably take something like 300/400ms. During this time, you’ll be able to do other things. This asynchronous nature makes Node.js extremely scalable.&lt;/li&gt;
  &lt;li&gt;Ecosystem: NPM (Node Package Manager) is the largest repository of open-source libraries in the world&lt;/li&gt;
  &lt;li&gt;It’s used by top companies: Netflix, Uber, Paypal, Wal-Mart, Groupon.&lt;/li&gt;
  &lt;li&gt;It’s Open-Source and released under the MIT Licence.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nodejs.org/en/&quot;&gt;Node.js&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.quora.com/What-is-the-best-example-of-how-NodeJS-has-saved-save-money-for-company-startup/answer/Nextbrain-Technologies&quot;&gt;What is the best example of how NodeJS has saved/save money for company/startup?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-rest-api&quot;&gt;The REST API&lt;/h2&gt;

&lt;p&gt;On this back-end, we are managing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;users&lt;/li&gt;
  &lt;li&gt;modules&lt;/li&gt;
  &lt;li&gt;scripts&lt;/li&gt;
  &lt;li&gt;sentences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s all!&lt;/p&gt;

&lt;p&gt;Let’s cover the routes we need:&lt;/p&gt;

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

&lt;ul&gt;
  &lt;li&gt;POST /signup =&amp;gt; account creation&lt;/li&gt;
  &lt;li&gt;POST /login =&amp;gt; login&lt;/li&gt;
  &lt;li&gt;PATCH /user/:id =&amp;gt; modify your account&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;POST /module =&amp;gt; publish a module&lt;/li&gt;
  &lt;li&gt;PATCH /module/:id =&amp;gt; modify a module&lt;/li&gt;
  &lt;li&gt;DELETE /module/:id =&amp;gt; remove a module&lt;/li&gt;
  &lt;li&gt;POST /module/:id/text =&amp;gt; publish new version of text&lt;/li&gt;
  &lt;li&gt;POST /module/:id/review =&amp;gt; review a module&lt;/li&gt;
  &lt;li&gt;POST /module/:id/version =&amp;gt; publish new version module&lt;/li&gt;
  &lt;li&gt;POST /module/:id/download =&amp;gt; user downloaded a module&lt;/li&gt;
  &lt;li&gt;GET /module =&amp;gt; get all modules with pagination&lt;/li&gt;
  &lt;li&gt;GET /user/:id/module =&amp;gt; get modules created by a user&lt;/li&gt;
  &lt;li&gt;GET /module/:id =&amp;gt; get a module by ID&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;POST /script =&amp;gt; create a script&lt;/li&gt;
  &lt;li&gt;PATCH /script/:id =&amp;gt; modify a script&lt;/li&gt;
  &lt;li&gt;DELETE /script/:id =&amp;gt; delete a script&lt;/li&gt;
  &lt;li&gt;GET /script =&amp;gt; get list of script&lt;/li&gt;
  &lt;li&gt;GET /user/:id/script =&amp;gt; get scripts created by user&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;POST /sentence =&amp;gt; create a sentence&lt;/li&gt;
  &lt;li&gt;POST /sentence/:id/vote =&amp;gt; vote for a sentence&lt;/li&gt;
  &lt;li&gt;DELETE /sentence/:id =&amp;gt; remove a published sentence&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I’m going to implement an admin dashboard to accept/reject published module. I don’t have currently any dashboard and it’s really annoying to manually accept module in the database.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;POST /module/:id/accept =&amp;gt; accept a module&lt;/li&gt;
  &lt;li&gt;POST /module/:id/reject =&amp;gt; reject a module&lt;/li&gt;
  &lt;li&gt;POST /module_text/:id/accept =&amp;gt; accept a new version of the text&lt;/li&gt;
  &lt;li&gt;POST /module_text/:id/reject =&amp;gt; reject a new version of the text&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;image-uploading&quot;&gt;Image uploading&lt;/h2&gt;

&lt;p&gt;To handle image upload, there are different options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Upload image to the server and the server is responsible for serving all images&lt;/li&gt;
  &lt;li&gt;Upload image to the server, which sends the image to a cloud file provider (Like Amazon S3, Google Cloud Storage or Microsoft Blob Storage)&lt;/li&gt;
  &lt;li&gt;Upload directly the image to a cloud file provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first option is clearly not the best for us. We want something scalable, easy to deploy and easy to migrate on another server if we want to upgrade the server. The other problem is that storage is not illimited!&lt;/p&gt;

&lt;p&gt;Second option seems better, but still, if we have lots of users uploading at the same time, the back-end will be busy working with files transfer: That’s clearly not his job.&lt;/p&gt;

&lt;p&gt;I’m going to pick the third option. All the heavy work is going to be done by our cloud provider, not our back-end. In our case, it’s going to be an Amazon S3 bucket.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;But, does it means the client uploads what he wants in our S3 bucket ??&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No, of course no. Before uploading, the client just need to ask our back-end for a pre-signed URL. It’s an URL that allows the client to upload only in a specific place in our S3 bucket, during a limited time.&lt;/p&gt;

&lt;p&gt;That means that the user cannot upload what he wants, when he wants, where he wants. But still, the user is uploading directly on Amazon S3. No extra server load on our side :)&lt;/p&gt;

&lt;p&gt;So, new route on our back-end:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;POST /module/image/upload =&amp;gt; will return a pre-signed URL for direct upload to S3&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;development&quot;&gt;Development&lt;/h2&gt;

&lt;h3 id=&quot;file-structure&quot;&gt;File structure&lt;/h3&gt;

&lt;p&gt;This is what our back-end file structure will looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-- core
---- api
------ user
-------- controller
---------- user.signup.js
-------- model
---------- user.create.js
----- service
-- index.js
-- package.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I prefer organizing my back-end by entity (user, module) with inside both controllers and models,
rather than doing the opposite (controllers and models, with inside ‘user’, ‘module’). 
It’s much more clear, and easier when you develop, because all files you need are just near. And when your app is becoming bigger, it’s still easy to find a file.&lt;/p&gt;

&lt;h3 id=&quot;password-hashing-and-authentification&quot;&gt;Password hashing and authentification&lt;/h3&gt;

&lt;p&gt;To hash password, I’m going to use &lt;a href=&quot;https://www.npmjs.com/package/bcrypt&quot;&gt;bcrypt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For authentification, we want a stateless way of authenticating users. We are going to use &lt;a href=&quot;https://jwt.io&quot;&gt;jsonwebtoken&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What is a jsonwebtoken ?&lt;/p&gt;

&lt;p&gt;It’s an encoded token composed of three parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A header, specifying the algorithm used. Example:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;A payload, which contains the data we wants. Example:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;sub&quot;: &quot;ce95683c-e682-4bcd-a18d-2e3d250aad48&quot;,
  &quot;name&quot;: &quot;John Doe&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;A signature. The signature is simply generated like this: 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HMACSHA256(base64UrlEncode(header) + &quot;.&quot; + base64UrlEncode(payload), secret)&lt;/code&gt;. Only the back-end is able to generate this signature because the HMACSHA256 function takes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret&lt;/code&gt;. And only the back-end knows the secret!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The user logs in with a POST /login request on our back-end&lt;/li&gt;
  &lt;li&gt;We generate a JWT with the user information inside, signs the JWT and return the token to the user&lt;/li&gt;
  &lt;li&gt;Each time the client sends an HTTP request to the back-end, he adds an “Authorization” header containing the JWT&lt;/li&gt;
  &lt;li&gt;The back-end reads the JWT, and verify that the signature is correct. If yes, it means that the JWT is valid, and it’s the right user who is making this request. We let him access the ressource he wants.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Does it means that the user can log in as any user just by changing the data inside the payload ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No, because if the user changes the payload, the signature is not valid anymore, and the back-end will reject the JWT.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What about expiration ? Does it means the token is valid forever ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good question, of course no! The JWT specification allows us to set a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt; attribute inside the payload. For example, we can say that a token is valid for 2 days. We will put inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt; attribute the timestamp of today + 2 days. When the user in 2 days will try to send a request, the back-end will open the JWT, see that the JWT is no longer valid, and reject the user. The user will need to log in again.&lt;/p&gt;

&lt;h3 id=&quot;data-validation&quot;&gt;Data validation&lt;/h3&gt;

&lt;p&gt;To validate data, I’m going to use &lt;a href=&quot;https://github.com/hapijs/joi&quot;&gt;Joi&lt;/a&gt;, it’s an awesome NPM package that allows us to validate JSON according to a defined schema. For example, for our user, I defined the schema as this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Joi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;joi&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Joi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Joi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Joi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Joi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;The email, password and name field are required.&lt;/li&gt;
  &lt;li&gt;Password should be minimum 6 characters&lt;/li&gt;
  &lt;li&gt;Email should be an email&lt;/li&gt;
  &lt;li&gt;Name should be a token, like on Twitter, you can’t have spaces in your username, only letters, numbers and special characters like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, in my model, when I’m creating a user, I just have to do that:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// params contains the body sent by the user&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// schema is Joi schema defined before&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// stripUnknown: true means that we want to remove other field that are not in the schema&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// example: if the user put a `lastname` attribute that is not in the schema, it will be cleaned&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stripUnknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// create user&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;db-requests&quot;&gt;DB requests&lt;/h3&gt;

&lt;p&gt;For database requests, we have mainly two options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We can use an ORM&lt;/li&gt;
  &lt;li&gt;We can write raw SQL queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having an ORM saves you time, but performance are not that great, and if you want to write specific query, you won’t be able to do it with the ORM, you will need to go back to SQL.&lt;/p&gt;

&lt;p&gt;I’m not going to use an ORM for all SELECT requests, for performance reason mainly.&lt;/p&gt;

&lt;p&gt;For insert/update requests, the problem is that attributes are not all required, and we don’t want to hand-generate all types of SQL request. For this, I will use &lt;a href=&quot;https://hiddentao.com/squel/&quot;&gt;squel&lt;/a&gt;, a SQL query builder which supports PostgreSQL.&lt;/p&gt;

&lt;p&gt;We have the best of both worlds:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;SELECT request never changes, so they are all written in SQL for maximum performance&lt;/li&gt;
  &lt;li&gt;INSERT/UPDATE are going to be generated by squel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;logging&quot;&gt;Logging&lt;/h3&gt;

&lt;p&gt;Logging is really important. You can’t know if your system is broken if you don’t have any logs.&lt;/p&gt;

&lt;p&gt;The thing is that you can’t browse all logs just by hand, it takes too much times. And if something is broken, you need alerts!&lt;/p&gt;

&lt;p&gt;Here, two options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Host your own logging solution (Like an ELK stack)&lt;/li&gt;
  &lt;li&gt;Use a cloud log provider (Like Sentry, Loggly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here I’m going to use the second solution, as it takes time to host my own logging platform. I don’t know yet which provider I’m going to use.&lt;/p&gt;

&lt;h3 id=&quot;emails&quot;&gt;Emails&lt;/h3&gt;

&lt;p&gt;Sending transactional emails (Confirmation email, reset password) is a serious job if you don’t want to fall in the SPAM folder of your user. I’m going to use &lt;a href=&quot;https://www.mailgun.com/&quot;&gt;Mailgun&lt;/a&gt; that I was already using before. Email are delivered correctly, and not that expensive (First 10 000/months are free, then it’s $0.00050 per email, so 20 000 emails = 5$, cheeaap)&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;I’m working hard on this new platform, and the back-end is in good way! Don’t hesitate to get a look on the code on the &lt;a href=&quot;https://github.com/GladysAssistant/dev-plaftorm-backend&quot;&gt;GitHub repository&lt;/a&gt;. Yes, this platform is open-source!&lt;/p&gt;

&lt;p&gt;I hope this article was clear, don’t hesitate to ask question in comments :)&lt;/p&gt;

&lt;p&gt;Have a nice week-end!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary of this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/22/refactoring-gladys-developer-website.html&quot;&gt;Refactoring Gladys Developer Platform (Part 1)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/25/database-design-with-postgresql.html&quot;&gt;Database Design with PostgreSQL (Part 2)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/30/building-rest-api-using-node-js.html&quot;&gt;Building a powerful REST API with Node.js (Part 3)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Setting up Unit testing, Continuous Integration and Deployment (Part 4) (Coming soon)&lt;/li&gt;
  &lt;li&gt;Leveraging caching with Redis (Part 5) (Coming soon)&lt;/li&gt;
  &lt;li&gt;Scheduling job with RabbitMQ (Part 6) (Coming soon)&lt;/li&gt;
  &lt;li&gt;A front-end in React/Redux (Part 7) (Coming soon)&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Database Design with PostgreSQL (Part 2)</title>
   <link href="https://pierregillesleymarie.com/blog/gladys/2017/04/25/database-design-with-postgresql.html"/>
   <updated>2017-04-25T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/gladys/2017/04/25/database-design-with-postgresql</id>
   <content type="html">&lt;p&gt;Hi, everyone!&lt;/p&gt;

&lt;p&gt;This is the second article of my series “Refactoring Gladys Developer Platform”. You can read the first article &lt;a href=&quot;/blog/gladys/2017/04/22/refactoring-gladys-developer-website.html&quot;&gt;here&lt;/a&gt;. The goal is to blog on how I’m building a fast and scalable back-end using Node.js, PostgreSQL, Redis and Nginx. We are going here to speak about database design.&lt;/p&gt;

&lt;h2 id=&quot;why-postgresql&quot;&gt;Why PostgreSQL?&lt;/h2&gt;

&lt;p&gt;It’s a complete ACID compliant database that offers many features and great performance. It’s able to handle very large amount of data, and you can probably design any database you want with it.&lt;/p&gt;

&lt;h3 id=&quot;things-i-love-with-postgresql&quot;&gt;Things I love with PostgreSQL&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Uuid type :&lt;/strong&gt; PostgreSQL supports uuid types, this means you can uses uuid as primary keys without having to generate and validate them on your backend side.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Full-text search:&lt;/strong&gt; You want fuzzy matching, multiple languages, stemming in search? PostgreSQL does that.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Spatial and Geographic objects:&lt;/strong&gt; You are building the next Uber? You want to store geographical data in your DB? And query them? PostGis provides out of the box methods to calculate distances between points and lots of tools to query geographical data.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;JSONB:&lt;/strong&gt; Since version 9.2, PostgreSQL is to able to store JSON with the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSONB&lt;/code&gt; type. You can query inside the JSON with great performance. The goal is not to replace your relational database, it’s just that sometimes you need to store documents or JSON data that you don’t want to split into tables for maximum performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read more:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://renesd.blogspot.fr/2017/02/is-postgresql-good-enough.html&quot;&gt;Is PostgreSQL good enough?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rachbelaid.com/postgres-full-text-search-is-good-enough/&quot;&gt;Postgres full-text search is Good Enough!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.codeship.com/unleash-the-power-of-storing-json-in-postgres/&quot;&gt;Unleash the Power of Storing JSON in Postgres&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;building-our-database&quot;&gt;Building our database&lt;/h2&gt;

&lt;h3 id=&quot;data-model&quot;&gt;Data Model&lt;/h3&gt;

&lt;p&gt;I’ve already talked about it in my last article, here is what the database is going to look like:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/2017-04-22-refactoring-gladys-developer-website/data-model.png&quot;&gt;&lt;img src=&quot;/assets/img/2017-04-22-refactoring-gladys-developer-website/data-model.png&quot; alt=&quot;Gladys Developer Platform Data Model&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;tables&quot;&gt;Tables&lt;/h3&gt;

&lt;p&gt;Because of all restricted keywords in PostgreSQL (user, group…), I took the habit to prefix all tables with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t_&lt;/code&gt; to prevent any issues. So I’m going to create theses tables:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;t_user
t_instance
t_module
t_module_text
t_module_version
t_module_download
t_module_review
t_script
t_sentence
t_sentence_vote
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is the final create table script :&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;active&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;last_connected&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;online&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;min_gladys_version&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;max_gladys_version&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;language&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;instruction_html&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;instruction_markdown&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;online&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_text_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_version_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;node_version&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;precision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;precision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;city&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;zipcode&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;first_seen&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;last_seen&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_instance_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;module_version_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;instance_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;network_hash&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_script&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;instruction_html&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;instruction_markdown&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;online&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_script&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_script_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sentence_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vote&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_gladys_version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uuid_generate_v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;character&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varying&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_gladys_version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_gladys_version_pkey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, uuid are created automatically by PostgreSQL if not provided by the backend.&lt;/p&gt;

&lt;p&gt;I’m putting a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_deleted&lt;/code&gt; boolean column on each table to avoid to hard delete rows in the DB from the back-end. We don’t want unexpected data loss, and here the back-end just has to update a row with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_deleted&lt;/code&gt; to true when something needs to be deleted. It can be restored if a mistake was made.&lt;/p&gt;

&lt;h3 id=&quot;foreign-keys&quot;&gt;Foreign Keys&lt;/h3&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module__user_id_t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_text&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module_text__module_id_t_module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_version&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module_version__module_id_t_module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_instance&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_instance__user_id_t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module_download__module_version_id_t_module_version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_version_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module_download__instance_id_t_instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module_review__module_id_t_module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_module_review__user_id_t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_script&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_script__user_id_t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_sentence__user_id_t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_sentence_vote_user_id_t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ONLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONSTRAINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fk_t_sentence_vote_sentence_id_t_sentence&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentence_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For those who don’t know foreign keys, the goal is to ensure that if I insert a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t_module&lt;/code&gt; in the database with a given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_id&lt;/code&gt;, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_id&lt;/code&gt; actually refers to an existing user uuid in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t_user&lt;/code&gt; table.&lt;/p&gt;

&lt;h3 id=&quot;indexes&quot;&gt;Indexes&lt;/h3&gt;

&lt;p&gt;Indexes are really important. They can really improve the performance of your database.&lt;/p&gt;

&lt;p&gt;I create indexes on all columns that are used to perform JOIN operations, ORDER BY and WHERE.&lt;/p&gt;

&lt;p&gt;For the moment, I’m going to create these indexes:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_user_email&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_text_module_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_version_module_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_instance_user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_download_instance_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_download_module_version_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_download&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_version_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_review_user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_module_review_module_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_module_review&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_script_user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_script&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_sentence_user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_sentence_vote_user_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_sentence_vote_sentence_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sentence_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_gladys_version_created_at&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_gladys_version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;unique-indexes&quot;&gt;Unique Indexes&lt;/h3&gt;

&lt;p&gt;In this database, I have several relation tables. For example, a user is able to vote for a sentence.
I don’t want a user to vote 2 times for the same sentence, so I’m going to put a unique index on the two columns (user_id, sentence_id) of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t_sentence_vote&lt;/code&gt; table.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;--- No duplicate (user_id, sentence_id) row in t_sentence_vote table&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--- Prevent a user from voting two times for the same sentence&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;UNIQUE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix_t_sentence_vote_sentence_id_user_id_unique&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_sentence_vote&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sentence_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_deleted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, it’s only a partial index on a subset of the table: Only rows where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_deleted&lt;/code&gt; = false (because a user can vote several times if his vote is deleted).&lt;/p&gt;

&lt;h2 id=&quot;running-postgresql-inside-docker&quot;&gt;Running PostgreSQL inside Docker&lt;/h2&gt;

&lt;h3 id=&quot;dockerfile&quot;&gt;Dockerfile&lt;/h3&gt;

&lt;p&gt;First, I’m saving all the SQL I need to init my DB in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.sql&lt;/code&gt; file. I just need to create a Dockerfile with the following content:&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; postgres:9.6-alpine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; init.sql /docker-entrypoint-initdb.d/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I start from the postgres 9.6 alpine image. Alpine is a lightweight Linux distribution that is used to create minimal Docker images. Perfect for us!&lt;/p&gt;

&lt;p&gt;I add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.sql&lt;/code&gt; file inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/docker-entrypoint-initdb.d&lt;/code&gt; folder so that the SQL file will be executed at first startup of the container.&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;We now have our database! I hope you enjoyed this article, don’t hesitate if you have any feedbacks. In the next article, I’m going to talk about the development of the REST API of the new dev platform.&lt;/p&gt;

&lt;p&gt;All of my code is on &lt;a href=&quot;https://github.com/GladysAssistant/dev-plaftorm-backend&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary of this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/22/refactoring-gladys-developer-website.html&quot;&gt;Refactoring Gladys Developer Platform (Part 1)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/25/database-design-with-postgresql.html&quot;&gt;Database Design with PostgreSQL (Part 2)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/30/building-rest-api-using-node-js.html&quot;&gt;Building a powerful REST API with Node.js (Part 3)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Setting up Unit testing, Continuous Integration and Deployment (Part 4) (Coming soon)&lt;/li&gt;
  &lt;li&gt;Leveraging caching with Redis (Part 5) (Coming soon)&lt;/li&gt;
  &lt;li&gt;Scheduling job with RabbitMQ (Part 6) (Coming soon)&lt;/li&gt;
  &lt;li&gt;A front-end in React/Redux (Part 7) (Coming soon)&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Refactoring Gladys Developer Platform (Part 1)</title>
   <link href="https://pierregillesleymarie.com/blog/gladys/2017/04/22/refactoring-gladys-developer-website.html"/>
   <updated>2017-04-22T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/gladys/2017/04/22/refactoring-gladys-developer-website</id>
   <content type="html">&lt;p&gt;Hi, everyone!&lt;/p&gt;

&lt;p&gt;The Gladys developer website was launched two years ago in 2015. The goal was to build a community of developer around Gladys. Today, the website is not as sexy as it was in 2015, and not that easy to use. I’ve been thinking about it a lot, and I’ve tried to see how I can transform this simple website into a real platform that can make the project move forward 🚀🚀.&lt;/p&gt;

&lt;h2 id=&quot;the-goal-of-the-new-platform&quot;&gt;The goal of the new platform&lt;/h2&gt;

&lt;h3 id=&quot;rethinking-gladys-main-website&quot;&gt;Rethinking Gladys main website&lt;/h3&gt;

&lt;p&gt;You all know that we have currently two websites on the Gladys Assistant:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gladysassistant.com&quot;&gt;https://gladysassistant.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.gladysassistant.com&quot;&gt;https://developer.gladysassistant.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is that today these websites are not well organized. The main website explains how to install Gladys, but the documentation is on the developer website. Modules are only displayed on Gladys developer website. Compatible devices are displayed on both websites. It’s a mess!&lt;/p&gt;

&lt;p&gt;I really think all information about installing and configuring Gladys should be on the main domain.&lt;/p&gt;

&lt;p&gt;The developer platform should only be about:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Creating a developer account&lt;/li&gt;
  &lt;li&gt;Publishing a module&lt;/li&gt;
  &lt;li&gt;Releasing new version of a module&lt;/li&gt;
  &lt;li&gt;Sharing scripts&lt;/li&gt;
  &lt;li&gt;Adding new sentences for Gladys’ brain&lt;/li&gt;
  &lt;li&gt;Voting for other people sentences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h3 id=&quot;open-api&quot;&gt;Open API&lt;/h3&gt;

&lt;p&gt;You know that in Gladys it’s possible to install modules directly from the dashboard. It means that the developer platform is not only a website, it’s a real open API that communicates with all Gladys instances.&lt;/p&gt;

&lt;h3 id=&quot;statistics&quot;&gt;Statistics&lt;/h3&gt;

&lt;p&gt;Today, I don’t have any insights about how many Gladys instances are running. I don’t know if they are running on Linux, if they are running on Windows, on MacOS. I don’t know which version of Node.js they use. I don’t know in which countries are my users!&lt;/p&gt;

&lt;p&gt;I can only rely on the main website analytics to understand best my users.&lt;/p&gt;

&lt;p&gt;I think that this platform is the best opportunity to calculate all that. The goal is not to track sensitive data about you, no, not at all. I don’t care! The goal is just to have interesting data like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;90% of Gladys instances are running on Linux&lt;/li&gt;
  &lt;li&gt;50% of Gladys users are in France&lt;/li&gt;
  &lt;li&gt;80% of Gladys instances runs on Node.js 4.x.x&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s all!&lt;/p&gt;

&lt;p&gt;To achieve that, Gladys is going, if you accept, to send this additional set of data in the Gladys update request everything 24 hours (Each 24 hours, Gladys check if there are some updates to download).&lt;/p&gt;

&lt;h2 id=&quot;data-model&quot;&gt;Data model&lt;/h2&gt;

&lt;p&gt;I’ve adapted the current data model of the developer website to fit our needs for this awesome platform!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/2017-04-22-refactoring-gladys-developer-website/data-model.png&quot;&gt;&lt;img src=&quot;/assets/img/2017-04-22-refactoring-gladys-developer-website/data-model.png&quot; alt=&quot;Gladys Developer Platform Data Model&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Click on the image to make it bigger)&lt;/p&gt;

&lt;p&gt;To explain a little bit more:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;User:&lt;/strong&gt; An account on the developer platform.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Module:&lt;/strong&gt; A module created by a user. A module has several version, several texts and several reviews given by users.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sentence:&lt;/strong&gt; A user can submit directly on Gladys new sentence to train Gladys’ brain. These sentences can be upvoted by the community.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Script:&lt;/strong&gt; Users can share scripts on the platform.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Instance:&lt;/strong&gt; A Gladys instance. It’s not mandatory that an instance is linked to a user, it’s anonymous by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;to-conclude&quot;&gt;To conclude&lt;/h2&gt;

&lt;p&gt;I’m going to work on this and explain on this blog in real-time how I’m implementing this in terms of architecture, database, and deployment.&lt;/p&gt;

&lt;p&gt;The goal is not only to build a faster and more awesome platform, it’s also to share my passion for back-end architecture 🙂&lt;/p&gt;

&lt;p&gt;This article is the beginning of series of 7 articles. I hope you will all enjoy it!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary of this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/22/refactoring-gladys-developer-website.html&quot;&gt;Refactoring Gladys Developer Platform (Part 1)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/25/database-design-with-postgresql.html&quot;&gt;Database Design with PostgreSQL (Part 2)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/gladys/2017/04/30/building-rest-api-using-node-js.html&quot;&gt;Building a powerful REST API with Node.js (Part 3)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Setting up Unit testing, Continuous Integration and Deployment (Part 4) (Coming soon)&lt;/li&gt;
  &lt;li&gt;Leveraging caching with Redis (Part 5) (Coming soon)&lt;/li&gt;
  &lt;li&gt;Scheduling job with RabbitMQ (Part 6) (Coming soon)&lt;/li&gt;
  &lt;li&gt;A front-end in React/Redux (Part 7) (Coming soon)&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>A brand new blog</title>
   <link href="https://pierregillesleymarie.com/blog/2017/04/22/a-brand-new-blog.html"/>
   <updated>2017-04-22T00:00:00+00:00</updated>
   <id>https://pierregillesleymarie.com/blog/2017/04/22/a-brand-new-blog</id>
   <content type="html">&lt;p&gt;Hi, everyone!&lt;/p&gt;

&lt;p&gt;After more than 3 years of blogging about home automation on &lt;a href=&quot;https://gladysassistant.com&quot;&gt;Gladys Assistant&lt;/a&gt;, I’ve decided to start a more personal blog in parallel.&lt;/p&gt;

&lt;p&gt;The goal of this blog is to talk about things I can’t talk on Gladys blog because it’s too technical/too personal.&lt;/p&gt;

&lt;p&gt;On this blog, I’m going to talk about two kinds of things, first is mostly about what I do on Gladys:&lt;/p&gt;

&lt;h3 id=&quot;engineering&quot;&gt;Engineering&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Back-end Development (Mostly around Node.js &amp;amp; PostgreSQL)&lt;/li&gt;
  &lt;li&gt;Performance Optimization&lt;/li&gt;
  &lt;li&gt;Code Quality/Unit Testing&lt;/li&gt;
  &lt;li&gt;Continuous Integration/Deployment at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;random-things&quot;&gt;Random Things&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;How I work, How I organize myself&lt;/li&gt;
  &lt;li&gt;Hardware reviews&lt;/li&gt;
  &lt;li&gt;Marketing tools I use on Gladys Assistant&lt;/li&gt;
  &lt;li&gt;Trip :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t be only blogging about technical stuff as you can see, it’s really a personal and opinionated blog about things I do in my life.&lt;/p&gt;

&lt;p&gt;If you want to receive the latest news about this blog, don’t hesitate to subscribe to &lt;a href=&quot;http://eepurl.com/cL9xhf&quot;&gt;my newsletter&lt;/a&gt;, I won’t spam you don’t worry, I will just send an email when an article is out (And you can unsubscribe at any time of course 🙂). You can also follow me on &lt;a href=&quot;https://twitter.com/pierregillesl&quot;&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;See you in my next article!&lt;/p&gt;
</content>
 </entry>
 
 
</feed>
