Self-hosted service
EdiWang/Moonglade avatar
EdiWang/Moonglade

Moonglade: A C# blog engine built for Azure, with sharp edges for self-hosters

Blog system of runs on Microsoft Azure. Azure Blob Storage (Recommended) Create an Azure Blob Storage container with appropriate permissions: Enable CDN in admin settings for faster image delivery.

538 stars139 forksC#GPL-3.0

At a glance

What is it?
Moonglade is a self-hosted, ASP.NET Core blogging platform that pairs a full authoring workflow with Azure storage and a firm stance on where it will not run. This review covers its architecture, deployment paths, and the trade-offs you accept when you adopt it.
Who is it for?
Adopt Moonglade if you are a .NET developer who wants a full-featured blog with scheduled posts, Webmention, and a clean admin UI, and you are comfortable with Azure storage or Docker volumes. Skip it if you need a zero-config hosted service, if you serve users in mainland China (the README explicitly forbids it), or if you dislike GPL-3.0's copyleft terms.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 14, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What Moonglade actually solves

Moonglade is a personal blogging platform for developers who want to own their publishing stack. It solves the problem of assembling a blog from separate pieces: posts, comments, categories, tags, archives, pages, and an admin portal. Instead of wiring together a headless CMS, a comment service, and a static site generator, you get one ASP.NET Core application that serves both the public reading experience and the authenticated admin interface. The README lists the features plainly: Markdown with Mermaid diagrams, scheduled publishing, drafts, recycle bin behavior, Webmention, RSS and Atom feeds, sitemap, OpenSearch, and IndexNow. That is a broad feature set for a self-hosted project. The intended user is a developer who already lives in the .NET ecosystem and wants a blog that runs on Microsoft Azure, though the deployment section says it can run on any cloud provider or on-premises. The sharpest clue about its intended audience is the README's warning: the system must not be used to serve users in mainland China or to publish content prohibited by Chinese law. That is a legal boundary, not a technical one, and it tells you the author has a specific jurisdiction in mind.

How the pieces fit together

The repository is split into focused projects under src/. The web host is Moonglade.Web, which contains Razor Pages, API controllers, middleware, and static assets. Business logic lives in Moonglade.Features, which handles commands and queries for posts, pages, comments, categories, tags, and assets. Data access goes through EF Core, with separate provider projects for SQL Server and PostgreSQL. Configuration is persisted through Moonglade.Configuration, and authentication supports both local accounts with TOTP and standards-based OpenID Connect. Background services handle scheduled publishing and update checks. This is a layered architecture: web layer calls features, features call data, and configuration is a separate persisted store. The README does not describe the internal data flow in detail, but the project layout suggests a command-query pattern, where each feature has explicit operations. One notable design choice is the separation of image storage into two paths: one for public images and one for original images. The deployment script mounts separate writable Azure Files shares at /app/images and /app/images-origin. That separation is deliberate, and it has operational consequences.

Getting it running: Docker and Azure paths

The quickest path to a running instance is Docker. The README gives a single command: docker compose up -d. The Compose file maps two named volumes to /app/images and /app/images-origin, and it configures both ImageStorage paths. You must keep both volumes when recreating or upgrading the container, because public and original images are stored separately. For Azure, there is an automated deployment script that provisions a Bicep template. That template creates two writable Azure Files shares, mounts them into the App Service container at the same paths, and configures Moonglade accordingly. The README explicitly notes that the template does not provision a CDN and does not migrate images from an older Blob container. So if you are moving from a previous deployment, you handle migration yourself. For local development, you build with dotnet restore and dotnet build on the Moonglade.Web project, then run it with dotnet run. The default connection strings in appsettings.json point to SQL Server or PostgreSQL, and you switch providers by changing ConnectionStrings:DatabaseProvider to SqlServer or PostgreSql. The default admin credentials are admin/admin123, and first sign-in requires scanning a TOTP QR code. That is a sensible security default, even if the default password is weak.

Where Moonglade gets awkward

The most obvious limitation is the jurisdictional restriction. The README states in no uncertain terms that the system must not be used to serve users in mainland China or to publish content prohibited by Chinese law. That is a hard constraint that rules out a large potential user base. If you operate in that region, Moonglade is the wrong tool, period. Another limitation is the image storage setup. The separation of public and original images into two volumes or shares is robust, but it adds operational friction. You must remember to preserve both volumes across upgrades, and the Azure template does not set up a CDN for you. The README mentions enabling CDN in admin settings for faster image delivery, but the deployment script does not provision one. That means you are responsible for CDN configuration, which is extra work. Also, the default email configuration contains no provider credentials. The application starts, logs a warning, and runs with email notifications inactive. That is fine if you do not need email, but it is a missing piece that you must fill through deployment secrets if you want comment notifications.

The license and upgrade cost

Moonglade is released under GPL-3.0. That is a strong copyleft license. If you modify the code and distribute it, your modifications must also be GPL-3.0. For a personal blog that you self-host, this is rarely a problem, because you are not distributing the software. But if you plan to offer Moonglade as a service or embed it in a proprietary product, the license will force you to open your changes. The README does not discuss upgrade procedures, but the project has a regular release cadence: v16.5.0, v16.4.0, and v16.2.0 all arrived within a few weeks in August 2026. That suggests active maintenance, which is good for security fixes but means you should expect to track releases. The README warns to always use the Release branch, not master, for stable code. That is a clear signal that master may contain unreleased work. For upgrades, the Docker Compose approach with named volumes means you can pull a new image and recreate the container, as long as you keep the volumes. The Azure deployment script does not handle image migration, so upgrading an existing Azure deployment likely requires manual steps. The documentation does not detail a migration path, so you should test upgrades in a staging environment first.

Alternatives and where Moonglade fits

The natural alternative to Moonglade is a static site generator like Hugo or Jekyll, paired with a comment service like Disqus or utterances. The difference in approach is fundamental. A static generator produces HTML files that you host on any CDN; there is no database, no admin portal, and no server-side logic. Moonglade is a dynamic application with a database, scheduled publishing, and an authenticated admin UI. If you want to write in Markdown and commit to a repository, a static generator gives you version control and zero server maintenance. If you want a web-based editor, drafts, and scheduled posts without touching Git, Moonglade is closer to that workflow. Another alternative is a hosted platform like WordPress.com or Ghost, but those are not self-hosted in the same sense. Moonglade sits between static generators and managed platforms: you control the server, but you get dynamic features. The database requirement is a real cost. You need either SQL Server or PostgreSQL running somewhere, which is more moving parts than a static site. But if you are already running .NET and Azure, the incremental cost is small.

What to verify before you adopt

Before you commit to Moonglade, check three things. First, confirm that your database choice is supported. The README lists SQL Server and PostgreSQL, with connection string examples for both. SQL Server Express is described as sufficient for most production uses. Second, verify that your image storage paths are writable and persistent. Whether you use Docker volumes or Azure Files, the separation of /app/images and /app/images-origin is non-negotiable. If you lose the original images volume, you lose the originals. Third, test the TOTP flow. The default admin account requires a 6-digit code from an authenticator app on first sign-in. That is a good security feature, but it adds a setup step. If you deploy in an environment where you cannot scan a QR code, you will need to find a workaround. The README mentions a bypass for local development, but it is ignored outside the Development environment. So production always requires TOTP. These are concrete checks, not generic advice. Moonglade is a capable blog engine, but it demands that you respect its storage and authentication boundaries.

Editorial conclusion

Adopt Moonglade if you are a .NET developer who wants a full-featured blog with scheduled posts, Webmention, and a clean admin UI, and you are comfortable with Azure storage or Docker volumes. Skip it if you need a zero-config hosted service, if you serve users in mainland China (the README explicitly forbids it), or if you dislike GPL-3.0's copyleft terms. Before committing, verify your database choice (SQL Server or PostgreSQL), confirm that your image storage path is writable and persistent, and check the release branch, not master, for stable code. The project is actively maintained with recent releases, but its Azure-centric defaults mean you must plan storage separation from day one.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Community notes

Community notes