Building a Custom Headless CMS for Astro (Instead of Paying for One)
We chose Astro as our frontend framework for its performance, developer experience, and versatility. But a fast frontend is only half the equation. Content still needs a home. Our clients need ways to update pages, publish blog posts, and manage their digital presence as part of any web design engagement. Instead of swapping one dependency (WordPress) for another SaaS CMS, we built our own custom headless CMS from scratch. What started as a simple internal tool for managing design showcases has evolved into a full-featured content management platform that powers all our Astro client sites.
Why Not Sanity, Contentful, or Strapi?
The most immediate problem with commercial SaaS CMS platforms is pricing. Sanity charges $99 per seat per month. Contentful starts at $300 per month for basic plans. When you manage over 10 client sites with multiple editors each, the math breaks down fast. Two editors per site across 10 sites means 20 seats. At $99 per seat, that is nearly $2,000 per month, or $24,000 annually, just for content management access.
Beyond cost, vendor lock-in is a real strategic risk. Your content lives on someone else's infrastructure, in their proprietary format. If they change pricing, deprecate features, or shut down, you scramble to migrate. We have seen this play out with other SaaS tools, and we prefer to own our data and infrastructure.
Strapi, the self-hosted open-source option, eliminates per-seat fees but brings its own maintenance burden as a large Node.js application. Its content modeling is flexible but opinionated in ways that did not always fit our projects. We wanted full control over the content API, the editing experience, and the data structure.
What We Built
The backend uses FastAPI, a high-performance Python web framework, with SQLAlchemy as the ORM and MariaDB for storage. FastAPI gives us fast, well-documented API endpoints with built-in async support. The admin interface uses Tailwind CSS for styling and Alpine.js for lightweight interactivity, creating a responsive dashboard without heavy JavaScript framework overhead.
The database schema spans 16 tables covering the full scope of content management: users, sites, pages, edit history, content locks, blueprints, API keys, page translations, forms, form submissions, activity logs, image variants, generation logs, and content templates. This structure supports multi-tenant operation across all our client sites from a single installation.
Key features include:
- Headless JSON API at /api/v1/ with API key authentication for secure external content access.
- Multi-language support with automatic hreflang-aware sitemap generation for international SEO.
- Blueprints for structured content templates that ensure consistency across content types.
- Scheduled publishing with a background task runner for automatic content release.
- Pessimistic content locking with 15-minute expiry and heartbeat refresh to prevent edit conflicts.
- Image transforms via Pillow: automatic thumbnail, small, medium, and large variants plus WebP conversion.
- Activity audit logs for tracking every action across the platform.
The AI Editor
This is what sets our CMS apart from everything else on the market. Claude Sonnet powers a chat-based editing experience that fundamentally changes how non-technical users interact with their website content.
Here is how it works: a client opens a page in the CMS and starts a chat. They describe what they want changed in plain language. "Make the hero section more prominent and add a call-to-action button." "Rewrite this paragraph to be more concise." "Add a testimonial section below the services overview." Claude Sonnet processes the instruction, understands the existing HTML context, and edits the markup directly.
A live preview renders side-by-side with the chat. Clients see changes in real time and can iterate with follow-up instructions until the content matches their vision. No WYSIWYG complexity, no drag-and-drop learning curve, no HTML knowledge required.
The AI editor lets clients describe what they want in plain language and see it happen in real time. No design tools to learn, no code to write. Just say what you need and review the result.
Claude Haiku handles automatic revision summaries. Every save generates a human-readable description of what changed: "Revised hero headline for clarity and added call to action" instead of a generic "Page updated" message. This makes the edit history actually useful for tracking content evolution and simplifying rollbacks.
How Astro Connects
We designed two integration patterns for connecting Astro frontends to the CMS.
Pattern 1: Direct database queries. For sites hosted on the same server, Astro SSR pages query the CMS's MariaDB tables directly using a mysql2 connection pool. A blog listing page calls getBlogPosts() which runs a SELECT against the blog_posts table. A dynamic case study route calls getCaseStudy(slug). This approach is simple and fast, with no HTTP overhead. We use this pattern on Med-Vision, a healthcare consulting site built with Astro 4 SSR.
Pattern 2: Headless JSON API. For distributed setups or static site generation, Astro makes authenticated fetch requests to /api/v1/ endpoints during the build process. The API returns structured JSON that Astro uses to generate static HTML files. This pattern works for any frontend, not just Astro, and supports aggressive caching.
Both patterns are actively used across our client sites. Direct DB is simpler for co-located deployments. The API provides flexibility for distributed architectures. This dual approach connects naturally with our WordPress migration workflow and leverages the full capabilities of the Astro framework.
SSH/SFTP Site Management
The CMS also functions as a remote site manager. Using Paramiko, a Python SSH library, it connects to live client servers for direct file management. Users can browse the remote file tree, edit files in-browser, upload assets, and manage permissions, all from the CMS dashboard.
This is particularly valuable during our static site conversions. When migrating a 100+ page legacy site to Astro, there are numerous static assets, configuration files, and redirects that need management across servers. The CMS centralizes this work alongside content management, eliminating the need for separate SFTP clients or terminal sessions for routine file operations.
The Build-vs-Buy Tradeoff
Building a CMS from scratch is significant engineering effort. The initial version took weeks of dedicated development, and we continue iterating on features and performance. This is not a weekend project.
But for a company managing 10+ client sites, the economics are clear:
Custom CMS (5-Year Cost)
Initial development: ~$20,000-40,000 (one-time). Annual maintenance: ~$5,000-10,000. 5-year total: $40,000-90,000. Per-seat cost for additional users: $0. Adding new client sites: $0.
SaaS CMS (5-Year Cost)
20 seats at ~$99/seat/month: $23,760/year. 5-year total: $118,800-180,000. Additional API calls and features incur extra fees. Price increases at vendor discretion.
Beyond cost, you get complete control over features and roadmap. The AI editor is something no off-the-shelf CMS offers at this level of integration. The CMS is shaped to our workflow, not the other way around.
Our recommendation: if you manage 1-2 sites, buy a SaaS CMS. The development investment does not justify the savings. If you manage 10+ sites with the development capability to build and maintain a custom solution, the investment pays for itself quickly in both direct cost savings and strategic flexibility.
You do not need a $500/month SaaS CMS to go headless. If you have the development capability and manage multiple sites, a custom CMS gives you full control, eliminates recurring per-seat fees, and lets you build features like an AI-powered editor that commercial platforms do not offer. Our CMS powers content across all our Astro client sites from a single platform, and the per-site cost is effectively zero. If you need a CMS that works for your business without the overhead, let's talk.