<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sasanka Rath</title>
    <description>The latest articles on DEV Community by Sasanka Rath (@avgskully).</description>
    <link>https://dev.to/avgskully</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3830263%2Fde2eb63b-f9ea-4b8c-a9d6-f10b2b27d018.png</url>
      <title>DEV Community: Sasanka Rath</title>
      <link>https://dev.to/avgskully</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avgskully"/>
    <language>en</language>
    <item>
      <title>I Built an AI That Speaks Fluent LinkedIn (And It's Terrifying)</title>
      <dc:creator>Sasanka Rath</dc:creator>
      <pubDate>Sat, 04 Apr 2026 16:04:37 +0000</pubDate>
      <link>https://dev.to/avgskully/i-built-an-ai-that-speaks-fluent-linkedin-and-its-terrifying-25a7</link>
      <guid>https://dev.to/avgskully/i-built-an-ai-that-speaks-fluent-linkedin-and-its-terrifying-25a7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Every year, millions of perfectly normal human thoughts are needlessly transformed&lt;br&gt;
into impenetrable corporate speak. "I fixed a bug" becomes &lt;em&gt;"I synergized the&lt;br&gt;
codebase by disrupting legacy anti-patterns through agile remediation."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;BuzzwordAI™&lt;/strong&gt; — the world's first bidirectional enterprise jargon translation&lt;br&gt;
engine, built with the Google Gemini API. It translates plain English &lt;strong&gt;into&lt;/strong&gt; maximum&lt;br&gt;
corporate buzzword nonsense — and corporate buzzwords &lt;strong&gt;back&lt;/strong&gt; into plain English.&lt;br&gt;
It also generates a fake analytics panel after each translation: Buzzword Density,&lt;br&gt;
Clarity Score, LinkedIn Readiness, and a smug AI observation.&lt;/p&gt;

&lt;p&gt;There's a Jargon Level slider from 1 to 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 = Slightly formal
3 = LinkedIn post
5 = McKinsey consultant
7 = Fortune 500 CEO email
10 = Sentient buzzword crisis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And hidden in the corner is a 🫖 teapot button. Click it. You're welcome.&lt;/p&gt;

&lt;p&gt;We are solving a problem that AI helped create. &lt;strong&gt;With AI.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://buzzword-ai.sasrath.com" rel="noopener noreferrer"&gt;Live app — buzzword-ai.sasrath.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Try these inputs:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What you'll get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;I fixed a bug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;A McKinsey-grade synergy statement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;I need a nap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Existential corporate poetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Let's leverage synergies to unlock net new pipeline&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;— (Corporate→Human mode)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"We need some sales leads."&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sasrath" rel="noopener noreferrer"&gt;
        sasrath
      &lt;/a&gt; / &lt;a href="https://github.com/sasrath/buzzwordAI" rel="noopener noreferrer"&gt;
        buzzwordAI
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🔄 Because "I took a nap" deserves a quarterly  business review. Gemini AI + HTTP 418 + zero sense. Built for the dev.to April Fools Challenge 2026.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;buzzwordAI&lt;/h1&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sasrath/buzzwordAI" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;





&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I used this as an excuse to build something absurd with a production-grade stack.&lt;br&gt;
Because overengineering April Fools projects is on-brand.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Next.js 15 (App Router), React 19, TypeScript 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI&lt;/td&gt;
&lt;td&gt;Google Gemini 2.5 Flash-Lite (via server-side API proxy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;Tailwind CSS 3, self-hosted (13 KB, no CDN)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limiting&lt;/td&gt;
&lt;td&gt;Upstash Redis — sliding window 20 req/min per IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Strict CSP, 8 security headers, HSTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Vercel (serverless, 30 s timeout)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The interesting bits:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Gemini is server-side only.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The user's API key is sent in a request header to &lt;code&gt;/api/gemini&lt;/code&gt; — Gemini is called&lt;br&gt;
from Node.js with an &lt;code&gt;AbortController&lt;/code&gt; 25-second timeout. The key is never logged&lt;br&gt;
or stored server-side. The CSP enforces &lt;code&gt;connect-src 'self'&lt;/code&gt; so the browser&lt;br&gt;
literally cannot call the Gemini API directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Five distinct AI calls per translation.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One for the translation, one for analytics (buzzword density, clarity score, etc.),&lt;br&gt;
and a few for edge cases. Each has its own system prompt with carefully engineered&lt;br&gt;
output schemas so the analytics JSON doesn't end up as a philosophical essay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Redis rate limiting with an in-memory fallback.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;@upstash/ratelimit&lt;/code&gt; sliding window — 20 req/min per IP, max 3 concurrent. If the&lt;br&gt;
Redis env vars aren't set (e.g. local dev), it falls back silently to in-memory&lt;br&gt;
counters so development still works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. HTTP 418 Easter Egg.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The teapot button calls a special Gemini prompt that outputs a completely&lt;br&gt;
straight-faced corporate memo that is secretly about being a teapot. It always&lt;br&gt;
ends with &lt;code&gt;"Short and stout."&lt;/code&gt; This is a tribute to RFC 2324 and Larry Masinter —&lt;br&gt;
the author of Hyper Text Coffee Pot Control Protocol (HTCPCP).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Aurora UI with zero hydration mismatches.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The background has 4 animated CSS gradient blobs, 28 floating particle dots, and a&lt;br&gt;
cursor-tracking 600px spotlight. The particles are generated deterministically from&lt;br&gt;
index arithmetic (no &lt;code&gt;Math.random()&lt;/code&gt;) to avoid React SSR/hydration mismatches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;p&gt;Submitting for &lt;strong&gt;all three&lt;/strong&gt; — because why not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏆 Best Google AI Usage&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Gemini is used for five distinct tasks per session: main translation, reverse&lt;br&gt;
translation, AI analytics, the teapot easter egg, and the random-example seeder.&lt;br&gt;
The prompts are carefully engineered to produce structured JSON from a generative&lt;br&gt;
model consistently (temperature 1.0, strict output format instructions).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🫖 Best Ode to Larry Masinter&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The HTTP 418 "I'm a Teapot" easter egg is the heart of the app. Hidden teapot button&lt;br&gt;
→ modal quoting RFC 2324 → Gemini writes a corporate memo that is secretly a teapot&lt;br&gt;
confession → output always ends with &lt;em&gt;"Short and stout."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Larry Masinter deserves more credit for giving the internet its most beloved joke&lt;br&gt;
status code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❤️ Community Favorite&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Because everyone has read a corporate email that needed translating. This is therapy.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Access Your Home Server from Anywhere — Tailscale, Oracle VPS, and frp</title>
      <dc:creator>Sasanka Rath</dc:creator>
      <pubDate>Sat, 04 Apr 2026 12:12:11 +0000</pubDate>
      <link>https://dev.to/avgskully/access-your-home-server-from-anywhere-tailscale-oracle-vps-and-frp-4887</link>
      <guid>https://dev.to/avgskully/access-your-home-server-from-anywhere-tailscale-oracle-vps-and-frp-4887</guid>
      <description>&lt;p&gt;Part 4 of the home server series. Set up private remote access with Tailscale, a free Oracle VPS as a public gateway, frp tunneling to bypass CGNAT, Nginx reverse proxy, and SSL with Certbot.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/avgskully/setting-up-nextcloud-with-docker-on-an-old-laptop-your-own-google-drive-in-30-minutes-4ain"&gt;Part 2&lt;/a&gt; we set up Nextcloud, and in &lt;a href="https://dev.to/avgskully/turn-your-home-server-into-a-nas-access-files-from-any-device-with-samba-5fgl"&gt;Part 3&lt;/a&gt; we added Samba NAS. Everything works great — on your home network.&lt;/p&gt;

&lt;p&gt;But the moment you step outside your house, your server disappears. You can't reach it from a coffee shop, your office, or while traveling. Your phone's auto-backup stops working on mobile data.&lt;/p&gt;

&lt;p&gt;This part fixes that. After this guide, you'll access your server from &lt;strong&gt;anywhere in the world&lt;/strong&gt; through two paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private path (Tailscale):&lt;/strong&gt; A direct encrypted tunnel between your devices and your server. Fast, simple, free. For personal use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public path (Oracle VPS + frp):&lt;/strong&gt; A clean URL like &lt;code&gt;files.yourdomain.com&lt;/code&gt; that anyone can open in a browser. For sharing with family.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the most complex part of the series, but I'll walk through every step.&lt;/p&gt;




&lt;h2&gt;
  
  
  The CGNAT problem
&lt;/h2&gt;

&lt;p&gt;Before we start, you need to understand why this is necessary.&lt;/p&gt;

&lt;p&gt;Most Indian ISPs (Airtel, Jio, ACT, BSNL) use &lt;strong&gt;CGNAT&lt;/strong&gt; — Carrier-Grade NAT. This means your home doesn't have a public IP address. Hundreds of homes share one public IP. Nobody on the internet can reach your server directly.&lt;/p&gt;

&lt;p&gt;It's like living in an apartment building where the entire building has one phone number. Someone calling that number reaches the building's reception, not your flat. There's no way to ring your flat directly from outside.&lt;/p&gt;

&lt;p&gt;Two solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tailscale&lt;/strong&gt; — creates a private tunnel that bypasses CGNAT entirely. Only your devices can use it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oracle VPS + frp&lt;/strong&gt; — your server reaches out to a public VPS, and the VPS forwards traffic back through the tunnel. Anyone on the internet can access your server through the VPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's set up both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part A: Tailscale (Private Access)
&lt;/h2&gt;

&lt;p&gt;Tailscale is the easiest way to access your server remotely. It creates an encrypted WireGuard VPN mesh between all your devices. No port forwarding, no firewall rules, no configuration headaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install Tailscale on your server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://tailscale.com/install.sh | sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will print a URL. Open that URL in a browser and sign in with your Google or Microsoft account. Your server is now on your Tailscale network.&lt;/p&gt;

&lt;p&gt;Check your server's Tailscale IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tailscale ip &lt;span class="nt"&gt;-4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get something like &lt;code&gt;100.x.x.x&lt;/code&gt;. Note this down — this is your server's private remote address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Install Tailscale on your devices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mac/Windows:&lt;/strong&gt; Download from &lt;a href="https://tailscale.com/download" rel="noopener noreferrer"&gt;tailscale.com/download&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iPhone:&lt;/strong&gt; Download from the App Store&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android:&lt;/strong&gt; Download from the Play Store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sign in with the &lt;strong&gt;same account&lt;/strong&gt; you used on your server. That's it — your devices can now talk to each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Test it
&lt;/h3&gt;

&lt;p&gt;From your phone (on mobile data, not WiFi) or a laptop outside your home, try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://100.x.x.x:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;100.x.x.x&lt;/code&gt; with your server's Tailscale IP. You should see Nextcloud.&lt;/p&gt;

&lt;p&gt;For NAS access from your Mac:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;smb://100.x.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh your-username@100.x.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything works exactly like it does at home — because Tailscale makes it feel like you're on the same network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Allow Tailscale through the firewall
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow &lt;span class="k"&gt;in &lt;/span&gt;on tailscale0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows all traffic through the Tailscale interface while keeping your server locked down on the regular network.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;HTTP over Tailscale is completely safe.&lt;/strong&gt; WireGuard encrypts ALL traffic regardless of HTTP vs HTTPS. Your ISP can't see what you're accessing. You don't need SSL for the Tailscale path.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 5: Update Nextcloud trusted domains
&lt;/h3&gt;

&lt;p&gt;Add your Tailscale IP to Nextcloud's trusted domains so it doesn't block access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nextcloud bash
nano /var/www/html/config/config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your Tailscale IP to the array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'trusted_domains'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'192.168.1.100'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'files.yourdomain.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'100.x.x.x'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save, exit the container, and restart Nextcloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit
&lt;/span&gt;docker restart nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tailscale is done
&lt;/h3&gt;

&lt;p&gt;That's really it. Tailscale handles NAT traversal, encryption, key exchange, and DNS — all automatically. Your phone's Nextcloud auto-backup now works on mobile data too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Tailscale for:&lt;/strong&gt; Personal file access, SSH, streaming videos, NAS access. Anything where you're the only user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailscale can't do:&lt;/strong&gt; Give family members access without installing the app on their devices. That's what the public path is for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part B: Oracle VPS + frp (Public Access)
&lt;/h2&gt;

&lt;p&gt;This is the more complex setup, but it gives you a public URL like &lt;code&gt;files.yourdomain.com&lt;/code&gt; that anyone can open in a browser — no app required.&lt;/p&gt;

&lt;p&gt;The architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet → Cloudflare (DNS + SSL) → Oracle VPS → frp tunnel → Your home server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Get a free Oracle Cloud VPS
&lt;/h3&gt;

&lt;p&gt;Oracle Cloud has an &lt;strong&gt;Always Free&lt;/strong&gt; tier that gives you a VPS that never expires and never costs anything.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://cloud.oracle.com" rel="noopener noreferrer"&gt;cloud.oracle.com&lt;/a&gt; and create an account&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Compute → Instances → Create Instance&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose these settings:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shape:&lt;/strong&gt; VM.Standard.E2.1.Micro (this is the free one)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OS:&lt;/strong&gt; Ubuntu 22.04 or 24.04&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Region:&lt;/strong&gt; Pick the closest to you (Mumbai for India)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add your SSH public key&lt;/strong&gt; — if you don't have one, generate it:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On your Mac or local machine&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/oracle_key
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/oracle_key.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the public key and paste it into the Oracle instance creation form.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt; and wait for it to provision (takes 1-2 minutes)&lt;/li&gt;
&lt;li&gt;Note the &lt;strong&gt;public IP address&lt;/strong&gt; Oracle assigns&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Configure Oracle Cloud security rules
&lt;/h3&gt;

&lt;p&gt;Oracle blocks almost all ports by default. You need to open them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Oracle Console, go to your instance → &lt;strong&gt;Subnet&lt;/strong&gt; → &lt;strong&gt;Security Lists&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;Ingress Rules&lt;/strong&gt; for:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7000&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;frp server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Also open these ports in the VPS's own firewall:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH into your Oracle VPS&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/oracle_key ubuntu@your-oracle-vps-ip

&lt;span class="c"&gt;# Open ports&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 6 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; NEW &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 6 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; NEW &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 443 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 6 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; NEW &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 7000 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="nb"&gt;sudo &lt;/span&gt;netfilter-persistent save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Get a domain and set up Cloudflare
&lt;/h3&gt;

&lt;p&gt;You need a domain name to point to your VPS. A &lt;code&gt;.com&lt;/code&gt; domain costs about ₹850/year through Cloudflare Registrar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register a domain&lt;/strong&gt; on &lt;a href="https://www.cloudflare.com/products/registrar/" rel="noopener noreferrer"&gt;Cloudflare Registrar&lt;/a&gt; or any registrar (Namecheap, Porkbun, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add your domain to Cloudflare&lt;/strong&gt; (free plan) if you didn't register through them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an A record:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;files&lt;/code&gt; (this creates &lt;code&gt;files.yourdomain.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Content: your Oracle VPS public IP&lt;/li&gt;
&lt;li&gt;Proxy status: &lt;strong&gt;DNS only&lt;/strong&gt; (gray cloud) — important for frp to work&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Make sure your domain's nameservers point to Cloudflare&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 4: Install frp server on Oracle VPS
&lt;/h3&gt;

&lt;p&gt;frp (Fast Reverse Proxy) creates the tunnel between your VPS and home server.&lt;/p&gt;

&lt;p&gt;SSH into your Oracle VPS and install frp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download frp&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt
&lt;span class="nb"&gt;sudo &lt;/span&gt;wget https://github.com/fatedier/frp/releases/download/v0.61.0/frp_0.61.0_linux_amd64.tar.gz
&lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; frp_0.61.0_linux_amd64.tar.gz
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;frp_0.61.0_linux_amd64 frp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the server config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /opt/frp/frps.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;bindPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7000&lt;/span&gt;
&lt;span class="py"&gt;vhostHTTPPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
&lt;span class="py"&gt;vhostHTTPSPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8443&lt;/span&gt;

&lt;span class="py"&gt;auth.method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"token"&lt;/span&gt;
&lt;span class="py"&gt;auth.token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your-secret-token-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Change &lt;code&gt;your-secret-token-here&lt;/code&gt; to a strong random string.&lt;/strong&gt; This token must match on both the VPS and your home server. Use something like &lt;code&gt;openssl rand -hex 32&lt;/code&gt; to generate one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a systemd service so frp starts on boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/systemd/system/frps.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;frp server&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/frp/frps -c /opt/frp/frps.toml&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;frps
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start frps
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status frps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;active (running)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Install frp client on your home server
&lt;/h3&gt;

&lt;p&gt;Back on your home server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt
&lt;span class="nb"&gt;sudo &lt;/span&gt;wget https://github.com/fatedier/frp/releases/download/v0.61.0/frp_0.61.0_linux_amd64.tar.gz
&lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; frp_0.61.0_linux_amd64.tar.gz
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;frp_0.61.0_linux_amd64 frp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the client config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /opt/frp/frpc.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;serverAddr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your-oracle-vps-ip"&lt;/span&gt;
&lt;span class="py"&gt;serverPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7000&lt;/span&gt;

&lt;span class="py"&gt;auth.method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"token"&lt;/span&gt;
&lt;span class="py"&gt;auth.token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your-secret-token-here"&lt;/span&gt;

&lt;span class="nn"&gt;[[proxies]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nextcloud"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http"&lt;/span&gt;
&lt;span class="py"&gt;localPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8888&lt;/span&gt;
&lt;span class="py"&gt;customDomains&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"files.yourdomain.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;your-oracle-vps-ip&lt;/code&gt; with your Oracle VPS public IP&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;your-secret-token-here&lt;/code&gt; with the same token from Step 4&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;files.yourdomain.com&lt;/code&gt; with your actual domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a systemd service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/systemd/system/frpc.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;frp client&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/frp/frpc -c /opt/frp/frpc.toml&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;frpc
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start frpc
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status frpc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tunnel is now live. Your home server is connected to the Oracle VPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Set up Nginx on Oracle VPS
&lt;/h3&gt;

&lt;p&gt;Nginx receives incoming web traffic on the VPS and forwards it through the frp tunnel to your home server.&lt;/p&gt;

&lt;p&gt;SSH into your Oracle VPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Nginx config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;files.yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;10G&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_read_timeout&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_send_timeout&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;files.yourdomain.com&lt;/code&gt; with your actual domain.&lt;/p&gt;

&lt;p&gt;Enable the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, &lt;code&gt;http://files.yourdomain.com&lt;/code&gt; should load your Nextcloud. But it's not secure yet — no HTTPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Set up SSL with Certbot
&lt;/h3&gt;

&lt;p&gt;Let's Encrypt gives you free SSL certificates. Certbot automates the entire process.&lt;/p&gt;

&lt;p&gt;On your Oracle VPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; files.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certbot will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask for your email (for renewal notifications)&lt;/li&gt;
&lt;li&gt;Ask you to agree to terms&lt;/li&gt;
&lt;li&gt;Automatically configure Nginx for HTTPS&lt;/li&gt;
&lt;li&gt;Set up auto-renewal (certificates renew every 90 days automatically)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test the renewal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot renew &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it says "Congratulations, all simulated renewals succeeded", you're set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Update Nextcloud for HTTPS
&lt;/h3&gt;

&lt;p&gt;Now that you have SSL, tell Nextcloud to use HTTPS:&lt;/p&gt;

&lt;p&gt;Back on your home server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nextcloud bash
nano /var/www/html/config/config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add these lines inside the config array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'overwrite.cli.url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://files.yourdomain.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;'overwriteprotocol'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save, exit, and restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit
&lt;/span&gt;docker restart nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test everything
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;https://files.yourdomain.com&lt;/code&gt; in any browser, from any network. You should see your Nextcloud login page with a valid SSL certificate (green padlock).&lt;/p&gt;

&lt;p&gt;Try it from your phone on mobile data. Try sharing a file link with someone. It all works now.&lt;/p&gt;




&lt;h2&gt;
  
  
  The complete picture
&lt;/h2&gt;

&lt;p&gt;You now have two access paths:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Via&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Private&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://100.x.x.x:8888&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tailscale&lt;/td&gt;
&lt;td&gt;Personal use, large files, NAS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Public&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://files.yourdomain.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cloudflare → VPS → frp&lt;/td&gt;
&lt;td&gt;Sharing with family, mobile backup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both work from anywhere. The private path is faster (direct tunnel). The public path is more convenient (just a URL).&lt;/p&gt;

&lt;p&gt;Your phone's Nextcloud app can use either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set it to &lt;code&gt;https://files.yourdomain.com&lt;/code&gt; for universal access&lt;/li&gt;
&lt;li&gt;Set it to the Tailscale IP for faster transfers when Tailscale is connected&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;files.yourdomain.com&lt;/code&gt; not loading?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check frpc on home server: &lt;code&gt;sudo systemctl status frpc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check frps on Oracle VPS: &lt;code&gt;sudo systemctl status frps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check Nginx on Oracle VPS: &lt;code&gt;sudo systemctl status nginx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restart the chain: &lt;code&gt;sudo systemctl restart frpc&lt;/code&gt; (home) → &lt;code&gt;sudo systemctl restart frps&lt;/code&gt; (VPS) → &lt;code&gt;sudo systemctl restart nginx&lt;/code&gt; (VPS)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;"502 Bad Gateway" error?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The frp tunnel is down. Check &lt;code&gt;sudo systemctl status frpc&lt;/code&gt; on your home server&lt;/li&gt;
&lt;li&gt;Make sure the auth tokens match in both &lt;code&gt;frps.toml&lt;/code&gt; and &lt;code&gt;frpc.toml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SSL certificate not working?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;sudo certbot --nginx -d files.yourdomain.com&lt;/code&gt; again&lt;/li&gt;
&lt;li&gt;Make sure the Cloudflare proxy is set to "DNS only" (gray cloud), not "Proxied" (orange cloud)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tailscale not connecting?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check status: &lt;code&gt;sudo tailscale status&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restart: &lt;code&gt;sudo tailscale down &amp;amp;&amp;amp; sudo tailscale up&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Make sure Tailscale is installed and signed in on both devices with the same account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nextcloud "Access through untrusted domain" error?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the domain to trusted_domains in Nextcloud's config (Step 5 of Part A)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nextcloud redirect loop after enabling HTTPS?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove the overwrite setting: &lt;code&gt;docker exec -it nextcloud php occ config:system:delete overwriteprotocol&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Clear browser cookies for your domain&lt;/li&gt;
&lt;li&gt;Re-add the setting carefully&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What you should have now
&lt;/h2&gt;

&lt;p&gt;✅ Tailscale installed — private encrypted access from anywhere&lt;br&gt;
✅ Oracle Free VPS running as a public gateway&lt;br&gt;
✅ frp tunnel connecting your home server to the VPS&lt;br&gt;
✅ Nginx reverse proxy handling incoming traffic&lt;br&gt;
✅ SSL certificate via Let's Encrypt — valid HTTPS&lt;br&gt;
✅ &lt;code&gt;https://files.yourdomain.com&lt;/code&gt; working from any browser, any network&lt;br&gt;
✅ Phone auto-backup working on mobile data&lt;br&gt;
✅ Family can access shared files via a simple link — no app needed&lt;/p&gt;

&lt;p&gt;Your home server is now fully accessible from anywhere in the world, and your home IP is completely hidden.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;🔒 Part 5: Security hardening + lessons learned&lt;/strong&gt; — The final part. We'll set up UFW firewall rules properly, configure Fail2Ban to block brute-force attacks, harden SSH, add basic monitoring, and cover everything I learned the hard way. Don't skip this one — your server is now public-facing.&lt;/p&gt;

&lt;p&gt;All config files from this series are available in the companion GitHub repo:&lt;br&gt;
👉 &lt;a href="https://github.com/sasrath/homecloud" rel="noopener noreferrer"&gt;github.com/sasrath/homecloud&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Follow for Part 5 — it's the one that keeps everything secure.&lt;/strong&gt; If you hit any snags with the VPS or frp setup, drop a comment and I'll help you debug it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your server is now live on the internet. Let's make sure it stays safe.&lt;/em&gt; 🔒&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>networking</category>
      <category>tailscale</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Turn Your Home Server into a NAS — Access Files from Any Device with Samba</title>
      <dc:creator>Sasanka Rath</dc:creator>
      <pubDate>Tue, 31 Mar 2026 02:28:34 +0000</pubDate>
      <link>https://dev.to/avgskully/turn-your-home-server-into-a-nas-access-files-from-any-device-with-samba-5fgl</link>
      <guid>https://dev.to/avgskully/turn-your-home-server-into-a-nas-access-files-from-any-device-with-samba-5fgl</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/avgskully/setting-up-nextcloud-with-docker-on-an-old-laptop-your-own-google-drive-in-30-minutes-4ain"&gt;Part 2&lt;/a&gt;, we set up Nextcloud with Docker — your own Google Drive with phone auto-backup and desktop sync. That handles cloud-style access perfectly.&lt;/p&gt;

&lt;p&gt;But sometimes you just want to drag files into a folder. No browser, no app, no sync client — just open Finder on your Mac (or File Explorer on Windows), and there's your server, looking like any other drive.&lt;/p&gt;

&lt;p&gt;That's what Samba does. It turns your home server into a &lt;strong&gt;NAS (Network Attached Storage)&lt;/strong&gt; that any device on your network can access natively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll have after this guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your server's folders visible in Mac Finder / Windows File Explorer&lt;/li&gt;
&lt;li&gt;Drag-and-drop file transfer at full network speed&lt;/li&gt;
&lt;li&gt;Auto-mount on startup so the drive is always there&lt;/li&gt;
&lt;li&gt;Nextcloud and Samba sharing the same storage — files added via either method are visible in both&lt;/li&gt;
&lt;li&gt;Remote NAS access via Tailscale (set up in Part 4)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's set it up.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Samba and Nextcloud work together
&lt;/h2&gt;

&lt;p&gt;This is important to understand before we start. Both Nextcloud and Samba will point to the &lt;strong&gt;same storage directory&lt;/strong&gt; (&lt;code&gt;/srv/nas&lt;/code&gt;). This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload a photo via Nextcloud's web interface → it appears in Samba immediately&lt;/li&gt;
&lt;li&gt;Drop a video into the Samba share from your Mac → Nextcloud can see it after a quick scan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They're two doors into the same room. Nextcloud gives you the cloud experience (browser, apps, sharing links). Samba gives you the local network experience (Finder, drag-and-drop, raw speed).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;When you add files via Samba, Nextcloud won't detect them automatically.&lt;/strong&gt; You'll need to run a quick scan command. I'll cover this at the end.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: Install Samba
&lt;/h2&gt;

&lt;p&gt;SSH into your server and install Samba:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;samba &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;smbd &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like &lt;code&gt;Version 4.x.x&lt;/code&gt;. Samba is now installed but not configured yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Create a Samba password
&lt;/h2&gt;

&lt;p&gt;Samba uses its own password system, separate from your Linux login. Create a Samba password for your user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;smbpasswd &lt;span class="nt"&gt;-a&lt;/span&gt; your-username
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;your-username&lt;/code&gt; with your actual Linux username. It'll ask you to set a password — this is what you'll enter when connecting from your Mac or PC. It can be different from your Linux password.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Configure Samba shares
&lt;/h2&gt;

&lt;p&gt;Open the Samba config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/samba/smb.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scroll to the bottom and add your shared folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[NAS]&lt;/span&gt;
   &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/srv/nas&lt;/span&gt;
   &lt;span class="py"&gt;browseable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
   &lt;span class="err"&gt;read&lt;/span&gt; &lt;span class="py"&gt;only&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
   &lt;span class="err"&gt;valid&lt;/span&gt; &lt;span class="py"&gt;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;your-username&lt;/span&gt;
   &lt;span class="err"&gt;create&lt;/span&gt; &lt;span class="py"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0644&lt;/span&gt;
   &lt;span class="err"&gt;directory&lt;/span&gt; &lt;span class="py"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0755&lt;/span&gt;
   &lt;span class="err"&gt;force&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
   &lt;span class="err"&gt;force&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;

&lt;span class="nn"&gt;[Photos]&lt;/span&gt;
   &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/srv/nas/photos&lt;/span&gt;
   &lt;span class="py"&gt;browseable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
   &lt;span class="err"&gt;read&lt;/span&gt; &lt;span class="py"&gt;only&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
   &lt;span class="err"&gt;valid&lt;/span&gt; &lt;span class="py"&gt;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;your-username&lt;/span&gt;
   &lt;span class="err"&gt;create&lt;/span&gt; &lt;span class="py"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0644&lt;/span&gt;
   &lt;span class="err"&gt;directory&lt;/span&gt; &lt;span class="py"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0755&lt;/span&gt;
   &lt;span class="err"&gt;force&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
   &lt;span class="err"&gt;force&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;

&lt;span class="nn"&gt;[Videos]&lt;/span&gt;
   &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/srv/nas/videos&lt;/span&gt;
   &lt;span class="py"&gt;browseable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
   &lt;span class="err"&gt;read&lt;/span&gt; &lt;span class="py"&gt;only&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
   &lt;span class="err"&gt;valid&lt;/span&gt; &lt;span class="py"&gt;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;your-username&lt;/span&gt;
   &lt;span class="err"&gt;create&lt;/span&gt; &lt;span class="py"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0644&lt;/span&gt;
   &lt;span class="err"&gt;directory&lt;/span&gt; &lt;span class="py"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0755&lt;/span&gt;
   &lt;span class="err"&gt;force&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
   &lt;span class="err"&gt;force&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain the key settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;path&lt;/code&gt;&lt;/strong&gt; — the directory to share&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;browseable = yes&lt;/code&gt;&lt;/strong&gt; — makes it visible when browsing the network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;read only = no&lt;/code&gt;&lt;/strong&gt; — allows writing (uploading files)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;valid users&lt;/code&gt;&lt;/strong&gt; — only this user can access the share&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;force user / force group = www-data&lt;/code&gt;&lt;/strong&gt; — this is critical. Nextcloud runs as &lt;code&gt;www-data&lt;/code&gt;, so any file created via Samba must also be owned by &lt;code&gt;www-data&lt;/code&gt;. Without this, Nextcloud can't read files you add through Samba.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save and exit (&lt;code&gt;Ctrl+X&lt;/code&gt;, then &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;Enter&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Set permissions
&lt;/h2&gt;

&lt;p&gt;Make sure the storage directory has the right ownership:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data /srv/nas
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 775 /srv/nas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures both Nextcloud (running as &lt;code&gt;www-data&lt;/code&gt;) and Samba can read and write to the same files without permission conflicts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Restart Samba
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart smbd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;smbd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;enable&lt;/code&gt; command ensures Samba starts automatically after reboots.&lt;/p&gt;

&lt;p&gt;Verify it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status smbd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;active (running)&lt;/code&gt; in green.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Open firewall ports
&lt;/h2&gt;

&lt;p&gt;If you're running UFW (which you should be — we'll set it up properly in Part 5), allow Samba traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 137/udp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 138/udp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 139/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 445/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the standard ports Samba uses for NetBIOS and file sharing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Connect from Mac
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Finder&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Cmd+K&lt;/strong&gt; (or click Go → Connect to Server)&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;smb://192.168.1.100&lt;/code&gt; (replace with your server's IP)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Connect&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Registered User&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your username and the Samba password you created in Step 2&lt;/li&gt;
&lt;li&gt;Select which shares to mount: &lt;strong&gt;NAS&lt;/strong&gt;, &lt;strong&gt;Photos&lt;/strong&gt;, &lt;strong&gt;Videos&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your server's folders now appear in the Finder sidebar under "Locations." You can drag files in, open them directly, and treat it exactly like a USB drive — except it's over your network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-mount on Mac startup
&lt;/h3&gt;

&lt;p&gt;So you don't have to reconnect every time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect to the NAS using the steps above&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;System Settings → General → Login Items&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;+&lt;/strong&gt; under "Open at Login"&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Network → your server IP → select the NAS folder&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The drive now mounts automatically every time your Mac starts&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 8: Connect from Windows
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;File Explorer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the address bar at the top&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;\\192.168.1.100&lt;/code&gt; (backslashes, not forward slashes)&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Enter&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your username and Samba password&lt;/li&gt;
&lt;li&gt;Check "Remember my credentials" if you want auto-login&lt;/li&gt;
&lt;li&gt;Your shares appear as network folders&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Map as a network drive (auto-mount):
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the share folder&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Map network drive&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose a drive letter (e.g., Z:)&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Reconnect at sign-in&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Finish&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your server now shows up as Drive Z: in File Explorer, permanently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Sync Samba files with Nextcloud
&lt;/h2&gt;

&lt;p&gt;When you add files through Samba (drag-and-drop from Mac/PC), Nextcloud doesn't know about them yet. You need to tell Nextcloud to scan for new files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; www-data nextcloud php occ files:scan &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scans the entire storage directory and adds any new files to Nextcloud's index. Takes a few seconds for small changes, longer for thousands of files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make it automatic
&lt;/h3&gt;

&lt;p&gt;If you're regularly adding files via Samba, set up a cron job to scan every 15 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;/15 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; www-data nextcloud php occ files:scan &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Nextcloud automatically picks up any files you add through Samba within 15 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: Bulk transfer — the fastest way to load your server
&lt;/h2&gt;

&lt;p&gt;If you have hundreds of gigabytes of photos and videos to move onto your server, Samba is by far the fastest method. Here's the recommended approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect via Samba&lt;/strong&gt; from your Mac or PC (Steps 7 or 8)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drag and drop&lt;/strong&gt; your files into the appropriate folders (photos → Photos, videos → Videos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait for transfer to complete&lt;/strong&gt; — local network transfers are fast, typically 50-100 MB/s over WiFi, faster over Ethernet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the Nextcloud scan&lt;/strong&gt; once the transfer is done:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; www-data nextcloud php occ files:scan &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much faster than uploading through Nextcloud's web interface, which has overhead from HTTP and PHP processing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Transferring a large collection?&lt;/strong&gt; Use an Ethernet cable for the transfer if possible — you'll see 2-3x faster speeds compared to WiFi. A &lt;a href="https://www.amazon.in/dp/B01DGVKBC6?tag=avgskully-21" rel="noopener noreferrer"&gt;Cat 6 Ethernet cable&lt;/a&gt; costs practically nothing and makes a big difference for bulk transfers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can't see the server in Finder/File Explorer?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure you're on the same WiFi network as the server&lt;/li&gt;
&lt;li&gt;Try connecting directly via IP: &lt;code&gt;smb://192.168.1.100&lt;/code&gt; (Mac) or &lt;code&gt;\\192.168.1.100&lt;/code&gt; (Windows)&lt;/li&gt;
&lt;li&gt;Check Samba is running: &lt;code&gt;sudo systemctl status smbd&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;"Permission denied" when writing files?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check ownership: &lt;code&gt;ls -la /srv/nas/&lt;/code&gt; — files should be owned by &lt;code&gt;www-data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fix permissions: &lt;code&gt;sudo chown -R www-data:www-data /srv/nas&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Make sure your &lt;code&gt;smb.conf&lt;/code&gt; has &lt;code&gt;force user = www-data&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Files added via Samba not showing in Nextcloud?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the scan: &lt;code&gt;docker exec -u www-data nextcloud php occ files:scan --all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or wait for the cron job if you set one up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Connection drops after Mac sleep?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is normal. The Mac disconnects SMB shares when it sleeps. It should auto-reconnect when it wakes up if you set up auto-mount (Step 7). If not, just press Cmd+K and reconnect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Slow transfer speeds?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switch to a wired connection. WiFi has overhead and interference. An &lt;a href="https://www.amazon.in/dp/B0FHK2HBFW?tag=avgskully-21" rel="noopener noreferrer"&gt;Ethernet connection&lt;/a&gt; will give you significantly faster and more reliable transfers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What you should have now
&lt;/h2&gt;

&lt;p&gt;After following this guide:&lt;/p&gt;

&lt;p&gt;✅ Samba installed and configured with NAS, Photos, and Videos shares&lt;br&gt;
✅ Mac or Windows PC sees your server as a network drive&lt;br&gt;
✅ Drag-and-drop file transfer at full network speed&lt;br&gt;
✅ Auto-mount on startup so the drive is always there&lt;br&gt;
✅ Nextcloud synced with Samba via manual scan or cron job&lt;br&gt;
✅ A complete NAS setup using hardware you already own&lt;/p&gt;

&lt;p&gt;Between Nextcloud (Part 2) and Samba (this guide), you now have two ways to access your files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Nextcloud&lt;/strong&gt; (browser/app)&lt;/td&gt;
&lt;td&gt;Remote access, phone backup, sharing links&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Samba&lt;/strong&gt; (Finder/Explorer)&lt;/td&gt;
&lt;td&gt;Bulk transfers, local file access, drag-and-drop&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use both. They complement each other perfectly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Right now, everything works on your home network. But what about when you're not home?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 Part 4: Access your server from anywhere&lt;/strong&gt; — Set up Tailscale for private encrypted access, get an Oracle Free VPS, configure frp tunneling to bypass CGNAT, set up Nginx and SSL, and point your custom domain to your home server. After Part 4, you'll access your NAS from anywhere in the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔒 Part 5: Security hardening + lessons learned&lt;/strong&gt; — UFW firewall, Fail2Ban, SSH hardening, monitoring, and everything I wish I'd known before starting.&lt;/p&gt;

&lt;p&gt;All config files from this series are available in the companion GitHub repo:&lt;br&gt;
👉 &lt;a href="https://github.com/sasrath/homecloud" rel="noopener noreferrer"&gt;github.com/sasrath/homecloud&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Hit Follow if you don't want to miss Part 4 — that's the one where everything comes together.&lt;/strong&gt; Drop a comment if you hit any issues with the Samba setup.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your server is now a NAS. Next up: accessing it from anywhere in the world.&lt;/em&gt; 🌐&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>linux</category>
      <category>networking</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>KPI Tracker — AI-Powered Client Performance Dashboard with Notion as the Entire Backend</title>
      <dc:creator>Sasanka Rath</dc:creator>
      <pubDate>Sat, 28 Mar 2026 13:17:52 +0000</pubDate>
      <link>https://dev.to/avgskully/kpi-tracker-ai-powered-client-performance-dashboard-with-notion-as-the-entire-backend-1hld</link>
      <guid>https://dev.to/avgskully/kpi-tracker-ai-powered-client-performance-dashboard-with-notion-as-the-entire-backend-1hld</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;KPI Tracker&lt;/strong&gt; is an AI-powered client quarterly performance dashboard where Notion is the &lt;em&gt;entire&lt;/em&gt; backend — no traditional database, no SQL, no Redis. Just Notion, connected through the official Model Context Protocol (MCP) server.&lt;/p&gt;

&lt;p&gt;The problem it solves: consultants, analysts, and account managers who track multiple clients' performance currently spend hours manually copy-pasting data from quarterly reports. KPI Tracker reduces that to under 2 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Paste a URL or drag-and-drop a PDF&lt;/strong&gt; — an earnings press release, 10-K, 10-Q, or any financial report&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI extracts all KPIs automatically&lt;/strong&gt; — Revenue, Gross Margin, Net Margin, EPS, Operating Income, Customer Count, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop review&lt;/strong&gt; — you see every extracted KPI with its confidence score before anything saves&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One click saves everything to Notion&lt;/strong&gt; — the app creates a client record, a quarterly report entry, and individual KPI rows via Notion MCP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live dashboard renders 11+ interactive charts&lt;/strong&gt; — all reading directly from Notion&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Key features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Model AI&lt;/strong&gt; — choose from 5 models at runtime: Gemini 2.5 Flash, Gemini 2.5 Pro, Claude Haiku 4.5, Sonnet 4.5, or Sonnet 4.6. No restart needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Revenue Forecasting&lt;/strong&gt; — trend-based next-quarter revenue predictions per client, with reasoning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;11+ Chart Types&lt;/strong&gt; — Revenue Trend (Area), Revenue Forecast (Bar), Margin Trends (Line), EPS Trends (Line), Segment Revenue (Stacked Bar), KPI Distribution (Pie), Confidence Distribution (Pie), QoQ Heatmap, Revenue vs Margins (Composed), KPI Gauges (Horizontal Bar), KPI Landscape (Treemap)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drag-and-Drop Charts&lt;/strong&gt; — reorder any chart by dragging; order persists in localStorage across sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin KPI Charts&lt;/strong&gt; — click any KPI in the data table to view its historical trend, then pin it as a new chart on the dashboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask AI&lt;/strong&gt; — query your data in natural language ("Which client had the highest revenue growth?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom KPIs&lt;/strong&gt; — manually add metrics the AI missed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart $B Formatting&lt;/strong&gt; — revenue values ≥ $1,000M auto-display as billions (e.g., $12.7B)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All Clients Aggregation&lt;/strong&gt; — summary cards intelligently aggregate across all clients (Revenue/OpIncome summed, margins/EPS averaged)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF + URL Ingestion&lt;/strong&gt; — works with public URLs or local files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt; — API keys never touch the browser; prompt injection detection built-in&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Next.js 15.1 (App Router)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;React 19, Tailwind CSS 3.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charts&lt;/td&gt;
&lt;td&gt;Recharts 2.13 (11+ chart types)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drag-and-Drop&lt;/td&gt;
&lt;td&gt;@dnd-kit/core + @dnd-kit/sortable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI (default)&lt;/td&gt;
&lt;td&gt;Google Gemini 2.5 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI (alternative)&lt;/td&gt;
&lt;td&gt;Anthropic Claude Haiku / Sonnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend/DB&lt;/td&gt;
&lt;td&gt;Notion (via MCP + direct SDK)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP&lt;/td&gt;
&lt;td&gt;@modelcontextprotocol/sdk 1.27, @notionhq/notion-mcp-server 2.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF Parsing&lt;/td&gt;
&lt;td&gt;pdf-parse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/J0Vk2eZ1--E"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Static demo&lt;/strong&gt; (no API keys needed): &lt;a href="https://notion-kpi.sasrath.com" rel="noopener noreferrer"&gt;notion-kpi.sasrath.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Show us the code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sasrath" rel="noopener noreferrer"&gt;
        sasrath
      &lt;/a&gt; / &lt;a href="https://github.com/sasrath/notion-kpi-tracker" rel="noopener noreferrer"&gt;
        notion-kpi-tracker
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Turn any quarterly report URL into a live KPI dashboard.  Powered by Claude/Gemini AI + Notion MCP. Zero traditional database.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;📊 KPI Tracker — Powered by Notion MCP&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;AI-powered client quarterly performance tracker with multi-model support (Google Gemini + Anthropic Claude), 11+ interactive charts, AI revenue forecasting, and Notion as the sole backend. Zero traditional database.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Built for the &lt;a href="https://dev.to/challenges/notion-2026-03-04" rel="nofollow"&gt;dev.to Notion MCP Challenge&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;🤖 &lt;strong&gt;AI Report Ingestion&lt;/strong&gt; — paste a URL or drag-and-drop a PDF, AI extracts KPIs automatically&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Interactive Dashboard&lt;/strong&gt; — 11+ chart types (Area, Bar, Line, Pie, Heatmap, Treemap, Composed &amp;amp; more)&lt;/li&gt;
&lt;li&gt;🔮 &lt;strong&gt;AI Revenue Forecasting&lt;/strong&gt; — trend-based next-quarter revenue predictions per client&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Multi-Model AI&lt;/strong&gt; — choose from 5 models: Gemini 2.5 Flash/Pro, Claude Haiku 4.5, Sonnet 4.5/4.6&lt;/li&gt;
&lt;li&gt;✋ &lt;strong&gt;Human-in-the-Loop&lt;/strong&gt; — review and edit extracted KPIs before anything saves to Notion&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Drag-and-Drop Charts&lt;/strong&gt; — reorder dashboard charts by dragging; order persists across sessions&lt;/li&gt;
&lt;li&gt;📌 &lt;strong&gt;Pin KPI Charts&lt;/strong&gt; — click any KPI in the table to view its trend, then pin it to the…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sasrath/notion-kpi-tracker" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The full source is on GitHub: &lt;a href="https://github.com/sasrath/notion-kpi-tracker" rel="noopener noreferrer"&gt;github.com/sasrath/notion-kpi-tracker&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture at a glance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Browser (React SPA)
    │
    ├── Google Gemini API   (web search, extraction, forecasts)
    ├── Anthropic Claude API (extraction, queries, forecasts)
    │
    └── Next.js API Routes
            │
            └── lib/notion-mcp.js (MCP Client singleton)
                    │ (stdio)
                    └── @notionhq/notion-mcp-server (subprocess)
                            │ (HTTP)
                            └── Notion API → 3 Databases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server runs as a long-lived subprocess managed as a singleton. It starts on the first API call and stays alive for subsequent requests. The &lt;code&gt;callNotionTool()&lt;/code&gt; helper handles JSON parsing, error detection, and auto-reconnect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;app/page.jsx&lt;/code&gt;&lt;/strong&gt; — Main dashboard: 11+ charts, DnD grid, KPI table, saving progress, tab persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lib/notion-mcp.js&lt;/code&gt;&lt;/strong&gt; — MCP client singleton with auto-reconnect and 21-tool mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lib/llm.js&lt;/code&gt;&lt;/strong&gt; — Multi-model AI layer (Gemini + Claude), &lt;code&gt;safeParseJSON&lt;/code&gt; for truncated responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lib/transforms.js&lt;/code&gt;&lt;/strong&gt; — 19 data transform functions for chart preparation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;setup-notion.mjs&lt;/code&gt;&lt;/strong&gt; — Automated Notion database setup script&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion is &lt;strong&gt;not&lt;/strong&gt; a side feature — it &lt;em&gt;is&lt;/em&gt; the backend. Every data operation flows through the official Notion MCP server (&lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt;). There is zero traditional database code anywhere in the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  The three-database schema
&lt;/h3&gt;

&lt;p&gt;The app uses three relational Notion databases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clients&lt;/strong&gt; — one row per company (Name, Industry, Website, Status)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quarterly Reports&lt;/strong&gt; — one row per earnings report, linked to a Client (Quarter, Year, Report URL, Raw Summary)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KPIs&lt;/strong&gt; — one row per metric, linked to both a Report and a Client (KPI Name, Value, Unit, Quarter, Year, Source, Confidence, Notes)&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP operations in action
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What the app does&lt;/th&gt;
&lt;th&gt;MCP Tool used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Save a new client, report, or KPI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API-post-page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load all clients / search KPIs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API-post-search&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete (archive) a KPI row&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API-patch-page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify Notion connection on startup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API-get-self&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check database schema&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API-retrieve-a-database&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's how the MCP connection is established in &lt;code&gt;lib/notion-mcp.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/sdk/client/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioClientTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/sdk/client/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioClientTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@notionhq/notion-mcp-server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;OPENAPI_MCP_HEADERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTION_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Notion-Version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-06-28&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kpi-tracker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every read and write — from loading the dashboard to saving AI-extracted KPIs to deleting a row — goes through this MCP connection. The app treats Notion exactly like a traditional database, but through the standardized MCP protocol.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why MCP over the direct Notion SDK?
&lt;/h3&gt;

&lt;p&gt;Using MCP means the app communicates through a standardized protocol layer. This gives us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tool discovery&lt;/strong&gt; — the MCP server exposes 21 tools that map to Notion's REST API; the app discovers them at startup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol standardization&lt;/strong&gt; — the same MCP client pattern works with any MCP-compatible service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupled auth&lt;/strong&gt; — credentials flow through the MCP transport layer, not hardcoded in API calls&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The result
&lt;/h3&gt;

&lt;p&gt;The entire data lifecycle — AI extraction → human review → save to Notion → read for dashboard → AI forecasting — runs through Notion MCP. No separate database server to manage, no migrations to run, no connection pools to configure. Just Notion.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔐 Full Demo Access
&lt;/h2&gt;

&lt;p&gt;The public demo at &lt;a href="https://notion-kpi.sasrath.com" rel="noopener noreferrer"&gt;notion-kpi.sasrath.com&lt;/a&gt; shows sample data to protect API limits.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;full live experience&lt;/strong&gt; with real Notion + Gemini:&lt;br&gt;
👉 &lt;a href="https://notion-kpi.sasrath.com/judges" rel="noopener noreferrer"&gt;notion-kpi.sasrath.com/judges&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pre-loaded with real earnings data from Apple, &lt;br&gt;
Microsoft and Intel Q4 2025.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I'm a consultant focused on practical AI integrations, data tooling, and building small, maintainable web apps that solve real problems. I publish code, demos, and write-ups so others can learn and contribute — feedback, issues, and pull requests are very welcome.My Github (source: &lt;a href="https://github.com/sasrath" rel="noopener noreferrer"&gt;https://github.com/sasrath&lt;/a&gt;). &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Setting Up Nextcloud with Docker on an Old Laptop — Your Own Google Drive in 30 Minutes</title>
      <dc:creator>Sasanka Rath</dc:creator>
      <pubDate>Tue, 24 Mar 2026 15:24:15 +0000</pubDate>
      <link>https://dev.to/avgskully/setting-up-nextcloud-with-docker-on-an-old-laptop-your-own-google-drive-in-30-minutes-4ain</link>
      <guid>https://dev.to/avgskully/setting-up-nextcloud-with-docker-on-an-old-laptop-your-own-google-drive-in-30-minutes-4ain</guid>
      <description>&lt;p&gt;Part 2 of the home server series. A complete, step-by-step guide to installing Docker, running Nextcloud, setting up phone auto-backup, and getting desktop sync working."&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/avgskully/i-replaced-google-drive-with-a-home-server-that-costs-almost-nothing-2c6l"&gt;Part 1&lt;/a&gt;, I explained why I replaced Google One with a home server and showed the architecture. Now we build it.&lt;/p&gt;

&lt;p&gt;This post covers the heart of the setup — &lt;strong&gt;Nextcloud running in Docker&lt;/strong&gt;. By the end of this guide, you'll have a fully working personal cloud that you can access from your browser, sync files from your desktop, and auto-backup photos from your phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An old laptop or PC (I'm using an HP Pavilion x360, but anything with 4GB+ RAM works)&lt;/li&gt;
&lt;li&gt;Ubuntu Server 24.04 LTS installed (the &lt;a href="https://ubuntu.com/tutorials/install-ubuntu-server" rel="noopener noreferrer"&gt;official install guide&lt;/a&gt; takes about 15 minutes)&lt;/li&gt;
&lt;li&gt;A wired or wireless internet connection&lt;/li&gt;
&lt;li&gt;About 30 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's go.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Give your server a static IP
&lt;/h2&gt;

&lt;p&gt;Before installing anything, your server needs a fixed IP address on your home network. Without this, your router might assign it a different IP every time it reboots, and nothing will be reachable.&lt;/p&gt;

&lt;p&gt;Find your current network details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip a
ip route | &lt;span class="nb"&gt;grep &lt;/span&gt;default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note down your interface name (something like &lt;code&gt;eth0&lt;/code&gt; for Ethernet or &lt;code&gt;wlo1&lt;/code&gt; for WiFi), your current IP, and your gateway IP (usually &lt;code&gt;192.168.1.1&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now edit the netplan config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/netplan/00-installer-config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a &lt;strong&gt;wired connection&lt;/strong&gt; (recommended — if your laptop doesn't have an Ethernet port, a &lt;a href="https://www.amazon.in/dp/B0FHK2HBFW?tag=avgskully-21" rel="noopener noreferrer"&gt;USB-C to Ethernet adapter&lt;/a&gt; works great):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;ethernets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dhcp4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.1.100/24&lt;/span&gt;
      &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
          &lt;span class="na"&gt;via&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
      &lt;span class="na"&gt;nameservers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1.1.1.1&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8.8.8.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a &lt;strong&gt;WiFi connection&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;wifis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;wlo1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dhcp4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
      &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.1.100/24&lt;/span&gt;
      &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
          &lt;span class="na"&gt;via&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
      &lt;span class="na"&gt;nameservers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1.1.1.1&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8.8.8.8&lt;/span&gt;
      &lt;span class="na"&gt;access-points&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your_WiFi_Name"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-wifi-password"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netplan apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip a | &lt;span class="nb"&gt;grep &lt;/span&gt;192.168.1.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your static IP. From now on, your server is always at &lt;code&gt;192.168.1.100&lt;/code&gt; (or whatever IP you chose).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pick an IP outside your router's DHCP range.&lt;/strong&gt; Most routers assign IPs in a range like 192.168.1.2 to 192.168.1.99. Setting your server to 192.168.1.100 avoids conflicts. Check your router's admin page if you're unsure.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Install Docker
&lt;/h2&gt;

&lt;p&gt;Docker lets you run Nextcloud in an isolated container — easy to install, easy to update, easy to back up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update packages&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker.io docker-compose &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Start Docker and enable it on boot&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker

&lt;span class="c"&gt;# Add your user to the docker group (so you don't need sudo every time)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Log out and log back in&lt;/strong&gt; for the group change to take effect. Then verify Docker is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
docker compose version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both should return version numbers. If they do, Docker is ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Prepare storage directories
&lt;/h2&gt;

&lt;p&gt;Create the folders where Nextcloud will store your files. I use &lt;code&gt;/srv/nas&lt;/code&gt; as the root — it's a standard Linux convention for service data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Running low on built-in storage?&lt;/strong&gt; You can mount an &lt;a href="https://www.amazon.in/dp/B07DQ5ZH1D?tag=avgskully-21" rel="noopener noreferrer"&gt;external USB hard drive&lt;/a&gt; and point these directories to it. A 1TB external drive costs around ₹4,000 and instantly doubles your capacity.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create folder structure&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /srv/nas/photos
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /srv/nas/videos
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /srv/nas/documents
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /srv/nas/nextcloud

&lt;span class="c"&gt;# Set ownership so Nextcloud (which runs as www-data) can write to it&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data /srv/nas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a clean folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/srv/nas/
├── photos/       ← All your photos
├── videos/       ← Personal videos
├── documents/    ← PDFs, spreadsheets, etc.
└── nextcloud/    ← Nextcloud internal data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later, when we set up Samba (Part 3), these same folders will be accessible as network drives on your Mac or PC.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Create the Docker Compose file
&lt;/h2&gt;

&lt;p&gt;Create a directory for your server configuration and add the compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/server
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/server
nano docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nextcloud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextcloud:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextcloud&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8888:80"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config:/var/www/html&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/srv/nas:/var/www/html/data&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NEXTCLOUD_TRUSTED_DOMAINS=192.168.1.100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain what each line does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;image: nextcloud:latest&lt;/code&gt;&lt;/strong&gt; — pulls the latest official Nextcloud image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;container_name: nextcloud&lt;/code&gt;&lt;/strong&gt; — gives the container a clean name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;restart: always&lt;/code&gt;&lt;/strong&gt; — auto-restarts the container if it crashes or after a reboot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ports: "8888:80"&lt;/code&gt;&lt;/strong&gt; — maps port 8888 on your server to port 80 inside the container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;volumes&lt;/code&gt;&lt;/strong&gt; — mounts your config directory and storage directory into the container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NEXTCLOUD_TRUSTED_DOMAINS&lt;/code&gt;&lt;/strong&gt; — tells Nextcloud which addresses are allowed to access it. We'll add more later (your domain, Tailscale IP, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why port 8888?&lt;/strong&gt; Port 80 is reserved for potential future services. A non-standard port also adds a minor layer of obscurity. You can pick any unused port you like.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: Launch Nextcloud
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/server
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; flag runs it in the background. First time will take a minute or two to download the Nextcloud image.&lt;/p&gt;

&lt;p&gt;Check it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE              STATUS          PORTS                  NAMES
&lt;/span&gt;&lt;span class="gp"&gt;abc123def456   nextcloud:latest   Up 2 minutes    0.0.0.0:8888-&amp;gt;&lt;/span&gt;80/tcp   nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open a browser on any device on your home network and go to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://192.168.1.100:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see the Nextcloud setup screen.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Configure Nextcloud
&lt;/h2&gt;

&lt;p&gt;On the setup screen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create an admin account&lt;/strong&gt; — pick a username and a strong password. This is your main login.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage &amp;amp; database&lt;/strong&gt; — select &lt;strong&gt;SQLite&lt;/strong&gt; for simplicity. It's perfectly fine for personal use with 1-3 users. (If you expect more users, you can set up MariaDB/PostgreSQL later.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install recommended apps&lt;/strong&gt; — check this box. It installs Calendar, Contacts, Talk, and other useful apps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Click &lt;strong&gt;Finish setup&lt;/strong&gt;. Give it a minute to install everything.&lt;/p&gt;

&lt;p&gt;Once done, you'll land on the Nextcloud dashboard — your personal cloud is live.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Update trusted domains
&lt;/h2&gt;

&lt;p&gt;Nextcloud only allows access from domains/IPs listed in its trusted domains config. Right now it only knows about &lt;code&gt;192.168.1.100&lt;/code&gt;. We need to add more for later parts of this series.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enter the Nextcloud container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nextcloud bash

&lt;span class="c"&gt;# Edit the config file&lt;/span&gt;
apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;nano &lt;span class="nt"&gt;-y&lt;/span&gt;
nano /var/www/html/config/config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the &lt;code&gt;trusted_domains&lt;/code&gt; section and update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'trusted_domains'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'192.168.1.100'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'files.yourdomain.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'100.x.x.x'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;files.yourdomain.com&lt;/code&gt; with your actual domain (we'll set this up in Part 4), and &lt;code&gt;100.x.x.x&lt;/code&gt; with your Tailscale IP once you have it. For now, just add placeholder entries — you can always update later.&lt;/p&gt;

&lt;p&gt;Save and exit (&lt;code&gt;Ctrl+X&lt;/code&gt;, then &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;Enter&lt;/code&gt;). Then exit the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Nextcloud to apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker restart nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 8: Set up phone auto-backup
&lt;/h2&gt;

&lt;p&gt;This is the killer feature — every photo you take automatically syncs to your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  iPhone:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;strong&gt;Nextcloud&lt;/strong&gt; from the App Store&lt;/li&gt;
&lt;li&gt;Open the app → enter server address: &lt;code&gt;http://192.168.1.100:8888&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Log in with your admin credentials&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Auto Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable it and select which folders to back up (Camera Roll at minimum)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Android:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;strong&gt;Nextcloud&lt;/strong&gt; from Play Store&lt;/li&gt;
&lt;li&gt;Same steps — enter server address, log in&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Auto Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable for Camera folder&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From now on, every photo and video automatically uploads to &lt;code&gt;/srv/nas/&lt;/code&gt; on your server. No more paying Google for photo storage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This only works when you're on your home WiFi&lt;/strong&gt; for now. In Part 4, when we set up Tailscale and the public domain, auto-backup will work from anywhere.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 9: Set up desktop sync
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mac:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download the Nextcloud desktop app from &lt;a href="https://nextcloud.com/install/#install-clients" rel="noopener noreferrer"&gt;nextcloud.com/install&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install and open it&lt;/li&gt;
&lt;li&gt;Server address: &lt;code&gt;http://192.168.1.100:8888&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Log in with your credentials&lt;/li&gt;
&lt;li&gt;Choose a local sync folder on your Mac (e.g., &lt;code&gt;~/Nextcloud&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Files now sync automatically — like Dropbox or OneDrive&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Windows / Linux:
&lt;/h3&gt;

&lt;p&gt;Same process — download the desktop client, point it to your server, and select a sync folder.&lt;/p&gt;

&lt;p&gt;You can also access Nextcloud from any browser at &lt;code&gt;http://192.168.1.100:8888&lt;/code&gt; — upload files, create folders, share links, just like Google Drive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: Useful Nextcloud commands
&lt;/h2&gt;

&lt;p&gt;A few commands you'll use regularly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if Nextcloud is running&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# View Nextcloud logs (useful for debugging)&lt;/span&gt;
docker logs nextcloud &lt;span class="nt"&gt;--tail&lt;/span&gt; 50

&lt;span class="c"&gt;# Restart Nextcloud&lt;/span&gt;
docker restart nextcloud

&lt;span class="c"&gt;# Stop everything&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/server &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose down

&lt;span class="c"&gt;# Start everything&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/server &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Force Nextcloud to re-scan files&lt;/span&gt;
&lt;span class="c"&gt;# (useful after adding files via Samba — covered in Part 3)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; www-data nextcloud php occ files:scan &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="c"&gt;# Check disk space&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Nextcloud not loading in browser?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the container is running: &lt;code&gt;docker ps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If not running: &lt;code&gt;cd ~/server &amp;amp;&amp;amp; docker compose up -d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check logs: &lt;code&gt;docker logs nextcloud --tail 50&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;"Access through untrusted domain" error?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're accessing from an IP/domain not in trusted_domains&lt;/li&gt;
&lt;li&gt;Add it to the config as shown in Step 7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Upload fails for large files?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nextcloud's default upload limit might be too low&lt;/li&gt;
&lt;li&gt;Fix it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nextcloud bash
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"upload_max_filesize=16G"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /usr/local/etc/php/conf.d/uploads.ini
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"post_max_size=16G"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /usr/local/etc/php/conf.d/uploads.ini
&lt;span class="nb"&gt;exit
&lt;/span&gt;docker restart nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;"Redirect loop" error?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear your browser cookies for &lt;code&gt;192.168.1.100&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Make sure you're using &lt;code&gt;http://&lt;/code&gt; not &lt;code&gt;https://&lt;/code&gt; (we don't have SSL yet — that's Part 4)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What you should have now
&lt;/h2&gt;

&lt;p&gt;After following this guide:&lt;/p&gt;

&lt;p&gt;✅ Docker installed and running&lt;br&gt;
✅ Nextcloud accessible at &lt;code&gt;http://192.168.1.100:8888&lt;/code&gt;&lt;br&gt;
✅ Storage organized in &lt;code&gt;/srv/nas/&lt;/code&gt; with separate folders for photos, videos, documents&lt;br&gt;
✅ Phone auto-backup working on your home WiFi&lt;br&gt;
✅ Desktop sync client connected&lt;br&gt;
✅ Your own personal Google Drive — running on hardware you own&lt;/p&gt;




&lt;h2&gt;
  
  
  Hardware recommendations
&lt;/h2&gt;

&lt;p&gt;You don't need to buy anything new to follow this guide — the whole point is using what you already have. But if you want to improve reliability or expand your setup, here's what I'd suggest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.amazon.in/dp/B07DQ5ZH1D?tag=avgskully-21" rel="noopener noreferrer"&gt;Seagate 1TB External Hard Drive&lt;/a&gt;&lt;/strong&gt; — for expanding storage when your built-in drive fills up. Plug it in via USB and mount it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.amazon.in/dp/B0FHK2HBFW?tag=avgskully-21" rel="noopener noreferrer"&gt;Amazon Basics USB-C to Ethernet Adapter&lt;/a&gt;&lt;/strong&gt; — if your laptop doesn't have an Ethernet port. Wired connection is significantly more reliable than WiFi for a 24/7 server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.amazon.in/dp/B0BBQK41CV?tag=avgskully-21" rel="noopener noreferrer"&gt;Zebronics 4-Port USB Hub&lt;/a&gt;&lt;/strong&gt; — useful if your laptop is short on USB ports after plugging in an external drive and Ethernet adapter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.amazon.in/dp/B01DGVKBC6?tag=avgskully-21" rel="noopener noreferrer"&gt;Cat 6 Ethernet Cable (5m)&lt;/a&gt;&lt;/strong&gt; — run this from your router to the server. Cat 6 handles gigabit speeds without any issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: These are affiliate links. If you purchase through them, I earn a small commission at no extra cost to you. I only recommend products I'd actually use.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;📂 Part 3: Turn your server into a NAS with Samba&lt;/strong&gt; — We'll make these same folders accessible directly in Mac Finder and Windows Explorer as network drives. This is the fastest way to transfer large files — drag and drop, just like a USB drive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 Part 4: Access your server from anywhere&lt;/strong&gt; — Tailscale for private access, Oracle Free VPS + frp for public sharing, and SSL for your custom domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔒 Part 5: Security hardening + lessons learned&lt;/strong&gt; — UFW, Fail2Ban, SSH hardening, and everything I wish I'd known before starting.&lt;/p&gt;

&lt;p&gt;All the config files from this series are available in the companion GitHub repo:&lt;br&gt;
👉 &lt;a href="https://github.com/sasrath/homecloud" rel="noopener noreferrer"&gt;github.com/sasrath/homecloud&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Hit Follow if you don't want to miss Part 3.&lt;/strong&gt; And if you run into any issues with the setup, drop a comment — I'll help you debug it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your Google Drive replacement is now running. Next up: making it work like a real NAS.&lt;/em&gt; 💾&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>docker</category>
      <category>nextcloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Replaced Google Drive with a Home Server That Costs Almost Nothing</title>
      <dc:creator>Sasanka Rath</dc:creator>
      <pubDate>Sat, 21 Mar 2026 06:27:20 +0000</pubDate>
      <link>https://dev.to/avgskully/i-replaced-google-drive-with-a-home-server-that-costs-almost-nothing-2c6l</link>
      <guid>https://dev.to/avgskully/i-replaced-google-drive-with-a-home-server-that-costs-almost-nothing-2c6l</guid>
      <description>&lt;p&gt;I was paying ₹650/month for Google One's 2TB plan. That's ₹6,500 a year — for storage I don't own, on servers I don't control, where my photos could be used to train AI models.&lt;/p&gt;

&lt;p&gt;Then I looked at the old HP Pavilion x360 collecting dust on my shelf. 1TB hard drive. 8GB RAM. A perfectly good Intel i5 processor doing absolutely nothing.&lt;/p&gt;

&lt;p&gt;What if I could turn it into my own cloud?&lt;/p&gt;

&lt;p&gt;Turns out, you can. And the only fixed cost is a domain name — about ₹70/month. Everything else is free, open-source software. Electricity varies by device and local rates, but a laptop sips power compared to a desktop.&lt;/p&gt;

&lt;p&gt;This is Part 1 of a 5-part series where I'll walk you through the entire setup. In this post, I'll cover the &lt;em&gt;why&lt;/em&gt; and the &lt;em&gt;what&lt;/em&gt;. The &lt;em&gt;how&lt;/em&gt; starts in Part 2.&lt;/p&gt;




&lt;h2&gt;
  
  
  What my server does
&lt;/h2&gt;

&lt;p&gt;My home server replaces Google Drive, Google Photos, and then some:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personal cloud storage&lt;/strong&gt; — I browse, upload, and organize files from any browser using Nextcloud, a self-hosted Google Drive alternative&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic phone photo backup&lt;/strong&gt; — every photo I take syncs to my server instantly via the Nextcloud mobile app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAS file sharing&lt;/strong&gt; — my Mac sees the server as an external drive in Finder, just like a USB hard drive (via Samba)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote access from anywhere&lt;/strong&gt; — whether I'm home or traveling, I reach everything via a custom domain or an encrypted private VPN&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure sharing with family&lt;/strong&gt; — I share photo albums and videos via secure links. Recipients don't need an account, they just open a link in their browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No monthly storage fees. No upload limits. No one scanning my files. Just my data, on my hardware, under my control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not just use Google Drive?
&lt;/h2&gt;

&lt;p&gt;Let me be specific about the problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost compounds.&lt;/strong&gt; Google One 2TB costs ₹6,500/year today. Over 5 years, that's ₹32,500 — and Google has a history of raising prices. My server's hardware was already paid for. The domain costs ₹850/year. That's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage is capped.&lt;/strong&gt; Hit 2TB on Google and you're paying ₹16,250/year for 5TB. With my server, I plug in a ₹5,000 external hard drive and I have 5TB. Once. No recurring cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don't own the data.&lt;/strong&gt; Google can change terms of service, lock your account, or discontinue a product (remember Google+, Inbox, Stadia?). My files live on a hard drive in my house.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy.&lt;/strong&gt; Google Photos uses your images for AI model training. Your documents pass through their servers. With a home server, your data never leaves your network unless you explicitly share it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture — how it all connects
&lt;/h2&gt;

&lt;p&gt;This is the part that took the most thinking. Here's the challenge: &lt;strong&gt;most Indian ISPs (Airtel, Jio, ACT) use CGNAT&lt;/strong&gt;, which means your home doesn't have a public IP address. Nobody on the internet can reach your server directly.&lt;/p&gt;

&lt;p&gt;I solved this with a two-path architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                        PUBLIC PATH (for sharing with others)

                    🌐  Internet
                        ↓
                    ☁️  Cloudflare — DNS + SSL + DDoS protection
                        ↓
                    🖧  Oracle Free VPS — public gateway (hides your home IP)
                        ↓  frp tunnel (encrypted)
                    🖥  Home Server — Ubuntu 24.04 | 8GB RAM | 1TB HDD
                        ↓
                    📦  Nextcloud  ·  Samba NAS  ·  Docker


                        PRIVATE PATH (for personal use)

                    📱  Your phone / laptop
                        ↓  WireGuard encrypted tunnel
                    🖥  Home Server directly — no VPS, full speed, fully private
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Two paths, two purposes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Public path&lt;/strong&gt; — for when I share a photo album with family. Traffic goes: Internet → Cloudflare → Oracle VPS → encrypted frp tunnel → my home server. This gives me a clean URL (&lt;code&gt;files.yourdomain.com&lt;/code&gt;) that anyone can open in a browser. My real home IP is completely hidden behind the VPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private path&lt;/strong&gt; — for my personal use. Tailscale creates a direct WireGuard VPN tunnel between my devices and my server. No middleman. No bandwidth limits. Military-grade encryption. I use this for streaming large videos, syncing files, and SSH access. It's free for up to 100 devices.&lt;/p&gt;

&lt;p&gt;Why two paths? The public path exists &lt;em&gt;only&lt;/em&gt; for sharing. Tailscale is faster and more secure, but it requires the app to be installed on every device. Family members who just want to see wedding photos shouldn't need to install a VPN.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hardware — it's probably on your shelf
&lt;/h2&gt;

&lt;p&gt;You don't need fancy hardware. Any old laptop or mini PC works. A laptop is actually &lt;em&gt;ideal&lt;/em&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Built-in UPS&lt;/strong&gt; — the battery keeps the server running during power cuts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low power&lt;/strong&gt; — a laptop uses a fraction of what a desktop draws&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small and quiet&lt;/strong&gt; — no fan noise, tuck it behind your router&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what I'm running:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;What I used&lt;/th&gt;
&lt;th&gt;Minimum you need&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Device&lt;/td&gt;
&lt;td&gt;HP Pavilion x360&lt;/td&gt;
&lt;td&gt;Any x86 laptop or mini PC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Intel i5&lt;/td&gt;
&lt;td&gt;Any dual-core processor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;4GB minimum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;1TB HDD&lt;/td&gt;
&lt;td&gt;256GB+ (expand anytime with USB drives)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;WiFi (200Mbps Airtel)&lt;/td&gt;
&lt;td&gt;Ethernet preferred for reliability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Ubuntu Server 24.04 LTS&lt;/td&gt;
&lt;td&gt;Any Debian-based Linux distro&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A Raspberry Pi 4/5 also works for lighter usage, though I'd recommend a proper laptop or mini PC if you plan to serve media files.&lt;/p&gt;




&lt;h2&gt;
  
  
  The software stack — everything is free
&lt;/h2&gt;

&lt;p&gt;This blew my mind when I first set it up. Every single piece of software is open source and free:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Software&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ubuntu Server 24.04&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The operating system — lightweight, no GUI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs Nextcloud in an isolated container for easy management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nextcloud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The Google Drive replacement — web UI, mobile apps, desktop sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Samba&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Makes your server appear as a network drive on Mac/Windows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tailscale&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates an encrypted private tunnel to your server from anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;frp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Punches through CGNAT so the internet can reach your home server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nginx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web server on the VPS that handles incoming traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Certbot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free SSL certificates so your custom domain uses HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UFW + Fail2Ban&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Firewall and brute-force protection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not a single subscription. Not a single license fee. The open-source community has built everything you need.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cost comparison
&lt;/h2&gt;

&lt;p&gt;Here's why this project was worth a weekend of setup:&lt;/p&gt;

&lt;h3&gt;
  
  
  Google One 2TB
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly cost&lt;/td&gt;
&lt;td&gt;₹650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yearly cost&lt;/td&gt;
&lt;td&gt;₹6,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5-year cost&lt;/td&gt;
&lt;td&gt;₹32,500+ (assuming no price hikes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage limit&lt;/td&gt;
&lt;td&gt;2TB, then pay more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data ownership&lt;/td&gt;
&lt;td&gt;Google's servers, Google's terms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Home server
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Domain name&lt;/td&gt;
&lt;td&gt;~₹850/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Electricity&lt;/td&gt;
&lt;td&gt;Varies (laptops are very low-power)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle VPS&lt;/td&gt;
&lt;td&gt;Free forever (Always Free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailscale&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All software&lt;/td&gt;
&lt;td&gt;Free and open source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage limit&lt;/td&gt;
&lt;td&gt;Whatever hard drive you plug in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data ownership&lt;/td&gt;
&lt;td&gt;Yours. Physically in your house.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The hardware? Already paid for — it was gathering dust. Even if you bought a used laptop for ₹10,000, it pays for itself in under 2 years compared to Google One.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is it reliable?
&lt;/h2&gt;

&lt;p&gt;I've been running this setup for a while now. Here's what I've observed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uptime is excellent.&lt;/strong&gt; The server boots automatically after power cuts (BIOS setting), and all services — Docker, Nextcloud, Tailscale, frp — start on their own. After a power outage, everything is back online in 2-3 minutes without me touching anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WiFi has been the weak link.&lt;/strong&gt; I've had occasional drops on the WiFi connection. If your server is near the router, use an Ethernet cable. It's noticeably more reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed is great on the local network.&lt;/strong&gt; Transferring files via Samba on my home network is as fast as a USB drive. Remote access via Tailscale is limited by your internet upload speed, but perfectly usable for photos and documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintenance is minimal.&lt;/strong&gt; I check on it maybe once a week — glance at disk space, make sure services are running. Docker makes updates trivial: pull the latest image and restart.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's coming in this series
&lt;/h2&gt;

&lt;p&gt;This was the &lt;em&gt;why&lt;/em&gt; and the &lt;em&gt;what&lt;/em&gt;. Starting next, we get into the &lt;em&gt;how&lt;/em&gt; — complete, copy-paste-ready tutorials:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📦 Part 2: Setting up Nextcloud with Docker&lt;/strong&gt; — Install Docker, write the compose file, configure Nextcloud, set up phone auto-backup, and get desktop sync working. Your Google Drive replacement, running in 30 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📂 Part 3: Turn your server into a NAS with Samba&lt;/strong&gt; — Configure Samba shares, connect from Mac and Windows, set up auto-mount on startup, and integrate with Nextcloud so both see the same files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 Part 4: Access your server from anywhere&lt;/strong&gt; — Set up Tailscale for private access, get an Oracle free VPS, configure frp tunneling, set up Nginx and SSL, and point your custom domain to your home server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔒 Part 5: Security hardening + lessons learned&lt;/strong&gt; — Configure UFW firewall, set up Fail2Ban, harden SSH, add monitoring, and everything I wish I'd known before starting.&lt;/p&gt;

&lt;p&gt;Each post is standalone — you can jump to whichever part you need. But if you're building from scratch, follow them in order.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;If you don't want to miss the next part, hit Follow.&lt;/strong&gt; I'll also be sharing shorter tips and updates on  Twitter/X (@ASkully58162) as I keep building.&lt;/p&gt;

&lt;p&gt;Have questions about the setup? Drop them in the comments — I'll answer everything and it might shape what I cover in the next parts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Got an old laptop collecting dust? It's about to become the most useful device you own.&lt;/em&gt; 🖥️&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>linux</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
