<?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: Tech Boy</title>
    <description>The latest articles on DEV Community by Tech Boy (@iamvps).</description>
    <link>https://dev.to/iamvps</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%2F3951939%2Fc2ee12ec-b870-4b50-a7c4-89059a263934.jpeg</url>
      <title>DEV Community: Tech Boy</title>
      <link>https://dev.to/iamvps</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iamvps"/>
    <language>en</language>
    <item>
      <title>I built a free Bitly/TinyURL alternative and self-hosted it on a $6/mo VPS — here's the full stack</title>
      <dc:creator>Tech Boy</dc:creator>
      <pubDate>Tue, 26 May 2026 07:10:08 +0000</pubDate>
      <link>https://dev.to/iamvps/i-built-a-free-bitlytinyurl-alternative-and-self-hosted-it-on-a-6mo-vps-heres-the-full-stack-4cd</link>
      <guid>https://dev.to/iamvps/i-built-a-free-bitlytinyurl-alternative-and-self-hosted-it-on-a-6mo-vps-heres-the-full-stack-4cd</guid>
      <description>&lt;p&gt;I got tired of Bitly limiting me to 10 links/month for free and TinyURL having zero analytics. So I built &lt;a href="//meshalive.com"&gt;meshalive.com&lt;/a&gt; — a free URL shortener with real-time click analytics, dynamic QR codes, custom slugs, and a REST API. Here's how it's built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: Go + Fiber — fast, minimal, compiles to a single binary&lt;/li&gt;
&lt;li&gt;Database: PostgreSQL with sqlc for type-safe queries (no ORM)&lt;/li&gt;
&lt;li&gt;Cache: Redis for sub-2ms redirect lookups — the short slug is looked up from Redis, DB is the fallback&lt;/li&gt;
&lt;li&gt;Frontend: Next.js 15 (App Router) + TypeScript, zero UI framework, all inline styles&lt;/li&gt;
&lt;li&gt;Infra: A single $6/mo VPS, Docker Compose, Nginx reverse proxy, Cloudflare in front&lt;/li&gt;
&lt;li&gt;Auth: JWT access tokens (15min TTL) + HttpOnly refresh cookies (7 days), rotated on every refresh&lt;/li&gt;
&lt;li&gt;Short domain: msha.live → every shortened link resolves through the Go API in &amp;lt;2ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How the redirect works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Browser → Cloudflare → Nginx → Go API&lt;br&gt;
    slug lookup: Redis first → Postgres fallback&lt;br&gt;
    → 301 to destination&lt;br&gt;
    → async click event written to Postgres&lt;/p&gt;

&lt;p&gt;The redirect path never touches Next.js — it goes directly to the Go API so there's no Node.js cold start penalty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's free (forever)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shorten any URL instantly — no account needed&lt;/li&gt;
&lt;li&gt;100 saved links with a free account&lt;/li&gt;
&lt;li&gt;Click analytics — geo, device, referrer&lt;/li&gt;
&lt;li&gt;Dynamic QR codes (update destination after printing)&lt;/li&gt;
&lt;li&gt;Custom slugs (msha.live/your-brand)&lt;/li&gt;
&lt;li&gt;Full REST API on paid plans from $4/mo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why self-hosted on one VPS instead of cloud functions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cost. A Lambda function + RDS + ElastiCache would run $40–80/mo for the same workload. The VPS runs everything for $6. Docker Compose means zero ops complexity — docker compose up -d and it's running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I learned&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NEXT_PUBLIC_ env vars in Next.js are baked at build time, not runtime — cost me an hour&lt;/li&gt;
&lt;li&gt;Docker bypasses UFW via iptables — remove port bindings from docker-compose.yml instead of using ufw deny&lt;/li&gt;
&lt;li&gt;backdropFilter: blur() on a parent element breaks position: fixed for any child — the mobile nav drawer was invisible for this reason&lt;/li&gt;
&lt;li&gt;Cloudflare caches 301 redirects in browsers permanently — test route changes in incognito&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try it: &lt;a href="//meshalive.com"&gt;meshalive.com&lt;/a&gt; — shorten a URL, no account needed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy to answer questions about the Go API, the sqlc setup, or the Nginx config.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
