<?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"><channel><title><![CDATA[The Open Source Lab]]></title><description><![CDATA[The Open Source Lab]]></description><link>https://hughj.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 21:35:12 GMT</lastBuildDate><atom:link href="https://hughj.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Vaultwarden On Raspberry Pi]]></title><description><![CDATA[Self‑hosting your own password manager is one of the most empowering projects you can take on, especially if you are already running services on a Raspberry Pi. For this setup, I decided to host an open-source version of Bitwarden using Vaultwarden. ...]]></description><link>https://hughj.dev/vaultwarden-on-raspberry-pi</link><guid isPermaLink="true">https://hughj.dev/vaultwarden-on-raspberry-pi</guid><category><![CDATA[vaultwarden]]></category><category><![CDATA[password manager]]></category><category><![CDATA[self-hosted]]></category><dc:creator><![CDATA[Hugh S]]></dc:creator><pubDate>Sun, 04 Jan 2026 22:50:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Jj3-Wh4MHs4/upload/d25c4c53dec7356705295244c42e6dab.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Self‑hosting your own password manager is one of the most empowering projects you can take on, especially if you are already running services on a Raspberry Pi. For this setup, I decided to host an open-source version of Bitwarden using Vaultwarden. My goal is to give you a clean, straightforward guide that reflects what actually worked for me, including the mistakes I made and the lessons I learned.</p>
<p>Before we get into the steps, here is a quick look at how I got started.</p>
<h2 id="heading-how-i-found-vaultwarden">How I Found Vaultwarden</h2>
<p>I first came across the Vaultwarden repo sometime in the summer of 2025. After watching a YouTube tutorial, I figured it was something I could handle, even though I was still pretty new to Docker. I tried running the script directly from the README:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">pull</span> <span class="hljs-string">vaultwarden/server:latest</span>
<span class="hljs-string">docker</span> <span class="hljs-string">run</span> <span class="hljs-string">--detach</span> <span class="hljs-string">--name</span> <span class="hljs-string">vaultwarden</span> <span class="hljs-string">\</span>
  <span class="hljs-string">--env</span> <span class="hljs-string">DOMAIN="https://vw.domain.tld"</span> <span class="hljs-string">\</span>
  <span class="hljs-string">--volume</span> <span class="hljs-string">/vw-data/:/data/</span> <span class="hljs-string">\</span>
  <span class="hljs-string">--restart</span> <span class="hljs-string">unless-stopped</span> <span class="hljs-string">\</span>
  <span class="hljs-string">--publish</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">:8000:80</span> <span class="hljs-string">\</span>
  <span class="hljs-string">vaultwarden/server:latest</span>
</code></pre>
<p>It worked in the sense that I could see the login screen when I navigated to the resource and port. But I could not actually log in or access anything, as you can see here:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767486027438/24517ed4-11a1-4fd8-aeb2-73407dc96b0d.png" alt class="image--center mx-auto" /></p>
<p>I searched around for a while but did not have the time to dig deeper. I eventually moved on to other projects like Portainer and SterlingPDF.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you start, here is what you need:</p>
<ul>
<li><p>Raspberry Pi running Raspberry Pi OS</p>
</li>
<li><p>Docker and Docker Compose installed</p>
</li>
<li><p>A Cloudflare account with a registered domain</p>
</li>
<li><p>Basic familiarity with SSH</p>
</li>
<li><p>A willingness to troubleshoot a bit</p>
</li>
</ul>
<p>This setup is beginner friendly, but having these pieces in place will save you time.</p>
<h2 id="heading-returning-to-the-project">Returning to the Project</h2>
<p>I came back to this project at the end of 2025 around New Year's weekend. This time, I switched to Docker Compose and cleaned up the configuration. Here is the actual file I used:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">vaultwarden:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">vaultwarden/server:alpine</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">vaultwarden</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">DOMAIN:</span> <span class="hljs-string">"https://pass.example.com"</span>
      <span class="hljs-attr">SIGNUPS_ALLOWED:</span> <span class="hljs-string">"true"</span> <span class="hljs-comment"># Set to false after creating your account</span>
      <span class="hljs-attr">ADMIN_TOKEN:</span> <span class="hljs-string">${ADMIN_TOKEN}</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./vw-data:/data</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">11001</span><span class="hljs-string">:80</span>
</code></pre>
<h3 id="heading-why-alpine-matters-on-raspberry-pi">Why Alpine Matters on Raspberry Pi</h3>
<p>This is one of the biggest gotchas. Raspberry Pi OS uses ARM architecture. The <code>latest</code> Vaultwarden image is built for x86 systems, so it will not run correctly on a Pi. The Alpine variant supports ARM out of the box, which is why <code>vaultwarden/server:alpine</code> is required.</p>
<h3 id="heading-why-https-is-required">Why HTTPS Is Required</h3>
<p>Vaultwarden does not support HTTP for normal operation. It needs to know it is behind HTTPS so that attachments, WebSockets, and the web vault function correctly. If you try to access it over plain HTTP, you will run into the same issues I did during my first attempt.</p>
<h3 id="heading-running-the-container">Running the Container</h3>
<p>I started the container with:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">compose</span> <span class="hljs-string">up</span> <span class="hljs-string">-d</span>
</code></pre>
<p>And checked logs with:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">logs</span> <span class="hljs-string">-f</span> <span class="hljs-string">vaultwarden</span>
</code></pre>
<p>This step is optional but helpful for catching errors early.</p>
<h2 id="heading-why-i-chose-cloudflare-tunnels">Why I Chose Cloudflare Tunnels</h2>
<p>Most tutorials use Caddy or Nginx Proxy Manager. I already had a Cloudflare Tunnel running for my NocoDB project and liked the simplicity of outbound-only connections. No port forwarding, no exposing your home network, and Cloudflare handles the heavy lifting.</p>
<p>In my first project, I installed the Cloudflare Tunnel via the CLI. This time, I wanted to use the Cloudflare One Dashboard. My domain was already registered with Cloudflare, so I logged into Cloudflare One.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767562423389/fa88030c-5ed6-4bff-a772-72aa8e5d9f96.png" alt class="image--center mx-auto" /></p>
<p>In the Zero Trust Platform, click on "Networks".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767578358422/f5dffaff-2094-4e65-b2cd-fceaff8d0070.jpeg" alt class="image--center mx-auto" /></p>
<p>Under Networks, go to “Manage Tunnels” and set up a tunnel if you do not already have one. The process is straightforward.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767564757437/2669c9d4-f8c3-46bf-af24-839abde83451.png" alt class="image--center mx-auto" /></p>
<p>From here, manage the routed hostnames as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767564999285/1369c1ab-9ffe-4409-a79e-f0958edae4f5.png" alt class="image--center mx-auto" /></p>
<p>Cloudflare will automatically create a CNAME record in your DNS settings. Within a few seconds, you will be able to navigate to your Vaultwarden instance securely.</p>
<p>This method works for almost any service running on a port. I prefer it over opening ports on my router, which can introduce security risks. Cloudflare Tunnels create an outbound-only connection from your device to Cloudflare and rely on their global network to handle the rest.</p>
<h2 id="heading-backup-reminder">Backup Reminder</h2>
<p>Your entire Vaultwarden instance lives in the <code>/data</code> directory. Back it up regularly. A simple file-level backup is enough to restore your vault if anything goes wrong.</p>
<h2 id="heading-security-tips">Security Tips</h2>
<p>A few quick reminders:</p>
<ul>
<li><p>Disable signups after creating your account.</p>
</li>
<li><p>Store your <code>ADMIN_TOKEN</code> in an <code>.env</code> file instead of hardcoding it.</p>
</li>
<li><p>Keep your Pi updated.</p>
</li>
<li><p>Use strong passwords for your Cloudflare account and enable 2FA.</p>
</li>
</ul>
<p>Small steps like these go a long way.</p>
<h2 id="heading-troubleshooting-tips">Troubleshooting Tips</h2>
<p>Here are a few common issues you might run into:</p>
<ul>
<li><p>Using the wrong image on Raspberry Pi</p>
</li>
<li><p>DOMAIN not set or set incorrectly</p>
</li>
<li><p>Tunnel routing to the wrong port</p>
</li>
<li><p>Trying to access Vaultwarden over HTTP</p>
</li>
<li><p>Missing or incorrect volume paths</p>
</li>
</ul>
<p>Checking logs usually points you in the right direction.</p>
<h2 id="heading-whats-next">What’s Next</h2>
<p>I will create another post soon about generating the keys needed to securely enable the Admin page. I also plan to cover backups, token rotation, and a few other Vaultwarden best practices.</p>
<p>If you are self-hosting other tools on your Pi, Cloudflare Tunnels can simplify your entire setup. This approach has been reliable for me, and I hope this guide helps you get your own instance running smoothly.</p>
]]></content:encoded></item><item><title><![CDATA[Nocodb on Raspberry Pi]]></title><description><![CDATA[Intro
Recently, I discovered Nocodb while searching for alternatives to AirTable. I needed a solution for a work-related problem and initially created a database on AirTable. However, I was hesitant to pay for it when a great open-source alternative ...]]></description><link>https://hughj.dev/nocodb</link><guid isPermaLink="true">https://hughj.dev/nocodb</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[Databases]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[S3-bucket]]></category><dc:creator><![CDATA[Hugh S]]></dc:creator><pubDate>Fri, 04 Jul 2025 04:01:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ZfVyuV8l7WU/upload/ab10fe3e4d1771682dd9debc1327a282.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-intro">Intro</h3>
<p>Recently, I discovered Nocodb while searching for alternatives to AirTable. I needed a solution for a work-related problem and initially created a database on AirTable. However, I was hesitant to pay for it when a great open-source alternative might be available. Nocodb emerged as a top choice, leading me to transition away from AirTable. I soon realized that Nocodb could also be useful for a personal project, which I will guide you through here.</p>
<h3 id="heading-setup">Setup</h3>
<p>I used <a target="_blank" href="https://www.docker.com/">Docker</a> on my Raspberry Pi 5 to install it with SQLite.</p>
<pre><code class="lang-bash">docker run -d --name nocodb \
-v <span class="hljs-string">"<span class="hljs-subst">$(pwd)</span>"</span>/nocodb:/usr/app/data/ \
-p 8080:8080 \
nocodb/nocodb:latest
</code></pre>
<p>After successful installation, I ran the command <code>docker start nocodb</code> to get access to the database. I used the <code>&lt;pi-ip-address:8080&gt;</code>, created a master username and password, and then created the initial tables and schema. The Initial setup was straightforward.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751677709262/8f23ef0f-d744-4204-b306-58761c1dc7ad.png" alt="Local Access" class="image--center mx-auto" /></p>
<p>After about a day of playing with the database on my local network, I quickly realized that I might need to access this outside my network. What if I’m on the road and want to add something to my database? Or, what if I wanted to invite someone else to add stuff to my database?</p>
<h3 id="heading-remote-access">Remote access</h3>
<p>I had a few options: port forwarding, VPN, hosting on the cloud, or Cloudflare Tunnel. I didn’t want to do port forwarding for a few reasons. For one, I’m not keen on exposing ports on my home network; it seems like a bad idea (though I may be wrong here). VPN wasn’t an option either, as it was cost-prohibitive. My goal for this setup was to keep things free if possible, and I wanted to host everything on my Raspberry Pi myself. That left <a target="_blank" href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/">Cloudflare Tunnel</a>.</p>
<p>Configuring Cloudflare Tunnel was surprisingly straightforward with the help of ChatGPT and online guides. I’m already a big fan of <a target="_blank" href="https://www.cloudflare.com/">Cloudflare</a>, and since my dev domain is hosted there, integrating the tunnel was even easier.</p>
<h2 id="heading-setting-up-cloudflare-tunnel">Setting Up Cloudflare Tunnel</h2>
<p>I installed the <a target="_blank" href="https://github.com/cloudflare/cloudflared"><code>cloudflared</code></a> daemon on my Raspberry Pi, then authenticated it with my Cloudflare account using:</p>
<pre><code class="lang-bash">cloudflared tunnel login
</code></pre>
<p>After creating the tunnel and configuring it to forward traffic to NocoDB’s local port (8080), I set up a DNS CNAME record on Cloudflare to point my subdomain to the tunnel endpoint.</p>
<p>This allowed me to securely access NocoDB remotely via HTTPS without exposing ports on my home network. On top of that, I configured the Raspberry Pi firewall <a target="_blank" href="https://help.ubuntu.com/community/UFW"><code>ufw</code></a> to only allow SSH traffic, blocking everything else. Inside NocoDB, I created user accounts with appropriate roles to restrict access as needed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751677858605/c1557224-38d0-4b70-8ff4-487d2834c395.png" alt="Accessing via domain" class="image--center mx-auto" /></p>
<h2 id="heading-integrating-cloudflare-r2-for-file-storage">Integrating Cloudflare R2 for File Storage</h2>
<p>I wanted to offload file uploads (images, documents) from my Pi to scalable cloud storage. Cloudflare R2’s S3-compatible API made it a natural fit.</p>
<p>I configured NocoDB’s Docker container with environment variables pointing to my R2 bucket:</p>
<pre><code class="lang-dockerfile">-e S3_ACCESS_KEY_ID=&lt;your-access-key-id&gt; \
-e S3_SECRET_ACCESS_KEY=&lt;your-secret-access-key&gt; \
-e S3_ENDPOINT=https://&lt;account-id&gt;.r2.cloudflarestorage.com \
-e S3_BUCKET=&lt;your-bucket-name&gt; \
-e S3_REGION=auto \
-e S3_FORCE_PATH_STYLE=true
</code></pre>
<p>Uploads now go directly to R2, keeping my Pi’s storage free and scalable.</p>
<h2 id="heading-backups-and-automation">Backups and Automation</h2>
<p>Before moving file storage to R2, I backed up my local SQLite database and uploaded folders. I used <a target="_blank" href="https://rclone.org/"><code>rclone</code></a> to sync my uploads folder to R2 manually, and then create a simple shell script to automate this process.</p>
<p>Scheduling that script with a cron job on the Pi ensured my uploads stay in sync without manual effort:</p>
<pre><code class="lang-bash">0 2 * * * /home/pi/sync-nocodb-uploads.sh &gt;&gt; /home/pi/cron-sync.log 2&gt;&amp;1
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>If you’re looking for an affordable, flexible way to run your own database accessible anywhere, give NocoDB on a Raspberry Pi with Cloudflare Tunnel and R2 a shot!</p>
]]></content:encoded></item></channel></rss>