<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Dan Jackson]]></title><description><![CDATA[System Administration & DevOps]]></description><link>https://danjackson.me/</link><image><url>https://danjackson.me/favicon.png</url><title>Dan Jackson</title><link>https://danjackson.me/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Thu, 28 May 2026 17:10:25 GMT</lastBuildDate><atom:link href="https://danjackson.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Paying The Bills (with Automation)]]></title><description><![CDATA[<p>Both my and my girlfriend get our wages paid into separate bank accounts. This is quite common for a lot of couples but can present quite an organizational challenge when you&apos;ve got joint bills to pay.</p><p>For example, does one person pay the mortgage and the other pay</p>]]></description><link>https://danjackson.me/paying-the-bills-with-automation/</link><guid isPermaLink="false">6263062a4c824100017340d5</guid><category><![CDATA[Homelab]]></category><category><![CDATA[SysAdmin]]></category><dc:creator><![CDATA[Dan Jackson]]></dc:creator><pubDate>Tue, 02 May 2023 21:02:26 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1608111283577-43d930222227?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1608111283577-43d930222227?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Paying The Bills (with Automation)"><p>Both my and my girlfriend get our wages paid into separate bank accounts. This is quite common for a lot of couples but can present quite an organizational challenge when you&apos;ve got joint bills to pay.</p><p>For example, does one person pay the mortgage and the other pay the food &amp; utility bills (I think this was how my dad arranged it when he was married), is this always fair? Or would you both pay X amount into a set account and then hope that the value doesn&apos;t change too much. </p><p>An alternative solution might be to have both wages paid into a single joint account and then divide the total income evenly once the bills have been processed but that can sometimes also be undesirable depending on how close you are with your partner and how large the pay gap is between you. </p><p>I bring this question up in conversation with friends sometimes and I get a range of responses so clearly everyone has a different approach to this problem </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://images.unsplash.com/photo-1634733988138-bf2c3a2a13fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" class="kg-image" alt="Paying The Bills (with Automation)" loading="lazy" width="4505" height="3006" srcset="https://images.unsplash.com/photo-1634733988138-bf2c3a2a13fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600 600w, https://images.unsplash.com/photo-1634733988138-bf2c3a2a13fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1000 1000w, https://images.unsplash.com/photo-1634733988138-bf2c3a2a13fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1600 1600w, https://images.unsplash.com/photo-1634733988138-bf2c3a2a13fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGJpbGxzfGVufDB8fHx8MTY1MDY1Njg5NA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2400 2400w" sizes="(min-width: 720px) 720px"><figcaption>Photo by <a href="https://unsplash.com/@towfiqu999999?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Towfiqu barbhuiya</a> / <a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Unsplash</a></figcaption></figure><h2 id="not-so-simple-after-all">Not so simple after all</h2><p>The approach I take is the &quot;both pay X amount into a joint account&quot; option. We have a joint account that is responsible for ensuring that the bills get paid and we top it up on payday to ensure there are funds in the account to pay for the bills when they are needed. This is really good for us as it means that a bulk of our wages are ring fenced away so that they cannot be spent on other items during the month. Whatever is left after payday is &quot;fair game&quot;.</p><p>This presents a few issues however</p><ul><li>How do we top it up? Bank Transfer or Standing Order?</li><li>What if the amount changes? How will we know?</li><li>Will we even remember to pay into this account?</li><li>How do we keep track of who pays what?</li></ul><p>I feel as though the system I currently use (and have been using for the last 2 years is the most simple solution to this issue for me). Here&apos;s the explanation:</p><h2 id="well-start-with-the-data">We&apos;ll start with the data</h2><p>To keep things simple here, I store all this data in Excel. Since we&apos;re storing a simple single table with generally no overlapping or relational elements, a simple flat file table is much easier here than any sort of complicated database. Here&apos;s what this looks like:</p><!--kg-card-begin: html--><table>
  <tr>
    <th>Bill</th>
    <th>Amount</th>
    <th>Owner</th>
    <th>Provider</th>
  </tr>
  <tr>
    <td>Mobile Phone</td>
    <td>20</td>
    <td>Dan</td>
    <td>The Blue Phone Company</td>
  </tr>
  <tr>
    <td>Car</td>
    <td>200</td>
    <td>Dan</td>
    <td>Super Car Finance Corp</td>
  </tr>
   <tr>
    <td>Mobile Phone</td>
    <td>30</td>
    <td>Mrs Dan</td>
    <td>The Purple Phone Company</td>
  </tr>
   <tr>
    <td>Mortgage</td>
    <td>500</td>
    <td>Joint</td>
    <td>Big Banker Plc</td>
  </tr>
</table><!--kg-card-end: html--><p>The provider field is surplus to requirements but it does help keep track of things.</p><p>With a couple of Excel formula&apos;s and we can find out exactly who owes what:</p><!--kg-card-begin: html--><pre><code class="language-xlsx">=(SUMIF(Table1[Owner],&quot;Joint&quot;,Table1[Amount])/2)+(SUMIF(Table1[Owner],[@People],Table1[Amount]))
</code></pre><!--kg-card-end: html--><p>In my example above, we&apos;d have 2 additional cells in our sheet with the formula calculating that Dan owes 450 whilst Mrs Dan owes 280</p><p>That&apos;s all pretty cool if this was the 90&apos;s and everyone still ran Windows 95. But it doesn&apos;t solve our main issues and that is:</p><ul><li>How do we update it easily?</li><li>How do we get reminded to pay it?</li></ul><h2 id="enter-nextcloud">Enter Nextcloud...</h2><p>We&apos;re not running Windows 95 anymore. We&apos;re running beefy AlmaLinux 8 servers with Docker. So how can I leverage that to allow me to update this from many machines. </p><p>For this, I use my Nextcloud container. </p><p>Nextcloud is a system that is similar to Dropbox or Google Drive. It&apos;s a system where changes can be made to a file on the local machine and these changes will get automatically uploaded to the Nextcloud server. Changes that are made server side are also synced locally back to the machine. I like to think of this concept almost like if IMAP and FTP had a baby except it all runs over HTTPS instead. </p><p>In my case, I would use this as one central location for my spreadsheet that can be accessed via my phone&apos;s native &quot;Files&quot; app whilst also appearing as a document on my computer. The easy part about this is that no matter which company contacts me to tell me that a bill has changed and no matter how they contact me, I can always ensure that I can update the spreadsheet with the correct information.</p><p>Consider the following:</p><p>You&apos;re in a car shop looking for a new car. With this system, you know how much you pay for your current car. You know how much you pay for all of your other bills. You know with a single change in an excel cell exactly what you would be paying in total if you bought the car and if you did buy the car, you could save the change to ensure that you&apos;re reminded to pay the bill upfront after pay day.</p><p>All sounds great so far, but how do we remember to pay it after payday?</p><h2 id="enter-node-red">Enter Node Red...</h2><p>Onto the reminder system, I used Node Red. It&apos;s a graphical system for arranging predefined function blocks that execute specific tasks. The Node Red <code>flow</code> I used for my reminder looks like so:</p><figure class="kg-card kg-image-card"><img src="https://danjackson.me/content/images/2022/04/nodered-1.png" class="kg-image" alt="Paying The Bills (with Automation)" loading="lazy" width="732" height="647" srcset="https://danjackson.me/content/images/size/w600/2022/04/nodered-1.png 600w, https://danjackson.me/content/images/2022/04/nodered-1.png 732w" sizes="(min-width: 720px) 720px"></figure><p>To explain this, the first part of the flow is a essentially a cronjob with specific formula&apos;s for mine and my girlfriend&apos;s payday. Whilst my payday is fairly regular, Mrs Dan&apos;s payday seems to be based on the whim of the finance team that week. Generally however it more or less the last working day before the 21st of the month so it can be entered programmatically. </p><p>Once the schedule is triggered using the Telegram chat ID as the ID for each person, the system will then query this information against a very basic JSON object (very crude I know) which also contains which excel cell the total value for each person is location on the spreadsheet.</p><p>The cell is then queried for it&apos;s value by taking the formulaic cell value directly from the spreadsheet. This is then formatted into a payload and sent directly to the Telegram user who&apos;s payday it is. A very basic but very reliable system for monthly bill&apos;s updates via a push notification. </p><p>This can also be queried manually by typing &quot;/bills&quot; to the telegram bot.</p><p>Once the alert comes through Telegram, the recipient can then log into the mobile banking and proceed with the transaction for the correct amount. The left over money is disposable income.</p><h2 id="closing-thoughts">Closing Thoughts</h2><p>This system is a very basic but surprisingly reliable solution to an organization problem. It&apos;s an easy way to relieve the mind of the stresses of managing bills. </p><p>My university lecturer once gave me the advice to &quot;program your future self&quot; as if your future self was some sort of zombie with no decision making capability. We&apos;re not always 100% switched on all of the time and sometimes we need to be told what to do, even if this is by our past self. To think about what bills need to be paid and when only serves to cause headaches and stress. </p><p>However to allow an automated system to manage this process for us means that we can take the lazy approach of ensuring that the money ends up in the right place at the right time and leaving the rest to pre-planned actions. </p><h3 id="future-plans">Future Plans</h3><p>For the future, I&apos;ve looked into ways to automate this further but as you can imagine, banking system&apos;s APIs are fairly locked down. For example, the Open Banking API allows you to be logged in but the log in only lasts a few months before you need to authenticate again. You also need to authenticate every transaction and the API is generally overly complicated and inaccessible to the general public without some FCA registered middleman system - so much for being &quot;open&quot;</p><p>There is one company however that do offer a potential silver lining to this issue and that is Monzo. Monzo appear to offer you standard run of the mill REST API which can be used to query accounts and to move money into &quot;pots&quot;. Not quite as good as moving money into different accounts but this pots idea might work. Is it enough for me to switch current accounts?</p><p>This means that the system might at least be able to send follow up reminders if the money hasn&apos;t been transferred with the ultimate goal of making the process as automated as possible. If only....</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://images.unsplash.com/photo-1623106405790-0ed93dd15bab?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI4fHxiYW5rZXJ8ZW58MHx8fHwxNjUwNjYxNzI2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" class="kg-image" alt="Paying The Bills (with Automation)" loading="lazy" width="6000" height="4000" srcset="https://images.unsplash.com/photo-1623106405790-0ed93dd15bab?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI4fHxiYW5rZXJ8ZW58MHx8fHwxNjUwNjYxNzI2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600 600w, https://images.unsplash.com/photo-1623106405790-0ed93dd15bab?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI4fHxiYW5rZXJ8ZW58MHx8fHwxNjUwNjYxNzI2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1000 1000w, https://images.unsplash.com/photo-1623106405790-0ed93dd15bab?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI4fHxiYW5rZXJ8ZW58MHx8fHwxNjUwNjYxNzI2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1600 1600w, https://images.unsplash.com/photo-1623106405790-0ed93dd15bab?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI4fHxiYW5rZXJ8ZW58MHx8fHwxNjUwNjYxNzI2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2400 2400w" sizes="(min-width: 720px) 720px"><figcaption>Photo by <a href="https://unsplash.com/@ibrahimboran?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Ibrahim Boran</a> / <a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Unsplash</a></figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Getting Started With This Site]]></title><description><![CDATA[<p>To start with this blog, I think it might be worth talking about this specific project; the website you are currently on.</p><p>To start with the basics, the server is hosted behind Cloudflare which I rate highly even just for it&apos;s API controlled DNS functionality. The server itself</p>]]></description><link>https://danjackson.me/getting-started-with-this-site/</link><guid isPermaLink="false">625f0ca84c82410001733df1</guid><category><![CDATA[Devops]]></category><category><![CDATA[Homelab]]></category><category><![CDATA[SysAdmin]]></category><dc:creator><![CDATA[Dan Jackson]]></dc:creator><pubDate>Tue, 19 Apr 2022 19:25:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1600887292404-ac6e38cf3302?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fHNwb29reXxlbnwwfHx8fDE2NTA0MDIzNDc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1600887292404-ac6e38cf3302?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fHNwb29reXxlbnwwfHx8fDE2NTA0MDIzNDc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Getting Started With This Site"><p>To start with this blog, I think it might be worth talking about this specific project; the website you are currently on.</p><p>To start with the basics, the server is hosted behind Cloudflare which I rate highly even just for it&apos;s API controlled DNS functionality. The server itself is a small virtual machine in some datacenter halfway across the world. It&apos;s quite cheap and as projects go on, I might find some way to take the whole thing &quot;serverless&quot;.</p><p>I find myself planning on what a page on my new site might contain. I imagine a &quot;Code Snippet&quot; might be pretty cool. A highlight of all of the Docker Compose iac or something? How do I even write a Code Block on Ghost?!</p><!--kg-card-begin: html--><pre>
  <code>
    I find myself trying to figure out how to write a code block. This one looks somewhat plain.
    
    Not to worry, it seems as though there&apos;s a tutorial I can follow:
    https://ghost.org/docs/tutorials/code-syntax-highlighting/
  </code>
</pre><!--kg-card-end: html--><p>^ This one is just some HTML. It&apos;s not even that intuitive. </p><p>The guide above says to use 3 backticks. Similar to markdown I guess:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">def hellocodeblock():
    print(&quot;Hey Look, it&apos;s a code block&quot;)</code></pre><figcaption>Well this is pretty</figcaption></figure><h2 id="docker-compose">Docker Compose</h2><p>With that out of the way, lets get into some Docker Compose. I generally will launch most of my projects using Docker. The reason? It&apos;s easier to clean up when I don&apos;t want it anymore and i change my mind a lot.</p><p>Lets say one day, I want to switch a service I&apos;m running. In my example, I don&apos;t want to use Keycloak anymore because I find it too overkill for my purposes. I want to swap it out for Authentik (more on this in a later post), can this even be done easily without Docker? Apt/Yum remove? What about left over files...</p><p>For this reason, I like to put all of my code into Docker and I use Docker Compose for the deployment. For this site, here is the code:</p><pre><code class="language-yaml">---
version: &apos;3.1&apos;

services:

  ghost:
    container_name: ghost
    image: ghost:4-alpine
    restart: unless-stopped
    ports:
      - 3008:2368
    volumes:
      - /opt/docker/ghost:/var/lib/ghost/content
    environment:
      database__client: mysql
      database__connection__host: mariadb
      database__connection__user: DATABASE_USERNAME
      database__connection__password: DATABASE_PASSWORD
      database__connection__database: DATABASE_NAME
      mail__transport: SMTP
      mail__options__host: MAILSERVER_IP
      mail__options__port: 25
      mail__options__auth__user: EMAIL_USERNAME
      mail__options__auth__pass: EMAIL_PASSWORD
      mail__from: Dan Jackson Ghost &lt;EMAIL_ADDRESS&gt;
      url: https://danjackson.me
    networks:
      - MySQL_Connect
      - frontend
networks:
  MySQL_Connect:
    external: true
  frontend:
    external: true
</code></pre><p>You might notice above that I&apos;ve created a bridge network called <code>frontend</code> and you might wonder why. The answers to that <a href="https://docs.docker.com/network/bridge/#differences-between-user-defined-bridges-and-the-default-bridge">question are found here</a></p><p>I&apos;ve got another network called <code>MySQL_Connect</code>. This is specifically to hold the connection to the MySQL database. The network is specified as internal only and is used to allow traffic to transmit between a single database instance on the Docker host. The MySQL container is not connected to any externally facing Docker network by design and therefore can only be accessed by other containers for additional security.</p><h2 id="reverse-proxy">Reverse Proxy</h2><p>Infront of every good HTTP based Docker system, there is some sort of reverse proxy to direct the traffic. In my case, despite years of using Nginx, I&apos;ve now switched to Caddy 2 for all of my traffic. </p><p>Why? </p><p>Painlessly easy for automated SSL. </p><p>Don&apos;t get me wrong, you can use both Certbot and Acme.sh for SSL certificates on Nginx. Both solutions support DNS validation too. These are both fairly easy but they both lack a certain instant automation. On Caddy 2 however, it couldn&apos;t be more easy. You simply add your Cloudflare API key to the Caddyfile and then define a domain</p><p>Here is that config:</p><figure class="kg-card kg-code-card"><pre><code class="language-json">{
    # email to use on Let&apos;s Encrypt
    email EMAILADDRESS

    acme_dns cloudflare SUPERSECRETAPIKEY
}
### SNIPPITS ###
(webconf) {
  encode gzip
}

(theheaders) {
    header_up X-Forwarded-Ssl on
    header_up Host {host}
    header_up X-Real-IP {remote}
    header_up X-Forwarded-For {remote_host}
    header_up X-Forwarded-Port {server_port}
    header_up X-Forwarded-Proto {scheme}
    header_up X-Url-Scheme {scheme}
    header_up X-Forwarded-Host {host}
}
danjackson.me {
   reverse_proxy http://127.0.0.1:3008 {
      import theheaders
    }
import webconf
}</code></pre><figcaption>Caddy file syntax doesn&apos;t get fancy highlighting it seems :(</figcaption></figure><p>...I probably don&apos;t even need the headers bit. </p><p>That&apos;s the basis of this Ghost installation. Following the above, I simply point ensure that Cloudflare is pointing at the right server. </p><h3 id="improvements">Improvements</h3><p>If you stumble across this and think you want to try this yourself, there are a couple of things with this that can be fixed. For example:</p><blockquote>reverse_proxy <a href="http://127.0.0.1:3008">http://127.0.0.1:3008</a></blockquote><p>There&apos;s nothing wrong with this Caddy code, however since both Caddy and Ghost are both in the <code>frontend</code> network, I don&apos;t need to open the port forwarding on the host AND I don&apos;t need to specify an IP address here, the container name would simply be enough. </p><p>Until next time.</p>]]></content:encoded></item></channel></rss>