Modula

Self-hosted headless CMS written in Go.

Two commands. Running.

SQLite database, config file, all tables, bootstrap data. Open-source under AGPL-3.0. All features included. No tiers, no usage caps, no upgrade prompts.

Schema Templates

Start with structure, not a blank page

Flexibility also means decision fatigue. Total freedom is great for about forty-five seconds, then you're staring at an empty datatypes list wondering where to start.

ModulaCMS ships with pre-built schema templates: blog, portfolio, e-commerce, documentation, FAQ, and more. Embedded in the binary, installable through the TUI. Pick one and you've got a working content structure with sensible fields in seconds.

But these aren't locked-in templates baked into the core. They're just datatypes and fields, the same as anything you'd build yourself. Don't need the excerpt field on your blog post? Remove it. Want to add a custom "mood" dropdown? Add it. The starter schemas get you to "something works" fast, then get out of your way.

Schema

How Content Works

ModulaCMS ships with one datatype (Pages) and one field (Title). Everything else is yours.

Five core tables model any content structure. Datatypes define what kinds of content exist. Fields define their properties. Content data holds instances. Content fields hold values. Routes assign content to URLs. All user-defined, all at runtime, all backed by relational tables with proper indexes.

A blog: define a "Blog" datatype, add fields to identify it (title, slug, author, publish date), then create child datatypes for the content that goes inside it. A rich text block, a featured image, a pull quote. Assign fields to each. Now you have a parent type with composable children you arrange however you want.

Features

Everything you need. Nothing you don't.

Single binary

27-29 MB. Three servers (HTTP, HTTPS, SSH), admin panel, TUI, plugin runtime, media pipeline, deploy engine. No runtime dependencies.

Multi-database

SQLite for local dev, MySQL or PostgreSQL for production. Switch with one config change. Stateless horizontal scaling.

User-defined schema

Define datatypes and fields at runtime. Posts, products, calendars, anything. Add or remove fields without breaking existing content.

Content versioning

Every publish creates an immutable snapshot. Schedule publication. Restore any previous version. Full history from day one.

Four interfaces

CLI for automation, TUI over SSH for developers, admin panel for content editors, REST API for applications. Same data, pick your environment.

SDKs

TypeScript, Go, Swift. Typed responses, branded IDs, zero external dependencies.

Sandboxed plugins

Lua VMs with isolated database storage, operation budgets, circuit breakers, admin approval. Extend the CMS without compromising it.

Media pipeline

S3-compatible storage. Automatic WebP conversion, focal point cropping, responsive dimension presets. Optimization at upload, not request time.

Deploy sync

Export and import content between instances. Dry-run preview. Hash-validated payloads. Schema version verification.

Multi-format API

Output as clean JSON, or in formats compatible with Contentful, Sanity, Strapi, and WordPress. Migrate without rewriting your data layer.

Plugins

When the Schema Isn't Enough

The content schema is intentionally flexible. But some problems need more than flexible content trees. They need structured data with direct relational mapping.

E-commerce is one. Orders, customers, products, inventory, shipping. These aren't website content. They're interconnected records with transactional integrity requirements that a content tree was never designed to handle. Forms are another. You need a schema for the form layout on the page, but you also need workflows that store and process submissions.

Sandboxed Lua plugins run inside the CMS with their own isolated database tables, operation budgets, circuit breakers, and admin approval. Build an order management system, a form processor, a booking engine. Your plugin gets its own storage, its own API endpoints, and the full protection of the CMS runtime. No separate service to deploy.

AI Integration

AI-Ready

Connect any AI assistant to your CMS. Draft content, build schemas, manage media, configure permissions. All through natural language.

ModulaCMS includes modula-mcp, a Model Context Protocol server that works with any compatible AI assistant. Point it at any environment and control how much access the AI has. Let it draft blog posts on your local instance but only read content in production. Give it full schema access in development so you can describe the content model you want and let the model build it.

Content, schema, routes, media, users, roles, plugins, deploy sync, configuration. Everything the TUI and admin panel can do, an AI assistant can do through MCP. Two environment variables to configure.

Authentication

Developer-Friendly Auth

ssh modula and you're in. No password. No 2FA prompt. Your SSH key is your key to every instance you manage.

No more keeping a spreadsheet of thirty client logins with different username conventions and expired passwords. If a client needs a content update, SSH drops you into the TUI. Make the change, get back to your work.

Native email and password authentication works out of the box. Full spec-compliant OAuth connects to any provider: Google, GitHub, Azure, Okta, Auth0, your company's homegrown identity server. API tokens are just as simple. Generate one from the TUI, drop it in your config, and use modula connect to run the TUI on your machine against any deployed API.

Two commands. See for yourself.

Security

Security by default

Every query is parameterized. ModulaCMS uses sqlc to generate all database access from typed SQL files. No query is ever constructed from string concatenation. SQL injection is not a category of bug that exists in this codebase.

Every protected endpoint passes through authentication middleware before any handler runs. Session cookies are HTTP-only with 24-hour expiration. Behind authentication sits a permission guard system with 47 discrete permissions mapped to roles. Permission lookups are O(1). If no permission set is found in the request context, the response is 403. No fallthrough. No defaults.

Auth endpoints are rate-limited at 10 requests per minute per IP using a token bucket algorithm. Docker deployments run as a non-root user (UID 1000:1000). The multi-stage build compiles with CGO for native SQLite and WebP support, then strips the toolchain from the runtime image.