<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Self-Hosting on UtilyNest</title>
    <link>https://www.utilynest.com/tags/self-hosting/</link>
    <description>Smart guides, tips, and reviews to help you choose the best software, platforms, and utilities online.</description>
    <generator>Hugo -- 0.146.0</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 24 Jul 2026 16:05:33 +0000</lastBuildDate>
    <atom:link href="https://www.utilynest.com/tags/self-hosting/index.xml" rel="self" type="application/rss+xml" />
    <atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com" />
    <item>
      <title>Build a Personalized, Self-Hosted Habit Tracker</title>
      <link>https://www.utilynest.com/blog/build-a-personalized-self-hosted-habit-tracker/</link>
      <pubDate>Fri, 24 Jul 2026 16:05:21 +0000</pubDate>
      <guid>https://www.utilynest.com/blog/build-a-personalized-self-hosted-habit-tracker/</guid>
      <description>** Tired of rigid habit trackers that add pressure? Learn to create your own flexible, self-hosted solution to track daily tasks without stress.</description>
      <content:encoded><![CDATA[<hr>
<h2 id="the-problem-pressure-from-rigid-habit-trackers">The Problem: Pressure from Rigid Habit Trackers</h2>
<p>Imagine this: You&rsquo;re excited to start a new habit, so you download a popular habit tracker. You set your goals, but after a few missed days, you feel guilty and pressured to keep up. The app&rsquo;s rigid structure doesn&rsquo;t fit your lifestyle, making it harder to stick with your goals. This frustration is common, leading many to abandon habit tracking altogether.</p>
<h2 id="diagnosis-the-root-of-the-problem">Diagnosis: The Root of the Problem</h2>
<p>The issue lies in the inflexibility of most habit trackers. They often impose strict routines and frequent reminders, which can be overwhelming. Users need a tool that adapts to their lifestyle, offering flexibility without the pressure of rigid tracking.</p>
<h2 id="the-solution-build-your-own-self-hosted-tracker">The Solution: Build Your Own Self-Hosted Tracker</h2>
<p>Creating your own tracker allows you to customize it to fit your needs perfectly. Let&rsquo;s guide you through building a simple, self-hosted habit tracker using Docker, Nuxt.js, Express.js, and PostgreSQL.</p>
<h3 id="step-1-set-up-your-development-environment">Step 1: Set Up Your Development Environment</h3>
<p>Ensure you have Docker installed. This will simplify setting up your development environment.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker --version
</span></span></code></pre></div><h3 id="step-2-choose-your-tech-stack">Step 2: Choose Your Tech Stack</h3>
<p>For this project, we&rsquo;ll use:</p>
<ul>
<li><strong>Frontend:</strong> Nuxt.js with Vuetify for a clean, responsive UI.</li>
<li><strong>Backend:</strong> Express.js with TypeScript for strong API handling.</li>
<li><strong>Database:</strong> PostgreSQL for reliable data storage.</li>
<li><strong>Hosting:</strong> Docker Compose for local development and Docker for deployment.</li>
</ul>
<h3 id="step-3-initialize-your-project">Step 3: Initialize Your Project</h3>
<p>Create a new directory for your project and initialize it with npm.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir habit-tracker <span style="color:#f92672">&amp;&amp;</span> cd habit-tracker
</span></span><span style="display:flex;"><span>npm init -y
</span></span></code></pre></div><h3 id="step-4-set-up-docker">Step 4: Set Up Docker</h3>
<p>Create a <code>docker-compose.yml</code> file to define your services.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#39;3.8&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">postgres</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:13</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">habituser</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">habitpass</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">habitdb</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;5432:5432&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">postgres_data:/var/lib/postgresql/data</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">client</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">node:16-alpine</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">working_dir</span>: <span style="color:#ae81ff">/app</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./:/app</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;8080:8080&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">command</span>: <span style="color:#ae81ff">sh -c &#34;npm install &amp;&amp; npm run dev&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">postgres_data</span>:
</span></span></code></pre></div><h3 id="step-5-initialize-nuxtjs-project">Step 5: Initialize Nuxt.js Project</h3>
<p>Install Nuxt.js and its dependencies.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install nuxt @nuxtjs/axios @nuxtjs/auth-next @nuxtjs/vuetify
</span></span></code></pre></div><h3 id="step-6-create-the-frontend">Step 6: Create the Frontend</h3>
<p>Set up your Nuxt.js project with Vuetify for a modern UI.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// nuxt.config.js
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">buildModules</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;@nuxtjs/vuetify&#39;</span>,
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">modules</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;@nuxtjs/axios&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;@nuxtjs/auth-next&#39;</span>,
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">vuetify</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">theme</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">dark</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="step-7-set-up-the-backend">Step 7: Set Up the Backend</h3>
<p>Create an Express.js server to handle API requests.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// server.js
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">express</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;express&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">cors</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;cors&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">helmet</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;helmet&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">morgan</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;morgan&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">app</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">express</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">helmet</span>());
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">cors</span>());
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">morgan</span>(<span style="color:#e6db74">&#39;dev&#39;</span>));
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#a6e22e">express</span>.<span style="color:#a6e22e">json</span>());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Routes
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">use</span>(<span style="color:#e6db74">&#39;/api&#39;</span>, <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;./routes&#39;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">PORT</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">env</span>.<span style="color:#a6e22e">PORT</span> <span style="color:#f92672">||</span> <span style="color:#ae81ff">3000</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">app</span>.<span style="color:#a6e22e">listen</span>(<span style="color:#a6e22e">PORT</span>, () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">`Server running on port </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">PORT</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h3 id="step-8-set-up-postgresql">Step 8: Set Up PostgreSQL</h3>
<p>Create a <code>.env</code> file for your database credentials.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>DB_HOST<span style="color:#f92672">=</span>postgres
</span></span><span style="display:flex;"><span>DB_USER<span style="color:#f92672">=</span>habituser
</span></span><span style="display:flex;"><span>DB_PASSWORD<span style="color:#f92672">=</span>habitpass
</span></span><span style="display:flex;"><span>DB_NAME<span style="color:#f92672">=</span>habitdb
</span></span></code></pre></div><h3 id="step-9-create-the-database-schema">Step 9: Create the Database Schema</h3>
<p>Use Prisma ORM for database interactions.</p>
<pre tabindex="0"><code class="language-prisma" data-lang="prisma">// prisma/schema.prisma
generator client {
  provider = &#34;prisma-client-js&#34;
}

datasource db {
  provider = &#34;postgresql&#34;
  url      = &#34;postgresql://habituser:habitpass@postgres:5432/habitdb&#34;
}

model Habit {
  id        Int      @id @default(autoincrement())
  name      String
  frequency Int      @default(1)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
</code></pre><h3 id="step-10-set-up-cron-jobs">Step 10: Set Up Cron Jobs</h3>
<p>Use a cron job to remind you of your habits without pressure.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>crontab -e
</span></span></code></pre></div><p>Add a line to trigger a notification script every morning.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">7</span> * * * /path/to/notification-script.sh
</span></span></code></pre></div><h2 id="common-pitfalls-avoiding-mistakes">Common Pitfalls: Avoiding Mistakes</h2>
<ul>
<li><strong>Choosing an Overly Complex Stack:</strong> Start simple and expand as needed.</li>
<li><strong>Ignoring Security:</strong> Always secure your application, especially if hosting publicly.</li>
<li><strong>Not Testing Cron Jobs:</strong> Ensure your cron job works by testing it locally.</li>
</ul>
<h2 id="verification-ensuring-everything-works">Verification: Ensuring Everything Works</h2>
<p>After deployment, check your application&rsquo;s status and logs.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker ps
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs -f habit-tracker_postgres_1
</span></span></code></pre></div><h2 id="going-further-enhancing-your-tracker">Going Further: Enhancing Your Tracker</h2>
<p>Consider adding features like analytics or social sharing to enhance your experience.</p>
<hr>
<p>By following these steps, you&rsquo;ve created a flexible, self-hosted habit tracker tailored to your needs. This solution reduces pressure and increases adaptability, helping you maintain your habits effortlessly.</p>
]]></content:encoded>
      <category>** productivity</category>
      <category>self-hosting</category>
      <category>habit tracking</category>
    </item>
  </channel>
</rss>
