selfhost.directory

The index of self-hostable projects — ranked, categorized and served with live updates.
Own your data and stop giving it to big tech.

EmDash logo

Self-host EmDash

@emdash-cms/plugin-test@0.2.10

TypeScript Astro CMS with sandboxed plugins and MCP

ReleaseOpen SourceMIT
docker run -p 4321:4321 -v emdash-data:/app/data my-emdash-site
Updated10 hours ago
LanguageTypescript
Setup difficultyModerate
Stars
14 026
Port
4321 (Default HTTP port of the built Astro/EmDash Node.js server (HOST/PORT env vars in the docs' Dockerfile))
EmDash post editor, light theme, with the floating formatting toolbar and draft changes ready to publishEmDash post editor, dark variant, showing draft changes ready to publish

What is EmDash?

Should you self-host EmDash?

EmDash scores 67/100 on selfhost.directory (how scoring works) and ranks #1 of 18 in Content Management. It has 14 026 GitHub stars.

It shipped 10 stable releases in the past year. The latest, @emdash-cms/plugin-test@0.2.10, came out on 18 October 2026.

Setup is moderate: expect some configuration beyond a single command. It is MIT-licensed and written mostly in Typescript.

Its closest alternative on selfhost.directory is Ghost (83/100, 55 626 stars). It scores higher overall, so compare the two before you pick. See EmDash vs Ghost side by side.

Features

  • Built-In MCP Server
  • CLI for site and content management
  • Drafts, revisions and scheduled publishing
  • Full-Text Search
  • Inline front-end editing
  • Media Library
  • Navigation menus and widgets
  • Plugin registry
  • REST API
  • Rich text editor (Portable Text)
  • Role-based Access Control
  • Sandboxed plugin system
  • SEO tooling
  • Visual content-type builder
  • WordPress content import

EmDash README

How to install EmDash

Install EmDash with Docker

Environment variables
HOST, PORT, EMDASH_SITE_URL, EMDASH_ENCRYPTION_KEY
docker build -t my-emdash-site .
docker run -p 4321:4321 -v emdash-data:/app/data my-emdash-site

The docs provide a multi-stage Dockerfile (node:22-alpine) that builds your own EmDash site image; no published EmDash image.

How to configure EmDash

  • adminAdmin branding and language limits: logo, siteName, footerLabel, favicon, locales.

    Admin branding and language limits: logo, siteName, footerLabel, favicon, locales.

    values: object

  • allowedOriginsExtra browser origins accepted by passkey verification for multi-hostname deployments; merged with EMDASH_ALLOWED_ORIGINS.

    Extra browser origins accepted by passkey verification for multi-hostname deployments; merged with EMDASH_ALLOWED_ORIGINS.

    values: array of https origins (same host or subdomain of siteUrl)

  • authAuthentication adapter that replaces built-in passkey login (for example Cloudflare Access).

    Authentication adapter that replaces built-in passkey login (for example Cloudflare Access).

    values: access({ teamDomain, audience ... })

  • authProvidersPluggable login providers offered alongside passkeys.

    Pluggable login providers offered alongside passkeys.

    values: github(), google(), microsoft(), atproto()

  • DATABASE_PATHPath to SQLite database

    Path to SQLite database

  • EMDASH_ALLOWED_ORIGINSComma-separated list of additional origins accepted by passkey verification (multi-subdomain deployments).

    Comma-separated list of additional origins accepted by passkey verification (multi-subdomain deployments).

  • EMDASH_AUTH_SECRETLegacy. Used as the IP-salt source if set; existing installs should keep this to preserve stable commenter-IP hashes across upgrade.

    Legacy. Used as the IP-salt source if set; existing installs should keep this to preserve stable commenter-IP hashes across upgrade.

  • EMDASH_DATABASE_URLOverride database URL

    Override database URL

  • EMDASH_ENCRYPTION_KEYKey for encrypting plugin secrets at rest. Operator-provided — never stored in the database.

    Key for encrypting plugin secrets at rest. Operator-provided — never stored in the database.

  • EMDASH_IP_SALTOptional override for the commenter-IP hash salt. When unset, a stable per-site value is generated and stored in the database.

    Optional override for the commenter-IP hash salt. When unset, a stable per-site value is generated and stored in the database.

  • EMDASH_MIGRATIONS_MODEOverrides the effective runtime migration mode set in config.

    Overrides the effective runtime migration mode set in config.

  • EMDASH_PREVIEW_SECRETOptional override for preview HMAC secret. When unset, a stable per-site value is generated and stored in the database.

    Optional override for preview HMAC secret. When unset, a stable per-site value is generated and stored in the database.

  • EMDASH_SITE_URLPublic browser-facing origin (falls back to SITE_URL)

    Public browser-facing origin (falls back to SITE_URL)

  • EMDASH_TURNSTILE_SECRET_KEYCloudflare Turnstile secret key (falls back to TURNSTILE_SECRET_KEY). When set, comment submissions must include a valid Turnstile token — pair it with the turnstileSiteKey prop on <CommentForm>.

    Cloudflare Turnstile secret key (falls back to TURNSTILE_SECRET_KEY). When set, comment submissions must include a valid Turnstile token — pair it with the turnstileSiteKey prop on <CommentForm>.

  • emdash typesFetches schema from a running local or remote instance and writes TypeScript types (default .emdash/types.ts).

    Fetches schema from a running local or remote instance and writes TypeScript types (default .emdash/types.ts).

    values: CLI command

  • EMDASH_URLRemote EmDash URL for schema sync

    Remote EmDash URL for schema sync

  • HOSTServer host

    Server host

  • imagestrueWraps Astro's image endpoint so stored media can be optimized from the configured storage adapter.

    Wraps Astro's image endpoint so stored media can be optimized from the configured storage adapter.

    values: true | false

  • maxUploadSize52428800Maximum media upload size in bytes for direct and signed-URL uploads (50 MB).

    Maximum media upload size in bytes for direct and signed-URL uploads (50 MB).

    values: positive integer bytes

  • mcptrueTurns the Model Context Protocol endpoint at /_emdash/api/mcp off when set to false.

    Turns the Model Context Protocol endpoint at /_emdash/api/mcp off when set to false.

    values: true | false

  • mediaProvidersExtra media services shown in the media library, such as Cloudflare Images or Cloudflare Stream.

    Extra media services shown in the media library, such as Cloudflare Images or Cloudflare Stream.

    values: array of provider descriptors

  • migrationsautoControls runtime handling of EmDash's internal database migrations (auto checks and applies pending ones, check returns 503 when pending, manual does nothing at runtime).

    Controls runtime handling of EmDash's internal database migrations (auto checks and applies pending ones, check returns 503 when pending, manual does nothing at runtime).

    values: { runtime: "auto" | "check" | "manual", dev }

  • objectCacheKey/value cache adapter for content and configuration reads; disabled when omitted.

    Key/value cache adapter for content and configuration reads; disabled when omitted.

    values: kvCache() (Cloudflare KV) or memoryCache() (Node)

  • PORTServer port

    Server port

  • registryhttps://registry.emdashcms.comPlugin registry aggregator and policy; false disables registry discovery and installs.

    Plugin registry aggregator and policy; false disables registry discovery and installs.

    values: false | URL string | { aggregatorUrl, acceptLabelers, policy }

  • S3_ACCESS_KEY_IDS3 access key

    S3 access key

  • S3_BUCKETS3 bucket name

    S3 bucket name

  • S3_ENDPOINTS3 endpoint URL

    S3 endpoint URL

  • S3_PUBLIC_URLPublic URL for media

    Public URL for media

  • S3_REGIONS3 region

    S3 region

  • S3_SECRET_ACCESS_KEYS3 secret key

    S3 secret key

  • sandboxRunnerModule specifier for the factory that starts isolated plugin runtimes; required for sandboxed and marketplace plugins.

    Module specifier for the factory that starts isolated plugin runtimes; required for sandboxed and marketplace plugins.

    values: sandbox() (Cloudflare) or the workerd runner on Node.js

  • siteUrlPublic browser-facing origin for the site; read from astro.config.mjs, otherwise EMDASH_SITE_URL then SITE_URL.

    Public browser-facing origin for the site; read from astro.config.mjs, otherwise EMDASH_SITE_URL then SITE_URL.

    values: http/https origin URL

  • toolbarserverHow the editor toolbar on public pages is delivered.

    How the editor toolbar on public pages is delivered.

    values: server | client | false

  • trustedProxyHeadersHeaders to trust for client-IP resolution behind a reverse proxy you control, used by auth rate limits and public comments.

    Headers to trust for client-IP resolution behind a reverse proxy you control, used by auth rate limits and public comments.

    values: e.g. ["x-real-ip", "fly-client-ip", "x-forwarded-for"]

  • updateChecktrueAdmin update banner; polls the public npm registry at most once a day. Accepts { minimumReleaseAge }.

    Admin update banner; polls the public npm registry at most once a day. Accepts { minimumReleaseAge }.

    values: true | false | { minimumReleaseAge: "48h" | seconds }

EmDash releases & version history

FAQ

Where can I ask questions?

Join the Discord, open an issue on GitHub, or read the docs, which cover setup, architecture and the API reference.

How do I get started?

Open the playground for an instant sandbox, or run npm create emdash to scaffold a new project locally ready for deployment.

Do I need to use AI to use EmDash?

No. Agents are optional; EmDash ships an MCP server exposing content, schema, media, taxonomies, menus and revisions that works with Claude, OpenCode or any MCP-aware tool.

What is EmDash?

An open-source CMS built on Astro and Cloudflare that gives you a typed schema, an admin panel and an MCP server, so people and AI agents can both manage content.

More in Website Builder

Rate this project

Click to rate — no account needed.

0 comments

Posting as guest
Loading comments…