<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Yarmo - selfhosting</title>
    <subtitle>Blog of an Open Source developer</subtitle>
    <link rel="self" type="application/atom+xml" href="https://yarmo.eu/tags/selfhosting/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://yarmo.eu"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2022-06-11T10:37:13+00:00</updated>
    <id>https://yarmo.eu/tags/selfhosting/atom.xml</id>
    <entry xml:lang="en">
        <title>Matrix Synapse: migrating from Cloudron to ansible</title>
        <published>2022-06-11T10:37:13+00:00</published>
        <updated>2022-06-11T10:37:13+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/synapse-cloudron-ansible/"/>
        <id>https://yarmo.eu/blog/synapse-cloudron-ansible/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/synapse-cloudron-ansible/">&lt;p&gt;Maybe, like me, you tried to selfhost a &lt;a href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;docs&#x2F;projects&#x2F;server&#x2F;synapse&quot;&gt;Matrix Synapse&lt;&#x2F;a&gt; server, miserably failed because it&#x27;s just not quite that easy and then settled for &lt;a href=&quot;https:&#x2F;&#x2F;docs.cloudron.io&#x2F;apps&#x2F;synapse&#x2F;&quot;&gt;Cloudron&#x27;s Synapse app&lt;&#x2F;a&gt; because it just works. Pay a bit more, worry a little less.&lt;&#x2F;p&gt;
&lt;p&gt;Sure, it works but you still introduced a middle man in your pristine homelab setup and the itch never goes away to get rid of it.&lt;&#x2F;p&gt;
&lt;p&gt;Time for round 2.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;The_ansible_playbook&quot;&gt;The ansible playbook&lt;&#x2F;h2&gt;
&lt;p&gt;The last few years, I have been slowly learning how to work with &lt;a href=&quot;https:&#x2F;&#x2F;www.ansible.com&#x2F;&quot;&gt;ansible&lt;&#x2F;a&gt; and ever since I found the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&quot;&gt;matrix-docker-ansible-deploy&lt;&#x2F;a&gt; project, I knew this would be my chance for redemption.&lt;&#x2F;p&gt;
&lt;p&gt;So, I found myself a spare machine (SBC, NUC, VPS, all should work), installed Debian 11, enabled SSH and went to work. Here&#x27;s what I did to finally make the Cloudron migration happen.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Decommissioning_Cloudron&quot;&gt;Decommissioning Cloudron&lt;&#x2F;h2&gt;
&lt;p&gt;!Read through the whole guide first! Understanding why you do certain things will help you do those things correctly or do them do differently: just because I did them a certain way doesn&#x27;t mean it&#x27;s the best way for everyone.&lt;&#x2F;p&gt;
&lt;p&gt;First, I moved some important files from the Cloudron server to my new server. There are two folders of interest, both can be obtained by visiting the Cloudron admin panel, going to the settings for the Matrix-Synapse app and clicking on &lt;code&gt;Storage&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;code&gt;&#x2F;home&#x2F;yellowtent&#x2F;appsdata&#x2F;&amp;lt;APP-ID&amp;gt;&lt;&#x2F;code&gt; folder, you&#x27;ll find the &lt;code&gt;postgresqldump&lt;&#x2F;code&gt; file that was generated during the last Cloudron app backup — so make sure to run a backup right before migrating to have the latest data!&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;code&gt;&#x2F;mnt&#x2F;data&#x2F;apps&#x2F;&amp;lt;APP-NAME&amp;gt;&#x2F;data&lt;&#x2F;code&gt; folder, you&#x27;ll find the all-importand &lt;code&gt;media_store&lt;&#x2F;code&gt; folder.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, in the &lt;code&gt;&#x2F;mnt&#x2F;data&#x2F;apps&#x2F;&amp;lt;APP-NAME&amp;gt;&#x2F;configs&lt;&#x2F;code&gt; folder, you will find the &lt;code&gt;homeserver.yaml&lt;&#x2F;code&gt; config file for the Matrix Synapse server — use this for inspiration for the new one but more importantly, make a note of the &lt;code&gt;database&#x2F;args&#x2F;user&lt;&#x2F;code&gt; value. There is also the &lt;code&gt;signing.key&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;Using your method of choice (rsync, wormhole…), copy the &lt;code&gt;postgresqldump&lt;&#x2F;code&gt; file, the &lt;code&gt;media_store&lt;&#x2F;code&gt; folder and the &lt;code&gt;signing.key&lt;&#x2F;code&gt; over to the new server in — for example — the &lt;code&gt;&#x2F;migration&lt;&#x2F;code&gt; folder.&lt;&#x2F;p&gt;
&lt;p&gt;This is the moment to power down the Cloudron machine (or simply the app if you wish to keep Cloudron running) and update the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;configuring-dns.md&quot;&gt;DNS records&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Fixing_the_database_dump&quot;&gt;Fixing the database dump&lt;&#x2F;h2&gt;
&lt;p&gt;Before we touch ansible, we need to go our &lt;code&gt;postgresqldump&lt;&#x2F;code&gt; file and replace all instances of the previous database user (the one you found in the &lt;code&gt;homeserver.yaml&lt;&#x2F;code&gt; on the Cloudron server, it should look like &lt;code&gt;user1a2b3c4d5e6f7g8h9i&lt;&#x2F;code&gt;) with &lt;code&gt;matrix&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;importing-postgres.md&quot;&gt;Importing an existing Postgres database&lt;&#x2F;a&gt;, this should be &lt;code&gt;synapse&lt;&#x2F;code&gt; and not &lt;code&gt;matrix&lt;&#x2F;code&gt;. I ran into permission issues when using &lt;code&gt;synapse&lt;&#x2F;code&gt; but that may have been due to a configuration error I made elsewhere. Feel free to attempt with &lt;code&gt;synapse&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Setting_up_the_new_server_with_ansible&quot;&gt;Setting up the new server with ansible&lt;&#x2F;h2&gt;
&lt;p&gt;I am going to skip the majority of the ansible process here, namely the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;configuring-playbook.md&quot;&gt;Configuring the Ansible playbook&lt;&#x2F;a&gt; part and only focus on what is relevant when migrating from a Cloudron instance. There is a special &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;importing-postgres.md&quot;&gt;Importing an existing Postgres database&lt;&#x2F;a&gt; but I had to change the steps a bit to make them work for me.&lt;&#x2F;p&gt;
&lt;p&gt;I followed the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;configuring-playbook.md&quot;&gt;Configuring the Ansible playbook&lt;&#x2F;a&gt; steps to obtain my ansible configuration file.&lt;&#x2F;p&gt;
&lt;p&gt;In the end, I added to &lt;code&gt;vars.yml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-yml &quot;&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# Set up synapse database connection
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;matrix_synapse_database_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;matrix&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;matrix_synapse_database_password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;SUPERSECRETPASSWORD&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;matrix_synapse_database_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;matrix&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# Set up postgres
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;matrix_postgres_connection_username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;matrix&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;matrix_postgres_connection_password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;SUPERSECRETPASSWORD&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;matrix_postgres_db_name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;matrix&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you chose in the previous section to try and import the database dump into the &lt;code&gt;synapse&lt;&#x2F;code&gt; database instead of the &lt;code&gt;matrix&lt;&#x2F;code&gt;, make sure to update those values here.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s let ansible set up everything on the server:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; inventory&#x2F;hosts setup.yml&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --tags&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;setup-all&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -K
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Do not start just yet! First, let&#x27;s import the database dump:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; inventory&#x2F;hosts setup.yml &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --extra-vars&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;server_path_postgres_dump=&#x2F;migration&#x2F;postgresqldump&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --tags&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;import-postgres&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -K
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If there weren&#x27;t any errors, let&#x27;s import the &lt;code&gt;media_store&lt;&#x2F;code&gt; folder as well:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; inventory&#x2F;hosts setup.yml &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --extra-vars&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;server_path_media_store=&#x2F;migration&#x2F;media_store&#x2F;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --tags&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;import-synapse-media-store&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -K
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If still no errors, great! Now let&#x27;s take a look at that signing key we copied over. What I should have done was follow the instructions &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;issues&#x2F;738&quot;&gt;here&lt;&#x2F;a&gt; to add the previous signing key to the list of old signing keys. But I didn&#x27;t know of these instructions when performing the setup, so I simply used the old key to overwrite the signing key ansible had generated and stored in &lt;code&gt;&#x2F;matrix&#x2F;synapse&#x2F;config&#x2F;matrix.your.domain.signing.key&lt;&#x2F;code&gt;. Not as elegant, I admit, but it works.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s time to start the server (and run the setup again for the new signing key):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; inventory&#x2F;hosts setup.yml&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --tags&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;setup-all,start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -K
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Done!&lt;&#x2F;p&gt;
&lt;p&gt;Again, I skipped a lot of important steps like setting up a reverse proxy — this playbook includes &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;configuring-playbook-nginx.md&quot;&gt;Nginx&lt;&#x2F;a&gt; — so make sure to read &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;tree&#x2F;master&#x2F;docs&quot;&gt;all the documentation&lt;&#x2F;a&gt; to end up with a fully functional instance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Something_went_wrong&quot;&gt;Something went wrong&lt;&#x2F;h3&gt;
&lt;p&gt;Surely, something went wrong in one of the steps above, it happens.&lt;&#x2F;p&gt;
&lt;p&gt;If something went wrong during the importing of the &lt;code&gt;postgresqldump&lt;&#x2F;code&gt;, you can&#x27;t just repeat the step as postgres will now complain that some import steps were already performed (see the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spantaleev&#x2F;matrix-docker-ansible-deploy&#x2F;blob&#x2F;master&#x2F;docs&#x2F;importing-postgres.md&quot;&gt;Importing an existing Postgres database&lt;&#x2F;a&gt; guide).&lt;&#x2F;p&gt;
&lt;p&gt;I followed the exact steps they propose. So, on the new server, run:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;systemctl&lt;&#x2F;span&gt;&lt;span&gt; stop matrix-postgres
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;rm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -rf&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;matrix&#x2F;postgres&#x2F;data&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;*
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;systemctl&lt;&#x2F;span&gt;&lt;span&gt; start matrix-postgres
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, back on the ansible controller, run:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; inventory&#x2F;hosts setup.yml &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --tags&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;setup-postgres&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -K
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You now have an empty database ready for a fresh import!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Checking_the_import_process_succeeded&quot;&gt;Checking the import process succeeded&lt;&#x2F;h3&gt;
&lt;p&gt;To check if the data was imported correctly, here&#x27;s how to log into the database and query the user table:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;&#x2F;usr&#x2F;local&#x2F;bin&#x2F;matrix-postgres-cli
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# List the databases
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#95e6cb;&quot;&gt;\l
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# Connect to the matrix database
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#95e6cb;&quot;&gt;\c &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;matrix
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# Query the users table
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; from users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If that last query returns the list of users you expect to see, we should be good! Well, almost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Resetting_passwords&quot;&gt;Resetting passwords&lt;&#x2F;h2&gt;
&lt;p&gt;For some explanation that is as of yet beyond me, the old passwords won&#x27;t work. That is, we haven&#x27;t changed the passwords during the import process, but we also cannot log in as the server will now complain the password is incorrect.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily, we can simply reset a user&#x27;s password to fix this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; inventory&#x2F;hosts setup.yml &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --extra-vars&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;username=USER password=SUPERSECRETPASSWORD&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;  --tags&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;update-user-password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -K
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This user can now log in again.&lt;&#x2F;p&gt;
&lt;p&gt;If like me, you host a Synapse server for a small number of people that trust you, this step is not an issue. If you host for a lot of people, I am not really sure how to proceed. Hopefully someone can help out here, explain what step I missed to make passwords work after the migration and I can update the guide accordingly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;There you have it, this is what I did to obtain a working fresh Synapse server with all the data from the Cloudron server. Cloudron was nice to work with but I am glad I have everything working again without the need for a middle man.&lt;&#x2F;p&gt;
&lt;p&gt;So now… dendrite when?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>InfluxDB 2 migration</title>
        <published>2022-05-20T18:36:41+00:00</published>
        <updated>2022-05-20T18:36:41+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/influxdb-2-migration/"/>
        <id>https://yarmo.eu/blog/influxdb-2-migration/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/influxdb-2-migration/">&lt;h2 id=&quot;What_is_InfluxDB?&quot;&gt;What is InfluxDB?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;&quot;&gt;InfluxDB&lt;&#x2F;a&gt; is the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;influxdata&#x2F;influxdb&#x2F;blob&#x2F;master&#x2F;LICENSE&quot;&gt;MIT licensed&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Time_series_database&quot;&gt;time series database&lt;&#x2F;a&gt; of my monitoring stack of choice, the so-called &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;blog&#x2F;introduction-to-influxdatas-influxdb-and-tick-stack&#x2F;&quot;&gt;TICK stack&lt;&#x2F;a&gt;, consisting of &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;time-series-platform&#x2F;telegraf&#x2F;&quot;&gt;Telegraf&lt;&#x2F;a&gt; (data collection agent), &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;&quot;&gt;InfluxDB&lt;&#x2F;a&gt; (time series database), &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;time-series-platform&#x2F;chronograf&#x2F;&quot;&gt;Chronograf&lt;&#x2F;a&gt; (charts and dashboard interface) and &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;time-series-platform&#x2F;kapacitor&#x2F;&quot;&gt;Kapacitor&lt;&#x2F;a&gt; (data processing engine).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;My_history_with_InfluxDB&quot;&gt;My history with InfluxDB&lt;&#x2F;h2&gt;
&lt;p&gt;I started my homelab somewhere in 2017-2018. When about a year in I faced some random crashes and reboots which I did not manage to understand or fix, I first turned to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;netdata&#x2F;netdata&quot;&gt;netdata&lt;&#x2F;a&gt; before settling with the TICK stack.&lt;&#x2F;p&gt;
&lt;p&gt;While I did eventually solve the random crashes, an obvious issue surfaced: if I want to monitor a crashing server, I probably shouldn&#x27;t host the monitoring stack on that same server.&lt;&#x2F;p&gt;
&lt;p&gt;And so, my setup eventually converged to the one I use today: a homelab and a few VPSs, all monitored by a TICK stack installed on a dedicated VPS.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.influxdata.com&#x2F;influxdb&#x2F;v2.0&#x2F;reference&#x2F;release-notes&#x2F;influxdb&#x2F;&quot;&gt;InfluxDB 2.0&lt;&#x2F;a&gt; was released in November of 2020 and I remember this moment clearly: I updated my InfluxDB docker container, I noticed everything immediately broke, I read up on what a gargantuan update this 2.0 release was, I said &amp;quot;nope&amp;quot; and immediately reverted to 1.8.x.&lt;&#x2F;p&gt;
&lt;p&gt;Yesterday, I decided it was finally time to sit down and calmly move to InfluxDB 2(.2.0).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;InfluxDB_2,_or_the_Death_of_the_TICK_stack&quot;&gt;InfluxDB 2, or the Death of the TICK stack&lt;&#x2F;h2&gt;
&lt;p&gt;Please refer &lt;a href=&quot;https:&#x2F;&#x2F;docs.influxdata.com&#x2F;influxdb&#x2F;v2.0&#x2F;reference&#x2F;release-notes&#x2F;influxdb&#x2F;&quot;&gt;release notes for InfluxDB 2&lt;&#x2F;a&gt; to see for yourself what has changed.&lt;&#x2F;p&gt;
&lt;p&gt;One of the most notable changes is that InfluxDB 2 now has a interface with graphs and dashboards (replacing Chronograf&#x27;s functionality) and can send alerts based on data (which previously required Kapacitor). While I am sure Chronograf and Kapacitor can still be used together with InfluxDB 2, they no longer need to and indeed, I have now removed these services from my monitoring stack.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;InfluxDB_2,_the_migration&quot;&gt;InfluxDB 2, the migration&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;_&#x2F;influxdb&quot;&gt;InfluxDB 2 dockerhub page&lt;&#x2F;a&gt; has clear instructions on how to migrate from 1.x to 2.x when using containers and so I did. And while the container logs showed the migration process was successful, I had this weird issue where when I would try to then log in on the web interface, I was greeted with a &amp;quot;fresh install&amp;quot; screen, suggesting the InfluxDB instance was in fact not aware of the migration.&lt;&#x2F;p&gt;
&lt;p&gt;And indeed, after following the instructions of the &amp;quot;fresh install&amp;quot; screen, everything was working fine but no trace of any migrated data :(&lt;&#x2F;p&gt;
&lt;p&gt;If my historical InfluxDB data was in any way valuable to me, I would have put more effort into migrating all of it to InfluxDB 2.&lt;&#x2F;p&gt;
&lt;p&gt;But it wasn&#x27;t.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;InfluxDB_2,_the_non-migration&quot;&gt;InfluxDB 2, the non-migration&lt;&#x2F;h2&gt;
&lt;p&gt;And thus, in a bit of an anticlimax to this whole situation, I &lt;code&gt;rm -rf&lt;&#x2F;code&gt;ed the whole data directory and started anew with a fresh InfluxDB 2 instance. No more Chronograf, no more Kapacitor. All servers have updated Telegraf configurations and I am currently rebuilding the dashboards in InfluxDB&#x27;s web interface.&lt;&#x2F;p&gt;
&lt;p&gt;And I also still need to rebuild my Grafana dashboards to make use of Flux instead of InfluxQL. I still have things to do before I can fully enjoy my monitoring solution again.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>playerctl: get currently playing music</title>
        <published>2022-05-03T14:35:49+00:00</published>
        <updated>2022-05-03T14:35:49+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/playerctl/"/>
        <id>https://yarmo.eu/blog/playerctl/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/playerctl/">&lt;h2 id=&quot;TLDR&quot;&gt;TLDR&lt;&#x2F;h2&gt;
&lt;p&gt;To get information about music currently playing on the computer, run:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;playerctl&lt;&#x2F;span&gt;&lt;span&gt; metadata&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --all-players --format &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;{{ status }}: {{ artist }} - {{ title }}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A wide variety of media players are MPRIS-enabled and can be queried using the above command, including media players running in the browser such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;airsonic-advanced&#x2F;airsonic-advanced&quot;&gt;Airsonic&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;man.archlinux.org&#x2F;man&#x2F;community&#x2F;playerctl&#x2F;playerctl.1.en&quot;&gt;playerctl on man.archlinux.org&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Explanation&quot;&gt;Explanation&lt;&#x2F;h2&gt;
&lt;p&gt;For a long time, I&#x27;ve been using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;airsonic-advanced&#x2F;airsonic-advanced&quot;&gt;Airsonic&lt;&#x2F;a&gt; as my media player, handy if you are into selfhosted services and despise streaming platforms with a passion.&lt;&#x2F;p&gt;
&lt;p&gt;Since I also do &lt;a href=&quot;https:&#x2F;&#x2F;yarmo.live&quot;&gt;live streaming&lt;&#x2F;a&gt; and like to play ambient music in the background, I attempted to build a little &amp;quot;Now playing&amp;quot; widget for on stream.&lt;&#x2F;p&gt;
&lt;p&gt;Sadly, no luck: Airsonic doesn&#x27;t have an API.&lt;&#x2F;p&gt;
&lt;p&gt;After a lot of trying out different clients and hosting additional services, I settled on syncing my music collection from the NAS to my computer and then playing music through mopidy, as I could then query the currently playing music using MPD:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# Snippet will be added soon, currently away from computer
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not my favorite solution but it works.&lt;&#x2F;p&gt;
&lt;p&gt;In comes this guy &lt;a href=&quot;https:&#x2F;&#x2F;deavid.wordpress.com&#x2F;&quot;&gt;DeavidSedice&lt;&#x2F;a&gt; and he tells me I can get that same information from most existing media players with a single line of bash:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# The command as he sent it to me
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;playerctl&lt;&#x2F;span&gt;&lt;span&gt; metadata&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --all-players --format &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;{{ status }}: {{ artist }} - {{ title }}               &amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;grep&lt;&#x2F;span&gt;&lt;span&gt; Playing &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;tr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;\n&amp;#39; &amp;#39; &amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why did I never hear about this before? A life changer! And it even works with Airsonic running in the browser.&lt;&#x2F;p&gt;
&lt;p&gt;This tool makes it quite trivial to write a bash script that loops the command, writes the output to a text file and have OBS display it on stream.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Traefik: managing both HTTP and HTTPS connections separately</title>
        <published>2020-07-28T00:00:00+00:00</published>
        <updated>2020-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/traefik-http-https/"/>
        <id>https://yarmo.eu/blog/traefik-http-https/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/traefik-http-https/">&lt;h2 id=&quot;Introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;If you have used the wonderful &lt;a href=&quot;https:&#x2F;&#x2F;containo.us&#x2F;traefik&#x2F;&quot;&gt;Traefik&lt;&#x2F;a&gt; before to route all your traffic to different docker containers or other services, you&#x27;ll know how easy it is to add SSL certificates and secure HTTPS connections, simple and free thanks to their integration with &lt;a href=&quot;https:&#x2F;&#x2F;letsencrypt.org&#x2F;&quot;&gt;Let&#x27;s Encrypt&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You will first need the following lines in the &lt;code&gt;traefik.toml&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;address &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;:80&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirections&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirections&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoint&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;to &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;websecure&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;scheme &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;https&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;websecure&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;address &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;:443&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To learn more about this setup using &lt;code&gt;traefik.yml&lt;&#x2F;code&gt; or the CLI, please check out the &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;https&#x2F;acme&#x2F;&quot;&gt;documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;What these lines do is make sure that Traefik listens to the correct ports, &lt;code&gt;80&lt;&#x2F;code&gt; for HTTP connections (or &lt;code&gt;web&lt;&#x2F;code&gt;) and &lt;code&gt;443&lt;&#x2F;code&gt; for HTTPS connections (or &lt;code&gt;websecure&lt;&#x2F;code&gt;). Because we only want secure connections coming from the internet, Traefik is instructed to always redirect &lt;code&gt;web&lt;&#x2F;code&gt; entrypoint connections to the &lt;code&gt;websecure&lt;&#x2F;code&gt; entrypoint. Easy and safe!&lt;&#x2F;p&gt;
&lt;p&gt;Now, let us get a SSL certificate and secure those HTTPS connections:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;certificatesResolvers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;myresolver&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;acme&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;test@example.com&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;storage &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;acme.json&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;certificatesResolvers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;myresolver&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;acme&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;httpChallenge&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoint &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;web&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Setting up the &lt;code&gt;certificateResolver&lt;&#x2F;code&gt; is only half the process, now you need to make sure your docker container uses it by appending a few labels to &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;mycontainer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;: someimage
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;- traefik&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;routers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;router0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rule&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;Host(`example.com`)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;- traefik&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;routers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;router0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;tls&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;- traefik&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;routers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;router0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;tls&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;certresolver&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;myresolver
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To learn more about this setup using swarm, kubernetes or rancher, please check out the &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;https&#x2F;acme&#x2F;&quot;&gt;documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t often get so much functionality out of a few lines of code: the service provided by &lt;code&gt;mycontainer&lt;&#x2F;code&gt;, whether it&#x27;s a &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;_&#x2F;nextcloud&quot;&gt;Nextcloud&lt;&#x2F;a&gt; container, a &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;wallabag&#x2F;wallabag&quot;&gt;Wallabag&lt;&#x2F;a&gt; container or any other service you might enjoy using, is now available by visiting example.com with HTTPS enabled automatically ensuring a secure connection now and in the future as Traefik manages the certificate renewal for you.&lt;&#x2F;p&gt;
&lt;p&gt;Once you start hosting 20, 30, maybe 40 containers and making them accessible to yourself, to friends and family or the entire world through the internet, Traefik really starts to make your self-hosting life a whole lot more enjoyable.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;The_problem:_home_connections&quot;&gt;The problem: home connections&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s one use-case where the &amp;quot;HTTPS everything&amp;quot; credo becomes counter-productive: services that should only be accessible via the home network.&lt;&#x2F;p&gt;
&lt;p&gt;In my home, I monitor a lot of services and devices using &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;time-series-platform&#x2F;telegraf&#x2F;&quot;&gt;Telegraf&lt;&#x2F;a&gt; which sends the metrics to an &lt;a href=&quot;https:&#x2F;&#x2F;www.influxdata.com&#x2F;&quot;&gt;InfluxDB&lt;&#x2F;a&gt; database to be analyzed by &lt;a href=&quot;https:&#x2F;&#x2F;grafana.com&#x2F;&quot;&gt;Grafana&lt;&#x2F;a&gt;, a setup also known as the &lt;a href=&quot;https:&#x2F;&#x2F;hackernoon.com&#x2F;monitor-your-infrastructure-with-tig-stack-b63971a15ccf&quot;&gt;TIG stack&lt;&#x2F;a&gt;. All these services are hosted on a server running inside my home.&lt;&#x2F;p&gt;
&lt;p&gt;The thing is, I don&#x27;t need access to my Grafana dashboards outside my home. In fact, I don&#x27;t even &lt;em&gt;want&lt;&#x2F;em&gt; those dashboards and the data they display available on the internet. So, I can just make up a domain that doesn&#x27;t exist, say &amp;quot;grafana.lan&amp;quot;, ensure that my home&#x27;s DNS resolver (this could be your router or, in my case, &lt;a href=&quot;https:&#x2F;&#x2F;pi-hole.net&#x2F;&quot;&gt;PiHole&lt;&#x2F;a&gt;) sends any &lt;code&gt;grafana.lan&lt;&#x2F;code&gt; requests to my server instead of to the internet and just make my Grafana container respond to &lt;code&gt;grafana.lan&lt;&#x2F;code&gt; requests using Traefik labels:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;grafana&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;: grafana&#x2F;grafana
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;- traefik&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;routers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;grafana&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rule&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;Host(`grafana.lan`)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;- traefik&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;grafana&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;loadbalancer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;server&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;3000
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I added a &lt;code&gt;server.port&lt;&#x2F;code&gt; label to make sure Traefik forwards all requests to the correct port that Grafana listens to, in this case &lt;code&gt;3000&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I removed the TLS-related labels since I won&#x27;t be needing HTTPS! Inside my home, I can just visit &lt;code&gt;http:&#x2F;&#x2F;grafana.lan&lt;&#x2F;code&gt; since I won&#x27;t be using the dangerous internet connections outside my home.&lt;&#x2F;p&gt;
&lt;p&gt;Simple, right?&lt;&#x2F;p&gt;
&lt;p&gt;Well, no. We have a problem, because this setup won&#x27;t work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Redirect_HTTP_to_HTTPS&quot;&gt;Redirect HTTP to HTTPS&lt;&#x2F;h2&gt;
&lt;p&gt;Remember the first lines we added to &lt;code&gt;traefik.toml&lt;&#x2F;code&gt;? The ones that redirected all HTTP requests to the HTTPS entrypoint? Well, every time we want to visit &lt;code&gt;http:&#x2F;&#x2F;grafana.lan&lt;&#x2F;code&gt;, it will be redirected to &lt;code&gt;https:&#x2F;&#x2F;grafana.lan&lt;&#x2F;code&gt;. In the best case scenario, your browser will display a few flashy warnings that you are about to enter a website over an unsecured connection, with a hidden button that allows you to proceed anyway. In the worst case scenario, your browser will not allow you anywhere close to your own container because there is a certificate problem.&lt;&#x2F;p&gt;
&lt;p&gt;So, the problem is that there is no certificate for &lt;code&gt;grafana.lan&lt;&#x2F;code&gt;? Well, simple! Let&#x27;s just add back the TLS labels and have a secure connection within the home!&lt;&#x2F;p&gt;
&lt;p&gt;Again, no can do. A domain needs to exist and be accessible from the internet in order for a SSL certificate to be issued. Since neither criteria are applicable to our &lt;code&gt;grafana.lan&lt;&#x2F;code&gt;, we can&#x27;t get a SSL certificate.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily, there is a solution.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;The_solution:_domain-specific_HTTP_redirection&quot;&gt;The solution: domain-specific HTTP redirection&lt;&#x2F;h2&gt;
&lt;p&gt;What we need to do is instruct Traefik to not redirect &lt;strong&gt;all&lt;&#x2F;strong&gt; HTTP connections to HTTPS, but &lt;strong&gt;only&lt;&#x2F;strong&gt; those that we can access from the internet. The connections that stay in the house should remain HTTP connections.&lt;&#x2F;p&gt;
&lt;p&gt;Remember these lines?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirections&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirections&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoint&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;to &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;websecure&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;scheme &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;https&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These need to be removed from &lt;code&gt;traefik.toml&lt;&#x2F;code&gt; so that you keep the basic entrypoint definitions:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;address &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;:80&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;websecure&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;address &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;:443&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, we need to add a so-called &lt;code&gt;dynamic file&lt;&#x2F;code&gt; configuration. Add the following line to the &lt;code&gt;[providers]&lt;&#x2F;code&gt; section in &lt;code&gt;traefik.toml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;providers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;filename &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;&#x2F;path&#x2F;to&#x2F;traefik_dynamic.toml&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;watch &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Create the file &lt;code&gt;&#x2F;path&#x2F;to&#x2F;traefik_dynamic.toml&lt;&#x2F;code&gt; and add the following content:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;middlewares&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;middlewares&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirect_to_https&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;middlewares&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirect_to_https&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;redirectscheme&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;scheme &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;https&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;routers&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;routers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;web_redir&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rule &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`)&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;entryPoints &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;web&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;middlewares &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;redirect_to_https&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;service &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;api@internal&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This code instructs Traefik to add a new &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;middlewares&#x2F;overview&#x2F;&quot;&gt;middleware&lt;&#x2F;a&gt; whose only responsibility is to redirect HTTP requests to HTTPS requests.&lt;&#x2F;p&gt;
&lt;p&gt;The next few lines applies this &lt;code&gt;middleware&lt;&#x2F;code&gt; to a &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;routing&#x2F;routers&#x2F;&quot;&gt;router&lt;&#x2F;a&gt;. The trick here is that this router is only applied to requests that target &lt;code&gt;example.com&lt;&#x2F;code&gt; and all its subdomains. You could add as many domains as you need to get them to always redirect to HTTPS. Our home domain &lt;code&gt;grafana.lan&lt;&#x2F;code&gt; is not in the list, therefore the &lt;code&gt;middleware&lt;&#x2F;code&gt; will not be applied to it and the connection will remain HTTP!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;&#x2F;strong&gt;: the router points to the service &lt;code&gt;api@internal&lt;&#x2F;code&gt;. This is done because a router always needs a service even when, as in this case, the service will never be used because the connection is redirected to HTTPS and after that, this router is no longer used. If you don&#x27;t like using &lt;code&gt;api@internal&lt;&#x2F;code&gt; for this purpose, you could always run a harmless &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;containous&#x2F;whoami&quot;&gt;whoami&lt;&#x2F;a&gt; container and point the router to that service.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;This Traefik setup takes a bit more effort but allows you to gracefully handle two different types of connections.&lt;&#x2F;p&gt;
&lt;p&gt;Connections coming from the internet are secured via SSL certificates and will always happen over HTTPS.&lt;&#x2F;p&gt;
&lt;p&gt;Connections coming from within the house stay within the house, they happen over HTTP and you can feel a lot safer knowing the internet no longer has direct access to that container.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Quick comparison: Plausible vs logs</title>
        <published>2020-07-13T23:19:39+00:00</published>
        <updated>2020-07-13T23:19:39+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/plausible-versus-logs/"/>
        <id>https://yarmo.eu/blog/plausible-versus-logs/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/plausible-versus-logs/">&lt;p&gt;About a month ago, I started collecting website usage data using both &lt;a href=&quot;https:&#x2F;&#x2F;plausible.io&quot;&gt;Plausible.io&lt;&#x2F;a&gt; and logs generated by &lt;a href=&quot;https:&#x2F;&#x2F;caddyserver.com&quot;&gt;Caddyserver&lt;&#x2F;a&gt;, my reverse proxy. The goal was to compare the data sources, just like &lt;a href=&quot;https:&#x2F;&#x2F;markosaric.com&#x2F;&quot;&gt;Marko Saric&lt;&#x2F;a&gt; did in a &lt;a href=&quot;https:&#x2F;&#x2F;plausible.io&#x2F;blog&#x2F;server-log-analysis&quot;&gt;post on the Plausible blog&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a quick overview of the results. For more details, read the post mentioned above, the results are nearly identical and Marko does a great job explaining the results.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Results&quot;&gt;Results&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;Quantitative_data&quot;&gt;Quantitative data&lt;&#x2F;h3&gt;
&lt;p&gt;The table below summarizes key metrics computed by both Plausible and &lt;a href=&quot;https:&#x2F;&#x2F;goaccess.io&quot;&gt;GoAccess&lt;&#x2F;a&gt; (based on Caddyserver logs). Data used was collected between June 13th and July 13th.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Metric&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Plausible.io&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Logs + GoAccess&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Δ factor&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Visitors&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;32.1k&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;76.9k&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x2.4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Pageviews&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;44.5k&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;468.6k&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;x10.5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Bandwidth&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;-&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;16.6 GiB&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;-&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Just as Marko noticed, logs show much higher numbers of visitors and pageviews, likely due to crawlers and bots that get noticed in the logs but do not run javascript and therefore are not picked up by Plausible.&lt;&#x2F;p&gt;
&lt;p&gt;I could compare other metrics like referrers and top pages, but again, I suggest you read the &lt;a href=&quot;https:&#x2F;&#x2F;plausible.io&#x2F;blog&#x2F;server-log-analysis&quot;&gt;post on the Plausible blog&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d like to add that the logs can provide some information about bandwidth usage and which files are downloaded the most. This would allow you to make informed decisions when optimizing caching and file loading. Plausible can&#x27;t help you with this data, one needs logs for this.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Qualitative_data&quot;&gt;Qualitative data&lt;&#x2F;h3&gt;
&lt;p&gt;The experience with Plausible was more convenient than with GoAccess, as the website of the former loads in seconds whilst the latter took 3 minutes to process the logs and generate the results.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Both methods have advantages and disadvantages. Plausible gives fast and precise results but potentially impacts page load (although minimally). Server logs don&#x27;t impact page load, can provide bandwidth stats but inflate numbers due to traffic noise generated by search engines, crawlers and bots. Personally, I will continue using both for the foreseeable future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Methodology&quot;&gt;Methodology&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;Plausible&quot;&gt;Plausible&lt;&#x2F;h3&gt;
&lt;p&gt;Visit the &lt;a href=&quot;https:&#x2F;&#x2F;plausible.io&quot;&gt;Plausible.io&lt;&#x2F;a&gt; website and simply look at the website&#x27;s stats.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Caddy_logs&quot;&gt;Caddy logs&lt;&#x2F;h3&gt;
&lt;p&gt;Logs were collected using the following snippet in the Caddyfile:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;log {
&lt;&#x2F;span&gt;&lt;span&gt;    output file &#x2F;var&#x2F;log&#x2F;caddy&#x2F;access.log {
&lt;&#x2F;span&gt;&lt;span&gt;        roll_size 100MiB
&lt;&#x2F;span&gt;&lt;span&gt;        roll_keep 10
&lt;&#x2F;span&gt;&lt;span&gt;        roll_keep_for 2160h
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;GoAccess&quot;&gt;GoAccess&lt;&#x2F;h3&gt;
&lt;p&gt;As GoAccess cannot read Caddy logs directly, a small bash script is needed:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;today_date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -u&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt; +&amp;quot;%Y-%m-%d&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;today_date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -u --date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span&gt;today_date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt; -30 day&amp;quot; +&amp;quot;%Y-%m-%d&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;today_ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -d &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;today_date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt; +%&lt;&#x2F;span&gt;&lt;span&gt;s)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;goaccess &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zcat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; logs&#x2F;access&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;* | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;jq&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --raw-output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;   .request.remote_addr |= .[:-6] |
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;   select(.request.remote_addr != &amp;quot;1.1.1.1&amp;quot;) |
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;   select(.request.remote_addr != &amp;quot;2.2.2.2&amp;quot;) |
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;   select(.ts &amp;gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;$today_ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;) |
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;   [
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      .common_log,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      .request.headers.Referer[0] &#x2F;&#x2F; &amp;quot;-&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      .request.headers.&amp;quot;User-Agent&amp;quot;[0],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      .duration
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;   ] | @csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;   --log-format&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;&amp;quot;%h - - [%d:%t %^] &amp;quot;&amp;quot;%m %r %H&amp;quot;&amp;quot; %s %b&amp;quot;,&amp;quot;%R&amp;quot;,&amp;quot;%u&amp;quot;,%T&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --time-format&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;%H:%M:%S&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; --date-format&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;%d&#x2F;%b&#x2F;%Y&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This was adapted from the bash script described by &lt;a href=&quot;https:&#x2F;&#x2F;fosstodon.org&#x2F;@AlexMV12&quot;&gt;Alessandro&lt;&#x2F;a&gt; in this &lt;a href=&quot;https:&#x2F;&#x2F;alexmv12.xyz&#x2F;blog&#x2F;goaccess_caddy&#x2F;&quot;&gt;blog post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Optimizing the website&#x27;s load performance</title>
        <published>2020-06-05T22:47:21+00:00</published>
        <updated>2020-06-05T22:47:21+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/website-load-performance/"/>
        <id>https://yarmo.eu/blog/website-load-performance/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/website-load-performance/">&lt;h2 id=&quot;My_old_webhosting&quot;&gt;My old webhosting&lt;&#x2F;h2&gt;
&lt;p&gt;When I started making websites back in 2010-ish (maybe even earlier than that, I don&#x27;t remember), I used shared hosting as I did not have the slightest clue about how a server worked, let alone set one up for web hosting. About two-three years ago, I switched to &lt;a href=&quot;https:&#x2F;&#x2F;www.cloudways.com&#x2F;en&#x2F;&quot;&gt;Cloudways&lt;&#x2F;a&gt;, allowing one to host websites on a virtual private server (VPS) and still not require any actual knowledge about the inner workings of a server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;My_new_webhosting&quot;&gt;My new webhosting&lt;&#x2F;h2&gt;
&lt;p&gt;However, I&#x27;ve been managing my own private server for almost two years so I felt confident I could do the hosting myself. Hip as I am (I am not), I decided to go with a &lt;a href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;&quot;&gt;Caddy server&lt;&#x2F;a&gt; as a &lt;a href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;get-started&quot;&gt;Docker container&lt;&#x2F;a&gt; on a VPS hosted by &lt;a href=&quot;https:&#x2F;&#x2F;www.digitalocean.com&#x2F;&quot;&gt;DigitalOcean&lt;&#x2F;a&gt;. For the low-traffic websites I currently maintain, this is largely sufficient.&lt;&#x2F;p&gt;
&lt;p&gt;I am in the process of moving each website one by one to the new hosting solution. It was time for this very website, &lt;a href=&quot;https:&#x2F;&#x2F;yarmo.eu&quot;&gt;yarmo.eu&lt;&#x2F;a&gt; and I thought to myself:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I should actually check if I gain any website load performance by moving to this new solution.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;When asking the Fediverse, 70% predicted &lt;a href=&quot;https:&#x2F;&#x2F;fosstodon.org&#x2F;web&#x2F;statuses&#x2F;104285148110095796&quot;&gt;Caddy would perform better than Cloudways&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Let&#x27;s_get_testing&quot;&gt;Let&#x27;s get testing&lt;&#x2F;h2&gt;
&lt;p&gt;I decided to use &lt;a href=&quot;https:&#x2F;&#x2F;www.webpagetest.org&quot;&gt;WebPageTest.org&lt;&#x2F;a&gt; to measure load perfomance. For each case described below, three measurements were performed and the median measurement is displayed and analyzed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Cloudways&quot;&gt;Cloudways&lt;&#x2F;h3&gt;
&lt;p&gt;First, a baseline measurement of my existing Cloudways solution.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_1a.png&quot; alt=&quot;Cloudways - overview&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Cloudways - overview&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_1b.png&quot; alt=&quot;Cloudways - rating&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Cloudways - rating&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_1c.png&quot; alt=&quot;Cloudways - waterfall&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Cloudways - waterfall&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So the server returns the first byte of information after 480 milliseconds. Now, I should tell you that my website is based on &lt;a href=&quot;https:&#x2F;&#x2F;phug-lang.com&quot;&gt;Phug&lt;&#x2F;a&gt;, the PHP port of &lt;a href=&quot;https:&#x2F;&#x2F;pugjs.org&quot;&gt;pug.js templating&lt;&#x2F;a&gt;. The page is rendered in real-time and apparently, that takes a little over 300 ms.&lt;&#x2F;p&gt;
&lt;p&gt;It is worth noting that any other metric is then dependent on how the website is programmed. Once Cloudways has sent over the data, it no longer has any influence on load performance.&lt;&#x2F;p&gt;
&lt;p&gt;The website if fully loaded  after 923 ms. Good to know. About a second to wait for my website to load.&lt;&#x2F;p&gt;
&lt;p&gt;Over on the waterfall, we see a bunch of files being downloaded simultaneously after the HTML page is loaded. The largest asset to load is the profile picture.&lt;&#x2F;p&gt;
&lt;p&gt;Wait, what is that &lt;code&gt;F&lt;&#x2F;code&gt; over on the rating? Security is NOT in order! As it turns out, Cloudways does not handle security-related HTTP headers for you… I did not know that! They &lt;a href=&quot;https:&#x2F;&#x2F;support.cloudways.com&#x2F;enable-hsts-policy&#x2F;&quot;&gt;recommend setting these headers in a .htaccess file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let this be a reminder to all of you: test your websites. One might learn a few tricks.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, can Caddy do better?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Caddy&quot;&gt;Caddy&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_2a.png&quot; alt=&quot;Caddy - overview&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy - overview&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_2b.png&quot; alt=&quot;Caddy - rating&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy - rating&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_2c.png&quot; alt=&quot;Caddy - waterfall&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy - waterfall&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Well, as it turns out, it&#x27;s largely the same performance. First byte arrived after 459 ms, but I&#x27;ve ran it a few times and there&#x27;s really little difference between Cloudways and Caddy.&lt;&#x2F;p&gt;
&lt;p&gt;BUT! Learning from my previous mistakes, I configured Caddy to set up all the correct headers and won&#x27;t you look at that, &lt;code&gt;A+&lt;&#x2F;code&gt; on the security score!&lt;&#x2F;p&gt;
&lt;p&gt;So that&#x27;s it then?&lt;&#x2F;p&gt;
&lt;p&gt;Well, not really… I learned that my website wasn&#x27;t running optimally because I forgot some basic HTTP headers. Did I forget more? In other words, can I do even better than this?&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve tried a lot of things, I&#x27;ll just narrow it down to the two most important findings.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Caddy_-_inline_most_of_it&quot;&gt;Caddy - inline most of it&lt;&#x2F;h3&gt;
&lt;p&gt;As it turned out, I had a few small SVG icons and some CSS files. I tried rendering them into the HTML page, so the data would be sent on the first data transmission and no separate requests were needed. For good measure, I also minified the CSS files which, for one file, reduced the size by 30%!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_6a.png&quot; alt=&quot;Caddy+inline - overview&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy+inline - overview&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_6b.png&quot; alt=&quot;Caddy+inline - rating&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy+inline - rating&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_6c.png&quot; alt=&quot;Caddy+inline - waterfall&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy+inline - waterfall&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On the waterfall above, you can clearly see the &lt;code&gt;dank-mono.css&lt;&#x2F;code&gt; was not inlined but I tried multiple configurations, there was no real gain as the image also needed to load and took longer anyway. So, all in all, inlining the SVG and CSS content did little in this case.&lt;&#x2F;p&gt;
&lt;p&gt;Also, note the regression from &lt;code&gt;A+&lt;&#x2F;code&gt; to &lt;code&gt;A&lt;&#x2F;code&gt; on the security score. There was one header I couldn&#x27;t quite get working properly so I had to disable that one, other than that, it&#x27;s working better than it ever has.&lt;&#x2F;p&gt;
&lt;p&gt;What drew my attention for the final step was the &lt;code&gt;B&lt;&#x2F;code&gt;. My server responds in within 480 ms and that it still not good enough for you, WebPageTest? Ok, have it your way.&lt;&#x2F;p&gt;
&lt;p&gt;What takes my server so long to respond? Well, obviously, it must be the templating. Can I improve the template? Perhaps. But as it turns out, I don&#x27;t have to! Ever heard of caching?&lt;&#x2F;p&gt;
&lt;p&gt;As described on &lt;a href=&quot;https:&#x2F;&#x2F;phug-lang.com&#x2F;#usage&quot;&gt;their website&lt;&#x2F;a&gt;, PHUG has support for caching and even calling an optimized version of their renderer. So I applied both caching and optimized rendering.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Caddy_-_PHUG_optimization&quot;&gt;Caddy - PHUG optimization&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_7a.png&quot; alt=&quot;Caddy+PHUG - overview&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy+PHUG - overview&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_7b.png&quot; alt=&quot;Caddy+PHUG - rating&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy+PHUG - rating&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;wpt_1_7c.png&quot; alt=&quot;Caddy+PHUG - waterfall&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Caddy+PHUG - waterfall&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Well, there it is!!! First byte of data arrived after a mere 173 ms, website is useable in less than half a second and all scores are &lt;code&gt;A&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s the result I was hoping for. Now on my todo list:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Optimize the profile picture further or try SVG&lt;&#x2F;li&gt;
&lt;li&gt;Get all HTTP headers perfect&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Any comments or recommendations&#x2F;optimization? Please &lt;a href=&quot;&#x2F;contact&quot;&gt;let me know&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;!-- https:&#x2F;&#x2F;www.webpagetest.org&#x2F;result&#x2F;200604_Z3_05019c9c3f872873bd1e964474cb0dac&#x2F; --&gt;
&lt;!-- https:&#x2F;&#x2F;www.webpagetest.org&#x2F;result&#x2F;200604_E2_026978bdd64ce5830ecf5be74b634120&#x2F; --&gt;
&lt;!-- https:&#x2F;&#x2F;www.webpagetest.org&#x2F;result&#x2F;200605_M4_b728be8e608e4807af0192cefaf55f2e&#x2F; --&gt;
&lt;!-- https:&#x2F;&#x2F;www.webpagetest.org&#x2F;result&#x2F;200605_CF_96d815e43897af2723f7a4d762d76ba3&#x2F; --&gt;
&lt;!-- https:&#x2F;&#x2F;www.webpagetest.org&#x2F;result&#x2F;200605_61_67b241dc40cbaf08ed257177e6efbef0&#x2F; --&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Case of the Missing Entropy</title>
        <published>2020-06-05T12:14:57+00:00</published>
        <updated>2020-06-05T12:14:57+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/missing-entropy/"/>
        <id>https://yarmo.eu/blog/missing-entropy/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/missing-entropy/">&lt;blockquote&gt;
&lt;p&gt;In computing, entropy is the randomness collected by an operating system or application for use in cryptography or other uses that require random data. (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Entropy_(computing)&quot;&gt;Source: wikipedia&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;Docker,_are_you_still_there?&quot;&gt;Docker, are you still there?&lt;&#x2F;h2&gt;
&lt;p&gt;It all started when I got myself a new VPS server for serving web content. I have a more-than-capable server at home but I&#x27;d rather not use it for &amp;quot;uptime-sensitive&amp;quot; use-cases, the odd crash still takes it down from time to time. I know, a CDN…&lt;&#x2F;p&gt;
&lt;p&gt;Sticking with what I&#x27;m comfortable with, I decided to go with a docker setup with only a few containers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;yarmo.eu&#x2F;blog&#x2F;missing-entropy&#x2F;caddyserver.com&#x2F;&quot;&gt;caddy&lt;&#x2F;a&gt; as web server and reverse proxy&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;_&#x2F;php&quot;&gt;php-fpm&lt;&#x2F;a&gt; as PHP processor&lt;&#x2F;li&gt;
&lt;li&gt;a couple of others with minor significance&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As per usual, I write my &lt;code&gt;docker-compose.yaml&lt;&#x2F;code&gt; and it&#x27;s all set. But not this time. At times, when I would change something in the yaml file and run &lt;code&gt;docker-compose up -d&lt;&#x2F;code&gt;, it would do it immediately, as I would expect from all the times I&#x27;ve run it on my homelab. But sometimes, it would wait a minute or longer and only execute then.&lt;&#x2F;p&gt;
&lt;p&gt;I accepted this behavior a few times, but at some point, it had to be dealt with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Investigating&quot;&gt;Investigating&lt;&#x2F;h2&gt;
&lt;p&gt;I noticed a few things. First, it did not seem to be due to a lack of computing resources. My &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;grafana&#x2F;grafana&quot;&gt;Grafana&lt;&#x2F;a&gt; dashboard (with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;influxdata&#x2F;influxdb&quot;&gt;InfluxDB&lt;&#x2F;a&gt; as backend and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;influxdata&#x2F;telegraf&quot;&gt;Telegraf&lt;&#x2F;a&gt; as agent) clearly showed me that CPU usage was about 1% and RAM was about 30% full. No excessive DISK IO or NETWORK IO. So we are not overwhelming the system!&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, while it was waiting to execute, I could open a new SSH connection and do other stuff. With one exception: any docker-related command would not execute.&lt;&#x2F;p&gt;
&lt;p&gt;Final clue: I could not ctrl-c my way out of a pending docker command execution, but if I would close the terminal, open a new one, connect via SSH and run any new docker command, it would still wait.&lt;&#x2F;p&gt;
&lt;p&gt;Final final clue: a minute later, I could run docker commands left, right and center without a single problem. Another minute later, it might do the whole waiting again. It was very… &amp;quot;Random&amp;quot;. Wink, wink…&lt;&#x2F;p&gt;
&lt;p&gt;Have you figured it out yet? I hadn&#x27;t.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Researching&quot;&gt;Researching&lt;&#x2F;h2&gt;
&lt;p&gt;With this information, I was confident enough to start searching online and I came across this &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;compose&#x2F;issues&#x2F;6552&quot;&gt;github issue&lt;&#x2F;a&gt; fairly quickly:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;docker-compose often takes a long time to do anything&amp;quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That sounds about right!&lt;&#x2F;p&gt;
&lt;p&gt;A few comments in, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;compose&#x2F;issues&#x2F;6552#issuecomment-529787442&quot;&gt;it was suggested&lt;&#x2F;a&gt; to run the following command: &lt;code&gt;cat &#x2F;proc&#x2F;sys&#x2F;kernel&#x2F;random&#x2F;entropy_avail&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;On my VPS, this returned &lt;code&gt;52&lt;&#x2F;code&gt;. Whoopsie…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Entropy&quot;&gt;Entropy&lt;&#x2F;h2&gt;
&lt;p&gt;For those of you who don&#x27;t know what (computing) entropy is, here&#x27;s the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Entropy_(computing)&quot;&gt;wikipedia article&lt;&#x2F;a&gt; for it. In short: computers are terrible at coming up with random numbers (just like humans! Topic for another day), which many applications require for their proper working.&lt;&#x2F;p&gt;
&lt;p&gt;Our operating systems have a clever way to solve this: take all input that is NOT generated by the computer itself and use that as &amp;quot;randomness&amp;quot;. For example, a computer doesn&#x27;t know in advance how you are going to move the cursor or which keyboard button you will press. The operating system takes these inputs, processes them to &amp;quot;extract the randomness&amp;quot; and stores it in the &lt;code&gt;entropy pool&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Any application needing some randomness can request some random data from the &lt;code&gt;entropy pool&lt;&#x2F;code&gt;. Maintaining sufficient entropy is therefore a challenge in itself: process enough random data to keep up with the demand.&lt;&#x2F;p&gt;
&lt;p&gt;Apparently, Docker is an application that requires randomness. But how have I never encountered this issue before?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;VPS_and_entropy&quot;&gt;VPS and entropy&lt;&#x2F;h2&gt;
&lt;p&gt;On both my desktop computer and homelab, the entropy available is around &lt;code&gt;4000&lt;&#x2F;code&gt;, which is perfect. They are able to maintain this entropy because of all the sources of randomness available to them. Mouse and keyboard inputs, processes running in the background, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Now, let&#x27;s take the VPS as a counter example. These things are made to be fully reproducible: every time you boot one up, they are expected to run in the same way. They are also very sealed off from the host system for security reasons: I cannot read core temperature values for my VPS. They don&#x27;t have &amp;quot;true hardware&amp;quot;, they get portions of hardware, shared with other VPS instances. Except for my SSH connection, the VPS has no mouse or keyboard inputs.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, VPSs are severely lacking in sources of entropy. That is why the entropy available was only &lt;code&gt;52&lt;&#x2F;code&gt; and why docker stalled: it had to wait for sufficient randomness to occur.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.digitalocean.com&#x2F;community&#x2F;tutorials&#x2F;how-to-setup-additional-entropy-for-cloud-servers-using-haveged&quot;&gt;More information on VPS and entropy from DigitalOcean&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;The_remedy:_haveged&quot;&gt;The remedy: haveged&lt;&#x2F;h2&gt;
&lt;p&gt;There is a way to remedy the situation: &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Haveged&quot;&gt;haveged&lt;&#x2F;a&gt;. Having only discovered it last night, I do not fully understand it yet but from what I have read, it is a pseudorandom number generator (PRNG) that fills the &lt;code&gt;entropy pool&lt;&#x2F;code&gt; with &amp;quot;pseudorandomness&amp;quot;. Installing &lt;code&gt;haveged&lt;&#x2F;code&gt; immediately solved my issue, all docker commands were running instantly again.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;blog&#x2F;entropy_haveged.png&quot; alt=&quot;Available entropy suddenly increases after installing haveged&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
&lt;em&gt;Can you tell when I installed haveged?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Caveat:_pseudorandomness&quot;&gt;Caveat: pseudorandomness&lt;&#x2F;h2&gt;
&lt;p&gt;There is a downside to this: PRNGs are NOT random. &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Pseudorandom_number_generator&quot;&gt;Wikipedia article on PRNGs&lt;&#x2F;a&gt;. PRNGs generate numbers that appear random but are fully deterministic: run the exact same algorithm twice, and you&#x27;ll get the same &amp;quot;random&amp;quot; numbers. Therefore, VPSs may not be the perfect solutions to perform entropy-heavy tasks such as cryptography: a cryptographic key generated with pseudorandom numbers is far less secure than one generated with truly random numbers.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>LunaSea: FOSS FTW</title>
        <published>2020-05-22T19:47:38+00:00</published>
        <updated>2020-05-22T19:47:38+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/lunasea/"/>
        <id>https://yarmo.eu/blog/lunasea/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/lunasea/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-05-22 &amp;gt;&amp;gt; 023&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Out_with_the_old&quot;&gt;Out with the old&lt;&#x2F;h2&gt;
&lt;p&gt;A couple of weeks ago, I finally discovered a FOSS alternative for nzb360, a great app for managing Plex, Radarr, Sonarr, etc. I wish to have kept using nzb360, but unfortunately, the app relies too heavily on Google Services and though I have paid for it, I can no longer use it as my LineageOS phone can&#x27;t process purchases made on official Google Android phones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;In_with_the_new&quot;&gt;In with the new&lt;&#x2F;h2&gt;
&lt;p&gt;Named &lt;a href=&quot;https:&#x2F;&#x2F;www.lunasea.app&quot;&gt;LunaSea&lt;&#x2F;a&gt;, it can do anything it should, (manage Sonarr, Radarr, Lidarr, NZB clients), it looks fantastic, it&#x27;s available for both Google Android and iPhone and, of course, it&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;LunaSeaApp&#x2F;LunaSea&quot;&gt;FOSS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Only thing I&#x27;m missing is a donation button. And a fediverse account :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Introduction to PiHole</title>
        <published>2020-05-10T23:24:04+00:00</published>
        <updated>2020-05-10T23:24:04+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/pihole/"/>
        <id>https://yarmo.eu/blog/pihole/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/pihole/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-05-10 &amp;gt;&amp;gt; 016&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pi-hole.net&#x2F;&quot;&gt;PiHole&lt;&#x2F;a&gt; is almost ubiquitously present on every list of services people could&#x2F;should selfhost. And rightfully so, it is easy to set up and extremely useful on a daily basis. It blocks ads on almost all websites on all the devices in your home without the necessity of installing anything on them. It will also stop some devices from communicating with their parent companies behind your back.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;How_it_works&quot;&gt;How it works&lt;&#x2F;h2&gt;
&lt;p&gt;To understand how PiHole does its thing, we need a quick introduction into how DNS works, the system that makes sure we can visit websites even if they are located on the other side of the world. The problem DNS solves is that the URL you use to visit a website doesn&#x27;t tell your device anything about the physical location or IP address of the server that hosts the website.&lt;&#x2F;p&gt;
&lt;p&gt;If you wish to visit a website, say &lt;a href=&quot;https:&#x2F;&#x2F;yarmo.eu&quot;&gt;yarmo.eu&lt;&#x2F;a&gt;, you enter that address in the top bar and hit enter. Your browser will then ask your router to get this website for you. If this is the first time you visit this website, your router doesn&#x27;t know yet where the server is located, so it asks a DNS server in geographical proximity, usually the DNS server of your ISP.&lt;&#x2F;p&gt;
&lt;p&gt;If this DNS server knows the IP address of the server, it will be relayed back to your device which will now ask that server directly for the content of the website. If the DNS server doesn&#x27;t have this information, it will ask another and so forth until the IP address of the host server is found.&lt;&#x2F;p&gt;
&lt;p&gt;As we established above, your router contains a DNS router. However, this can almost always be delegated to another DNS server in your home. That&#x27;s where PiHole comes in. Instead of your router trying to figure out where the website server is located, it will ask PiHole to do so.&lt;&#x2F;p&gt;
&lt;p&gt;But PiHole has a trick up its sleeve: it has a built-in database of hundreds of thousands of URLs that are associated with ads and when they are requested, PiHole simply ignores them.&lt;&#x2F;p&gt;
&lt;p&gt;So you want to visit &lt;code&gt;coolsite.com&lt;&#x2F;code&gt;? Fine, PiHole will get you that website. Now, &lt;code&gt;coolsite.com&lt;&#x2F;code&gt; suddenly wants to load an ad from &lt;code&gt;ads.gafam.com&lt;&#x2F;code&gt;? The computer asks the router, the router asks PiHole, PiHole knows this URL is used to serve ads and will block that request, giving you a website without ads. Awesome!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Something_you_want_to_say?&quot;&gt;Something you want to say?&lt;&#x2F;h2&gt;
&lt;p&gt;Meanwhile, you are listening to music using a wireless speaker in your living room from a made-up brand &amp;quot;NOSON&amp;quot;. What you don&#x27;t know is that this device is continuously sending messages to the company containing information about the music you play and more. PiHole knows this and as soon as the speaker requests to send a message to &lt;code&gt;metrics.noson.com&lt;&#x2F;code&gt;, PiHole says no.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s how PiHole blocks ads AND protects your privacy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Dedicated_hardware&quot;&gt;Dedicated hardware&lt;&#x2F;h2&gt;
&lt;p&gt;Dedicating hardware to PiHole is advised but the hardware can be as simple as a &lt;a href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;products&#x2F;raspberry-pi-zero&#x2F;&quot;&gt;Raspberry Pi Zero&lt;&#x2F;a&gt;. The reason it is advised to use dedicated hardware is because if your PiHole crashes, there&#x27;s no more internet in the home until you get the PiHole working again. It way to prevent this situation from happening is to always have two PiHoles running on separate hardware and telling the router about both PiHoles.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Second_DNS_server?&quot;&gt;Second DNS server?&lt;&#x2F;h2&gt;
&lt;p&gt;Oh, and while we&#x27;re on the subject: do not put any &amp;quot;fallback&amp;quot; DNS servers like Google&#x27;s or Cloudflare&#x27;s in the second DNS server field on your router. Unfortunately, it doesn&#x27;t work like a fallback, all routers will simply divide the workload over the two DNS servers. This means that if an outside DNS server is put in second place, it will receive DNS calls even if the PiHole is fully functional.&lt;&#x2F;p&gt;
&lt;p&gt;Having a proper DNS fallback server is difficult to set up, so best would be to use two different PiHole instances. Unless, of course, you don&#x27;t mind a small period of internet loss and you are always nearby to fix the situation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Caveats&quot;&gt;Caveats&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately, ads on video platforms like YouTube will not be blocked. This is because they serve the ads on the same domains as they serve the main content, meaning that they don&#x27;t have a &lt;code&gt;ads.youtube.com&lt;&#x2F;code&gt; or something similar. Therefore, PiHole cannot block the ads. As there are a few of these edge cases, it is always recommended to use PiHole in conjunction with on-device ad blockers like &lt;a href=&quot;https:&#x2F;&#x2F;getublock.com&#x2F;&quot;&gt;uBlock Origin&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Final_words&quot;&gt;Final words&lt;&#x2F;h2&gt;
&lt;p&gt;Really, there are few reasons to not get PiHole into your home and the benefits vastly outweigh the challenges (IMHO). It is also a great start on a journey of selfhosting more services and realising that one can be independent of major corporations to some degree.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Traefik migrated to v2</title>
        <published>2020-05-09T23:01:34+00:00</published>
        <updated>2020-05-09T23:01:34+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/traefik-migration/"/>
        <id>https://yarmo.eu/blog/traefik-migration/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/traefik-migration/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-05-09 &amp;gt;&amp;gt; 015&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Last september, &lt;a href=&quot;https:&#x2F;&#x2F;containo.us&#x2F;traefik&#x2F;&quot;&gt;traefik&lt;&#x2F;a&gt; received its &lt;a href=&quot;https:&#x2F;&#x2F;containo.us&#x2F;blog&#x2F;traefik-2-0-6531ec5196c2&#x2F;&quot;&gt;big version 2 update&lt;&#x2F;a&gt;. I was very excited about TCP routers and the newly implemented middlewares. Can&#x27;t have been more than a few days later that I tried to migrate my homelab to the new version. I remember being annoyed by the lack of a proper migration guide. Sure, it&#x27;s possible that I didn&#x27;t look good enough, but I searched for it for a few days without results. I tried using the new documentation and failed, everything crashed and could not get it working. As I did not have the time to do much more extensive research and also, I needed the selfhosted services on a daily basis, so I left it.&lt;&#x2F;p&gt;
&lt;p&gt;Until today. The whole migration took me a little over three hours and I learned quite a bit on the way. Also, the &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;migration&#x2F;v1-to-v2&#x2F;&quot;&gt;migration guide&lt;&#x2F;a&gt; has helped quite a bit. If this was updated since September, great article. If not, still a great article and I really did not take the appropriate amount of time to prepare my migration.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Easy_steps&quot;&gt;Easy steps&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing I did was a general search-and-replace for the docker labels (both routers and services). What was &lt;code&gt;traefik.frontend.rule=Host:xyz&lt;&#x2F;code&gt; now is &lt;code&gt;traefik.http.routers.router0.rule=Host(``xyz``)&lt;&#x2F;code&gt;. What was &lt;code&gt;traefik.port=80&lt;&#x2F;code&gt; now is &lt;code&gt;traefik.http.services.service0.loadbalancer.server.port=80&lt;&#x2F;code&gt;. Quite a bit longer and more cumbersome, but in the end, more extensible.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;traefik.docker.network=xyz&lt;&#x2F;code&gt; is now unnecessary in most cases as you can define a default network in the &lt;code&gt;traefik.toml&lt;&#x2F;code&gt; file. Speaking of which, you can now work with a YAML file. It&#x27;s not to everyone&#x27;s taste, but I will switch to it in the future when I have some more time.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;traefik.toml&lt;&#x2F;code&gt; still needed quite the makeover, but everything is will explained in the &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;migration&#x2F;v1-to-v2&#x2F;&quot;&gt;migration guide&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;docs.traefik.io&#x2F;reference&#x2F;static-configuration&#x2F;file&#x2F;&quot;&gt;reference page&lt;&#x2F;a&gt;. Content-wise, I changed little, it&#x27;s just that the syntax is different. Notable changes are the domains for which certificates are needed now are declared in the &lt;code&gt;entrypoints&lt;&#x2F;code&gt; section instead of the &lt;code&gt;acme&lt;&#x2F;code&gt; section and the &lt;code&gt;file&lt;&#x2F;code&gt; section can no longer include the &amp;quot;router&#x2F;service declarations&amp;quot;, they belong in a separate file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Pitfalls&quot;&gt;Pitfalls&lt;&#x2F;h2&gt;
&lt;p&gt;Doing this resulted in a non-functional state. Two different possibilities: either the container could not be redirected to the correct service resulting in a 404, or the redirect was correct but without correct certificate. That first possibility was mostly on me: containers are now no longer exposed by default and I forgot to add &lt;code&gt;traefik.enable=true&lt;&#x2F;code&gt;. Mind you, I always set &lt;code&gt;traefik.enable=false&lt;&#x2F;code&gt; when I didn&#x27;t want a container exposed and still do.&lt;&#x2F;p&gt;
&lt;p&gt;However this did not solve the issue for all the containers. I suspect there&#x27;s still some trickery I need to do in case of using multiple routers. I tried explicitly specifying the &lt;code&gt;service&lt;&#x2F;code&gt; for the different routers but that wasn&#x27;t the solution.&lt;&#x2F;p&gt;
&lt;p&gt;As for the other issue, the solution was simple but finding the source was quite hard: as it turns out, I renamed the &lt;code&gt;certificateResolver&lt;&#x2F;code&gt; to something other than &lt;code&gt;default&lt;&#x2F;code&gt;. If such is the case, then containers will NOT automatically use it for their certificates. Adding &lt;code&gt;traefik.http.routers.router0.tls=true&lt;&#x2F;code&gt; and &lt;code&gt;traefik.http.routers.router0.tls.certresolver=mycertresolver&lt;&#x2F;code&gt; to each container solves this issue.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Todo&quot;&gt;Todo&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I haven&#x27;t got working yet is using the &lt;code&gt;providers.file&lt;&#x2F;code&gt; provider. I tried to mimic the container labels but to no avail. Yet.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Update 2020-05-12&lt;&#x2F;strong&gt;: I fixed the &lt;code&gt;providers.file&lt;&#x2F;code&gt; issue. Remember kids, always read the documentation well. It turns out, I missed the line that starts with &lt;code&gt;*&lt;&#x2F;code&gt; in the code below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;Service01&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;Service01&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;loadBalancer&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;*           [[http.services.Service01.loadBalancer.servers]]
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;foobar&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Varken: Plex monitoring solution</title>
        <published>2020-05-05T21:49:58+00:00</published>
        <updated>2020-05-05T21:49:58+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/varken/"/>
        <id>https://yarmo.eu/blog/varken/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/varken/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-05-05 &amp;gt;&amp;gt; 011&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Today, I discovered &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Boerderij&#x2F;Varken&quot;&gt;Varken&lt;&#x2F;a&gt;, a neat solution to monitor your Plex ecosystem (including Sonarr, Radarr, etc.) and store the data in your InfluxDB instance. This solution is a great addition as I can now make Grafana or Chronograf dashboardz encompassing both server metrics and Plex metrics. The reason this is important is that I have a relatively low-power server (NUC) and a single stream on Plex can have a noticeable impact on the CPU usage.&lt;&#x2F;p&gt;
&lt;p&gt;Varken requires a &lt;a href=&quot;https:&#x2F;&#x2F;tautulli.com&#x2F;&quot;&gt;Tautulli&lt;&#x2F;a&gt; instance to collect the data from as well as a &lt;a href=&quot;https:&#x2F;&#x2F;www.maxmind.com&quot;&gt;MaxMind&lt;&#x2F;a&gt; API key which unfortunately isn&#x27;t optional. I run all software mentioned in this post in separate docker containers.&lt;&#x2F;p&gt;
&lt;p&gt;Also, 11th post for #100DaysToOffload today and 11 is my lucky number :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Selfhost email… But should you?</title>
        <published>2020-05-03T19:46:47+00:00</published>
        <updated>2020-05-03T19:46:47+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/selfhost-email-drawbacks/"/>
        <id>https://yarmo.eu/blog/selfhost-email-drawbacks/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/selfhost-email-drawbacks/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-05-03 &amp;gt;&amp;gt; 009&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yesterday, I wrote about &lt;a href=&quot;&#x2F;blog&#x2F;selfhost-email&quot;&gt;how you &lt;strong&gt;can&lt;&#x2F;strong&gt; selfhost your very own email server&lt;&#x2F;a&gt;. Shortly after publishing the post, it was pointed out to me that are very reasonable drawbacks to doing this. So today, let me give you my answer to the question of whether you &lt;strong&gt;should&lt;&#x2F;strong&gt; selfhost your email.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Relying_on_hardware&quot;&gt;Relying on hardware&lt;&#x2F;h2&gt;
&lt;p&gt;Firstly, I mentioned in that article that although I have two domains on my selfhosted server, I still fall back to a protonmail address for the most important stuff like banking and governmental services. So what&#x27;s the point of selfhosting then, if I do use third-party email addresses? Well, what I failed to mention was that in the long term, yes, I do want everything selfhosted.&lt;&#x2F;p&gt;
&lt;p&gt;When I started my email hosting adventure, I was very cautious. Only months before did I start my own homelab and as it turned out, that had the tendency to crash every so often, making it a no-go for email hosting. I resorted to use a VPS while getting my homelab sorted out. This worked great and still does, but at the time, you can imagine I was still discovering the DNS parameters, the reputation handling (more on this later). Also, what were the consequences of running a VPS for 24&#x2F;7? I could not commit to using the selfhosted email for anything more than experimentation.&lt;&#x2F;p&gt;
&lt;p&gt;Fastforward about a year and the VPS has held up greatly, the email software has never crashed or acted against my expectations. It has received regular updates and never failed once during one. Meanwhile, my homelab has proven to be extremely reliable and with the upcoming hardware upgrade, I expect even less irregularities than I do now. Soon enough, when all the stars align and I figure out how to make recoveries as fast on my local hardware as I can on a VPS (make a new instance based on a daily snapshot and voilà!), my email server will be transferred to my homelab and I will use it for everything.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;The_pain_of_administering_an_email_server&quot;&gt;The pain of administering an email server&lt;&#x2F;h2&gt;
&lt;p&gt;Secondly, I&#x27;ve also heard of people not resorting to selfhost their email because of the fragility of the underlying processes and if one thing is slightly out of tune, the whole email server stops working. Although I&#x27;ve toyed around with most of the individual processes like dovecot at the beginning to understand what they do and how they work, I haven&#x27;t touched a single one of them in almost a year. &lt;a href=&quot;https:&#x2F;&#x2F;mailcow.email&#x2F;&quot;&gt;mailcow.email&lt;&#x2F;a&gt; is just that good. I&#x27;ve played around with the settings and it won&#x27;t stop working. Meanwhile, I get an antivirus, spam monitoring, those handy &amp;quot;+topic&amp;quot; email filtering. I&#x27;d like to try out &lt;a href=&quot;https:&#x2F;&#x2F;mailinabox.email&#x2F;&quot;&gt;Mail-in-a-Box&lt;&#x2F;a&gt;, mostly because it is also recommended by &lt;a href=&quot;https:&#x2F;&#x2F;www.privacytools.io&#x2F;providers&#x2F;email&#x2F;#selfhosting&quot;&gt;PrivacyTools&lt;&#x2F;a&gt; but I have no incentive too. My current solution just works great for me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;The_reputation_of_a_server&quot;&gt;The reputation of a server&lt;&#x2F;h2&gt;
&lt;p&gt;Lastly, I need to address a IMO bigger problem: reputation. If other servers don&#x27;t trust you, your emails may easily be thrown into the spam folder of the recipient or even rejected. The main reason for this is to fight spam: mass email spammers usually operate from unknown IP addresses. Unfortunately, this hurts the selfhosters. So, before you have even installed your email server software, you are already mistrusted by simply not using the big servers like Gmail and Hotmail. And indeed, when I started, most of my emails landed in spam.&lt;&#x2F;p&gt;
&lt;p&gt;This got greatly improved simply by using an email relay; in my case, &lt;a href=&quot;https:&#x2F;&#x2F;www.mailgun.com&#x2F;&quot;&gt;mailgun&lt;&#x2F;a&gt;. These paid-for (but often with free tier) services are a lot more trusted since mailgun will do spam prevention on their end, so letting them send your emails for you is a great improvement. And even with the &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=22192543&quot;&gt;recently reduced free tier&lt;&#x2F;a&gt;, I don&#x27;t send nearly enough emails to come close to the free tier quota.&lt;&#x2F;p&gt;
&lt;p&gt;However, it still happens that my emails are treated as spam so I often do follow ups via other channels of communication. Another issue may be that the IP address you were given already has a bad reputation caused by a previous owner: this is difficult to find out and even harder to fix. DDG-ing &lt;code&gt;improve email server reputation&lt;&#x2F;code&gt; yields many articles but read a handful of them and soon you&#x27;ll realise it&#x27;s really really hard to improve it. There&#x27;s no central repository, no forms. Getting a mistrusted IP address can quickly suck all the fun out of having your own email server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Answering_the_question&quot;&gt;Answering the question&lt;&#x2F;h2&gt;
&lt;p&gt;So, &lt;strong&gt;should you&lt;&#x2F;strong&gt;? This depends on how willing you are to be independent of third-party email services and how much you are willing to put up with. I started naïvely and had to answer this question along the way while experimenting. By now, my personal answer to this question is: yes. I see the benefits and drawbacks. I&#x27;m not sure if it&#x27;s the usage of mailgun, or me sending mails to family and friends and then asking them to tell their services to not mark it as spam, but most of my emails are properly received nowadays. Also, I have managed to improve my infrastructure, I can rely on the hardware (and soon on the emergency recovery mechanisms) and will soon migrate my email server so it&#x27;s nicely at home.&lt;&#x2F;p&gt;
&lt;p&gt;Hosting your own email server is not easy and requires your full dedication. And with many upcoming &lt;a href=&quot;https:&#x2F;&#x2F;www.privacytools.io&#x2F;providers&#x2F;email&#x2F;&quot;&gt;trusted and privacy friendly email services&lt;&#x2F;a&gt;, it may not always be the right tool for the job.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Selfhost email</title>
        <published>2020-05-02T16:20:35+00:00</published>
        <updated>2020-05-02T16:20:35+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/selfhost-email/"/>
        <id>https://yarmo.eu/blog/selfhost-email/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/selfhost-email/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-05-02 &amp;gt;&amp;gt; 008&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yes, you can selfhost email. And you should, if and ONLY if you feel comfortable with maintaining a linux server. I&#x27;m not a pro at all, but I&#x27;ve been doing it for almost two years, I know where to find my logs, I know how to find the correct answers on stackoverflow and troubleshoot a less-than-functional system.&lt;&#x2F;p&gt;
&lt;p&gt;So don&#x27;t start with this, but eventually, soon enough, you can selfhost your email.&lt;&#x2F;p&gt;
&lt;p&gt;Because email is important to me, I have chosen to not host it at home, any network issue could prevent emails from coming in. Granted, the sending server will usually retry for 24 hours until the email is actually received on your side so small errors are forgiven, but still, I&#x27;ve opted for a dedicated droplet on digitalocean, though any VPS will do.&lt;&#x2F;p&gt;
&lt;p&gt;And then, follow the instructions on &lt;a href=&quot;https:&#x2F;&#x2F;mailcow.email&#x2F;&quot;&gt;mailcow.email&lt;&#x2F;a&gt; and you&#x27;re set. SSH in once a week on your VPS to run the updater. The administration side has plenty of features for advanced administration of the email server and the included webclient is the awesome SOGo.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to make sure your email server is as trusted by other servers as possible, your emails are sent as securely as possible and your experience with other email clients is as smooth as possible, please check out my &lt;a href=&quot;https:&#x2F;&#x2F;yarmo.eu&#x2F;blog&#x2F;email-dns&quot;&gt;post on email server DNS settings&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With all that being said, I still use a protonmail address for critical websites and services like governmental services and banking, because whatever happens, I need to make sure that I really receive these emails. On my selfhosted email server, I use two domains: one which I share with the world and with websites for logins and one that I keep private and only use for direct communication with other people. I have yet to experience a single minute of outage, credits to digitalocean and the people behind mailcow.&lt;&#x2F;p&gt;
&lt;p&gt;---UPDATE---&lt;&#x2F;p&gt;
&lt;p&gt;After a &lt;a href=&quot;https:&#x2F;&#x2F;fosstodon.org&#x2F;@Matter&#x2F;104099349377193869&quot;&gt;fair comment&lt;&#x2F;a&gt; on the fediverse, I have written a &lt;a href=&quot;&#x2F;blog&#x2F;selfhost-email-drawbacks&quot;&gt;follow-up post&lt;&#x2F;a&gt; to address a few more critical points like server reputation and how I deal with that.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The People&#x27;s Web</title>
        <published>2020-04-30T08:53:37+00:00</published>
        <updated>2020-04-30T08:53:37+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/peoples-web/"/>
        <id>https://yarmo.eu/blog/peoples-web/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/peoples-web/">&lt;p&gt;&lt;code&gt;#100DaysToOffload &amp;gt;&amp;gt; 2020-04-30 &amp;gt;&amp;gt; 006&#x2F;100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The day has long past that we should have started worrying about the openness of our web. It was only a matter of time before censorship and individual tracking would seep into the web of the western world. It pained me to see the levels of state interference in foreign countries, but at least, to me, that was something I only read about in the news, it was then unimaginable that this would happen in the short term in the &amp;quot;free world&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Now, in these trying times, states and big corporations may see it more than justified to track individuals and adjust the information we receive. I do not question their motives: &amp;quot;we are in this together&amp;quot;. It takes the world to fight this pandemic, and the next one, and the one after that.&lt;&#x2F;p&gt;
&lt;p&gt;But the question on a lot of minds is: &amp;quot;how do we come back from this?&amp;quot; The hardest part is for the people to accept they are being tracked and their information filtered, so big corporations did it with shady and hidden tactics to avoid this confrontation. We are past this: YouTube has announced it will ban all content that does not conform to WHO and countries everywhere are building apps to track our health and social interactions. Again, this could prove to be what humanity needs right now, but what about after? Is there even an after?&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, we all have the power to make a few changes to improve our online well-being: change social networks, don&#x27;t rely on corporations, self-host as much as you can. I will be dedicating a large number of posts on this topic. Self-hosting is not hard, it just takes a little effort to get started.&lt;&#x2F;p&gt;
&lt;p&gt;As a friendly reminder, this website (blog included) has no tracking whatsoever: I do not care who you are, where you are from or with how many you are reading this post.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Selfhosted email: DNS records</title>
        <published>2020-02-18T18:39:54+00:00</published>
        <updated>2020-02-18T18:39:54+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yarmo.eu/blog/email-dns/"/>
        <id>https://yarmo.eu/blog/email-dns/</id>
        
        <content type="html" xml:base="https://yarmo.eu/blog/email-dns/">&lt;p&gt;When selfhosting email, an essential element to get right are the DNS records. Some are absolutely mandatory for email to work, some build trust and some just make life easier. Here&#x27;s an overview of how I set up DNS for my personal mail server.&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h2 id=&quot;My_setup&quot;&gt;My setup&lt;&#x2F;h2&gt;
&lt;p&gt;I have a VPS server running &lt;a href=&quot;https:&#x2F;&#x2F;mailcow.email&#x2F;&quot;&gt;mailcow&lt;&#x2F;a&gt; and two domains: one linking to the mail server, admin page and the web client (let&#x27;s call this &lt;code&gt;mail.server.domain&lt;&#x2F;code&gt;), and the other just being a email domain used in the email address (let&#x27;s call this &lt;code&gt;public.domain&lt;&#x2F;code&gt;, so the email address would be &lt;code&gt;hi@public.domain&lt;&#x2F;code&gt;). This way, even if you know the email domain, you don&#x27;t directly know server domain. Granted, one could find this out by looking at a few DNS records.&lt;&#x2F;p&gt;
&lt;p&gt;The benefit is that I can host email for multiple domains, as long as they all point to &lt;code&gt;mail.server.domain&lt;&#x2F;code&gt; by using the correct DNS records.&lt;&#x2F;p&gt;
&lt;p&gt;Please note that using a single domain is just as easy, as &lt;code&gt;mail.server.domain&lt;&#x2F;code&gt; and &lt;code&gt;public.domain&lt;&#x2F;code&gt; will simply be the same. Another scenario for which you could use the 2-domain setup is when you want your email address to be &lt;code&gt;hi@public.domain&lt;&#x2F;code&gt; (without subdomain) but wish to put the mail server (and&#x2F;or web client) on &lt;code&gt;mail.public.domain&lt;&#x2F;code&gt; (with subdomain).&lt;&#x2F;p&gt;
&lt;p&gt;I use &lt;a href=&quot;https:&#x2F;&#x2F;www.digitalocean.com&#x2F;docs&#x2F;networking&#x2F;dns&#x2F;&quot;&gt;DigitalOcean&lt;&#x2F;a&gt; as my VPS and DNS server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Mandatory_DNS_records_for_server.domain&quot;&gt;Mandatory DNS records for server.domain&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;A_records&quot;&gt;A records&lt;&#x2F;h3&gt;
&lt;p&gt;A records link domain names to IP addresses. When you want to use the admin page or web client provided by your email selfhosting software, the browser needs to know the IP address of the server&#x2F;VPS and that is what the A record is used for. A records are not used by mail servers when sending or receiving emails.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;TYPE  HOSTNAME            VALUE    TTL
&lt;&#x2F;span&gt;&lt;span&gt;A     mail.server.domain  1.2.3.4  3600
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;Mandatory_DNS_records_for_public.domain&quot;&gt;Mandatory DNS records for public.domain&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;MX_records&quot;&gt;MX records&lt;&#x2F;h3&gt;
&lt;p&gt;MX records tell other mail servers where to actually send the emails. In my case, my email address is &lt;code&gt;hi@public.domain&lt;&#x2F;code&gt; but my mail server is located at &lt;code&gt;mail.server.domain&lt;&#x2F;code&gt;. Other mail servers look at the address, see &lt;code&gt;public.domain&lt;&#x2F;code&gt; and will assume this is our mail server. We use MX records to direct the emails to &lt;code&gt;mail.server.domain&lt;&#x2F;code&gt; instead.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;TYPE  HOSTNAME       VALUE               PRIORITY  TTL
&lt;&#x2F;span&gt;&lt;span&gt;MX    public.domain  mail.server.domain  1         14400
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;Optional_DNS_records_for_public.domain&quot;&gt;Optional DNS records for public.domain&lt;&#x2F;h2&gt;
&lt;p&gt;A and MX records is all you need to get a functional email address. However, for ease of use and good reputation&#x2F;trust, a few additional DNS records are recommended.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;SRV_records_(ease_of_use)&quot;&gt;SRV records (ease of use)&lt;&#x2F;h3&gt;
&lt;p&gt;SRV records are used to link specific protocols to specific domains and ports. Just like how MX records tell other mail servers to direct their mails to your mail server on a different domain, the same must be done for mail clients. Say you want to use Thunderbird (or any other mail client) to access your emails. You will log in with your address (&lt;code&gt;hi@public.domain&lt;&#x2F;code&gt;) and password in Thunderbird, and it will then assume your mail server must be located at &lt;code&gt;public.domain&lt;&#x2F;code&gt;. It will not find it there, warn you about this and you will have to manually enter your IMAP and SMTP server details. If you have set up SRV records, Thunderbird will automatically detect the correct server location (&lt;code&gt;mail.server.domain&lt;&#x2F;code&gt;) and save you some hassle.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;TYPE  HOSTNAME            VALUE               PORT  PRIORITY  WEIGHT  TTL
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _imap._tcp          mail.server.domain  143   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _imaps._tcp         mail.server.domain  993   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _pop3._tcp          mail.server.domain  110   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _pop3s._tcp         mail.server.domain  995   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _submission._tcp    mail.server.domain  587   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _smtps._tcp         mail.server.domain  465   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _sieve._tcp         mail.server.domain  4190  1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _autodiscover._tcp  mail.server.domain  443   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _carddavs._tcp      mail.server.domain  443   1         100     14400
&lt;&#x2F;span&gt;&lt;span&gt;SRV   _caldavs._tcp       mail.server.domain  443   1         100     14400
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;TXT_records_(good_reputation)&quot;&gt;TXT records (good reputation)&lt;&#x2F;h3&gt;
&lt;p&gt;TXT records are simply messages that provide additional information. Here, TXT records are used to tell other mail servers more about your own mail server in order to build some trust between them: these records are a useful tool against spoofing where bad actors try to impersonate you and pretend you are sending the bad emails they are sending.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;TYPE  HOSTNAME         VALUE
&lt;&#x2F;span&gt;&lt;span&gt;TXT   @                &amp;quot;v=spf1 mx ~all&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;TXT   dkim._domainkey  &amp;quot;v=DKIM1;k=rsa;t=s;s=email;p=...&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;TXT   _dmarc           &amp;quot;v=DMARC1;p=reject;rua=mailto:admin@public.domain&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Detailed information on these records can be found &lt;a href=&quot;https:&#x2F;&#x2F;www.skelleton.net&#x2F;2015&#x2F;03&#x2F;21&#x2F;how-to-eliminate-spam-and-protect-your-name-with-dmarc&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;, but in short:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;SPF records tells other mail servers that only the specified mail server (in this case, &lt;code&gt;mx&lt;&#x2F;code&gt; which points to &lt;code&gt;mail.server.domain&lt;&#x2F;code&gt; via the MX record) is allowed to send emails for your email domain (in this case, &lt;code&gt;public.domain&lt;&#x2F;code&gt;);&lt;&#x2F;li&gt;
&lt;li&gt;DKIM applies a virtual signature to all your sent emails and other mail servers use the second TXT record above to validate that signature;&lt;&#x2F;li&gt;
&lt;li&gt;DMARC records tell other mail servers what should happen to emails that fail the SPF and DKIM policies; the record above states these emails should be rejected and a notification sent to &lt;code&gt;admin@public.domain&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The DKIM record contains a cryptographic key (replaced above by &lt;code&gt;...&lt;&#x2F;code&gt;). In my case, this key was generated for me by mailcow and is unique for each email domain.&lt;&#x2F;p&gt;
&lt;p&gt;Please note that having the above TXT records does not guarantee that other servers trust you immediately: your emails are still likely to end up in spam folders at first. Using intermediaries like &lt;a href=&quot;https:&#x2F;&#x2F;www.mailgun.com&#x2F;&quot;&gt;mailgun&lt;&#x2F;a&gt; can help with avoiding the spam folder. More on that in a later blog post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;References&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;p&gt;Mailcow has their own &lt;a href=&quot;https:&#x2F;&#x2F;mailcow.github.io&#x2F;mailcow-dockerized-docs&#x2F;prerequisite-dns&#x2F;&quot;&gt;recommended DNS records guide&lt;&#x2F;a&gt; which, in conjunction with their admin page, should make setting up DNS records a breeze.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.skelleton.net&#x2F;2015&#x2F;03&#x2F;21&#x2F;how-to-eliminate-spam-and-protect-your-name-with-dmarc&#x2F;&quot;&gt;This guide&lt;&#x2F;a&gt; has a lot of in-depth information about the SPF, DKIM and DMARC records (TXT records above).&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
