← Systems
Systems · Internal Systems · · Active

Building My Own Medium at $0: A Publishing System Without Platform Dependencies

01
Problem
Medium charges $5/month for custom domains, locks content behind paywalls, owns distribution, and can change terms anytime. Ghost costs $9/month minimum. Substack takes 10% of revenue and controls reader relationships. All platforms create dependency and extract rent for what should be infrastructure-level capability: publishing words to the internet.
02
Model
Treat publishing infrastructure as a solved problem. Use commodity tools (Markdown, Git, CDN) to own the entire stack from writing to distribution. The system should cost nothing to run, be fully portable, and eliminate all platform dependencies while maintaining professional quality.
03
System
Write in Obsidian (local Markdown), commit to GitHub (version control + source of truth), auto-deploy via Vercel (global CDN + HTTPS), serve from custom domain (ukdiaries.com). Content lives in MDX files with structured frontmatter. Astro generates static HTML at build time. Dark mode, RSS, typography, and search are owned code. Total monthly cost: $0.
04
Metrics
Deploy time: 30 seconds from git push to live. Build cost: $0 (within Vercel free tier). Lighthouse score: 95+ on all metrics. Content portability: 100% (plain markdown files). Platform risk: 0% (no vendor dependencies). Time to publish: 2 minutes from final draft to live URL.
05
Lessons
Infrastructure should be invisible. The best publishing system is one you forget exists — it just works. Platform risk compounds over time; ownership compounds returns. The constraint of zero cost forced architectural clarity: no database, no CMS, no complexity. Git as CMS is underrated for solo knowledge work. The gap between 'free' and 'paid' publishing tools is a myth perpetuated by platforms that want rent.

The Problem Space

I needed a place to publish long-form thinking. Three modules: essays, systems documentation, and technical notes. Requirements were clear:

  1. Ownership — I need to own the content, the design, the distribution, and the reader relationship
  2. Portability — Must be able to move to any platform in 10 minutes
  3. Longevity — Should work for 10+ years without breaking
  4. Zero cost — Should not require payment to exist
  5. No platform risk — No terms of service changes, no acquired-and-shut-down risk
  6. Professional quality — Must look and feel authoritative

Medium fails criteria 1, 2, 4, and 5. Substack fails 1, 4, and 5. Ghost and WordPress fail 4 and add maintenance burden. Notion-as-website fails 2 and 5.

The fundamental issue: all platforms extract rent for what should be commodity infrastructure. Publishing text to the internet is a solved problem. Why pay recurring fees for it?

The Model

After reviewing the landscape, the model became obvious: treat publishing as a build pipeline, not a platform.

The stack:

  • Write: Obsidian (free, local-first, markdown)
  • Version control: Git + GitHub (free)
  • Build: Astro (open source, generates static HTML)
  • Deploy: Vercel (free tier: 100GB bandwidth/month)
  • Domain: GoDaddy (already owned, $12/year)
  • Analytics: Plausible (optional, $9/month — I haven’t enabled it yet)

The entire system compiles down to static HTML. No database. No server. No runtime. Just files served from a CDN.

Why this works:

  • Markdown is the most portable format that exists
  • Git gives me infinite version history and branching
  • Static HTML is the fastest, most reliable way to serve content
  • CDNs are commodity infrastructure (Vercel, Cloudflare, Netlify — all have free tiers)
  • No vendor lock-in at any layer

System Implementation

Layer 1: Content Creation (Obsidian)

I write in Obsidian using three templates — one per content type:

Thinking template:

---
title: ""
subtitle: ""
theme: "Philosophy"
publishDate: 2025-02-23
draft: true
---

## Core Thesis
[State argument]

## Main Body
[Content]

Systems template:

---
title: ""
category: "Internal Systems"
problem: ""
model: ""
system: ""
metrics: ""
lessons: ""
draft: true
---

Technical template:

---
title: ""
type: "Architecture"
tags: []
draft: true
---

## The Problem
## The Solution
## When This Works

Obsidian gives me:

  • Local-first editing (works offline, no API calls)
  • Graph view (see connections between ideas)
  • Backlinks (automatic bidirectional links)
  • Full-text search across all notes
  • Zero cost

Layer 2: Version Control (Git + GitHub)

Every piece of content lives in src/content/ as an MDX file:

src/content/
├── thinking/
│   ├── architecture-of-belief.mdx
│   └── predictive-processing.mdx
├── systems/
│   └── zero-dollar-publishing.mdx
└── technical/
    └── edge-first-architecture.mdx

Content schemas are enforced in src/content/config.ts:

const systemsCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    problem: z.string(),
    model: z.string(),
    system: z.string(),
    metrics: z.string(),
    lessons: z.string(),
    category: z.enum(['Growth Systems', 'Internal Systems', 'Business Architecture']),
    status: z.enum(['Active', 'Archived']),
    draft: z.boolean().default(false),
  }),
});

This gives me:

  • Type-safe frontmatter (TypeScript catches schema errors at build time)
  • Infinite version history (can see every edit, revert anytime)
  • Branch-based drafting (can work on experimental pieces in branches)
  • Automatic backup (GitHub is the source of truth)

Layer 3: Build System (Astro)

Astro compiles MDX → static HTML at build time. No runtime, no hydration, just HTML/CSS.

Key files:

  • src/pages/systems/[slug].astro — Dynamic route for all systems entries
  • src/layouts/Base.astro — HTML shell, meta tags, fonts
  • src/styles/global.css — Design system (CSS custom properties)

The build process:

  1. Read all MDX files from src/content/
  2. Validate frontmatter against schemas
  3. Compile markdown → HTML
  4. Apply syntax highlighting (Shiki)
  5. Generate static pages
  6. Output to dist/

Build time: ~8 seconds for 3 posts. Scales linearly.

Layer 4: Deployment (Vercel)

Connected GitHub → Vercel once. Now every git push triggers:

  1. Vercel detects push
  2. Runs npm run build
  3. Deploys dist/ to global CDN
  4. Live in ~30 seconds

Vercel free tier:

  • 100GB bandwidth/month (enough for 10K+ pageviews)
  • Automatic HTTPS (Let’s Encrypt)
  • Global CDN (content served from nearest edge location)
  • Preview deployments (every PR gets a unique URL)

Layer 5: Domain (GoDaddy → Vercel)

Added ukdiaries.com in Vercel dashboard. Configured DNS:

A     @     76.76.21.21       (Vercel's IP)
CNAME www   cname.vercel-dns.com

Propagation: 15 minutes. SSL: automatic.

Total cost: $0/month (domain was $12/year, already owned).

The Publishing Workflow

End-to-end, from idea → live:

1. Open Obsidian → Insert systems template
2. Write content (set draft: false when ready)
3. Copy MDX file to Astro project src/content/systems/
4. Terminal:
   git add .
   git commit -m "system: zero-dollar publishing"
   git push
5. Wait 30 seconds
6. Live at ukdiaries.com/systems/zero-dollar-publishing

Time from “ready to publish” to “live URL”: 2 minutes.

No CMS login. No admin panel. No database. No clicking through UI. Just files and Git.

What I Built

The site includes:

  • Home page — Latest from all three modules
  • Module indexes — Filterable lists (Thinking, Systems, Technical)
  • Reading layout — Typography-optimized, scroll progress bar
  • Dark/light mode — Persistent via localStorage
  • RSS feed — Auto-generated from content collections
  • SEO — Meta tags, Open Graph, sitemap
  • Performance — Lighthouse 95+ (no JavaScript except theme toggle)

Design tokens:

  • Serif (Libre Baskerville) for essays
  • Sans (DM Sans) for UI
  • Warm parchment background (#f5f3ef)
  • Forest green accent (#1a3a2a)
  • Systems entries use 5-column framework grid

Total lines of code: ~2,500 (including all templates, components, styles).

Current Metrics (February 2025)

Cost:

  • Monthly: $0
  • Annual: $0 (domain already owned)

Performance:

  • Lighthouse Performance: 98
  • Time to Interactive: under 1 second
  • First Contentful Paint: 0.4s
  • Deploy time: 30 seconds average

Portability:

  • Content format: Plain markdown (MDX)
  • Vendor dependencies: Zero
  • Time to migrate to new platform: ~10 minutes (copy MDX files, done)

Publishing velocity:

  • Time to add new post: 2 minutes
  • Build time per post: under 1 second
  • Number of clicks to publish: 0 (just git push)

What I’d Change

Image handling needs work. Right now images go in public/images/. This works but isn’t elegant. Would consider:

  • Cloudflare R2 (free tier: 10GB storage)
  • Astro’s image optimization (@astrojs/image)
  • Or just keep it simple — static images are fine

Reading time calculation is hardcoded. Currently shows “12 min read” for everything. Should add:

npm install reading-time mdast-util-to-string

And implement remark plugin to calculate actual reading time from word count.

No search yet. Plan is to use Fuse.js with a statically generated search index. Would add ~15KB to the bundle. Not critical yet with only 3 posts.

No newsletter integration. Placeholder says “Subscribe via Substack” but I haven’t set up the Substack yet. When I do, it’s just an embed — no code changes needed.

Mobile nav is hidden on small screens. Should add a hamburger menu. Current approach: just hide the nav on mobile and rely on home page links. Works but not ideal.

Why This Works

The constraint of zero cost forced architectural clarity. Every decision was: “Can this be static?” The answer was almost always yes.

Key insights:

  1. Most publishing doesn’t need a database. You’re not Twitter. You’re not tracking real-time user interactions. You’re publishing words. That’s a build-time operation, not a runtime operation.

  2. Git is an underrated CMS for solo knowledge work. It gives you version control, branching, and a complete audit trail. The UI is worse than Notion, but the primitives are better.

  3. Static beats Dynamic for content that doesn’t change often. Every dynamic request is a potential failure point. Static HTML just works.

  4. Platform risk compounds. If I’d built on Medium in 2020, I’d be locked in by now. Five years from now, I’ll still own this system. The content is portable. The code is mine.

  5. The gap between “free” and “paid” tools is smaller than platforms claim. Vercel’s free tier is production-grade. You don’t need Ghost or WordPress unless you’re building a team or need specific CMS features.

When This System Breaks

This works because I’m:

  • Solo (no need for collaborative editing)
  • Technical (comfortable with Git, VS Code, Terminal)
  • Publishing long-form (not real-time news or social features)
  • Low-volume (~1 post/week, not 10/day)

This system would NOT work if:

  • You need collaborative editing (multiple writers)
  • You’re non-technical and can’t use Git
  • You need scheduled publishing, content approval workflows, or editorial tools
  • You’re publishing high-frequency content (breaking news, daily posts)
  • You want a WYSIWYG editor

At that point, you’d add a headless CMS (Sanity, Contentful) or use a platform (Ghost). But for solo knowledge work? This is optimal.

The Deeper Point

Publishing platforms exist because they solve coordination problems: multiple writers, editors, payment processing, subscriber management, content discovery.

But if you’re solo and writing for learning/thinking (not monetization), you don’t have those problems. You have a different problem: you want to own your work for decades.

Medium can change their terms. Substack can add paywalls. Ghost can 10x their pricing. Platforms optimize for their business model, not your longevity.

Static HTML on a CDN doesn’t have a business model. It just exists. That’s the point.


Total setup time: 6 hours (learning Astro, building templates, wiring components)
Total cost: $0/month
Ownership: 100%
Platform risk: 0%

This is how publishing should work.

```