Self-hosted comments that run on one Cloudflare Worker.

No container, no VPS, no database server, nothing to patch. Two credentials and you're live — every comment in your own D1 database, served from your own domain, behind two lines of HTML.

TypeScript · Cloudflare Workers + D1 + KV · ~17 KB gzipped embed

Why Garrul instead of Disqus, Commento, or Giscus

No vendor lock-in

Disqus, Commento, and friends own your comments. Garrul stores every thread in your own Cloudflare D1 database. Run npm run db:export whenever you like — no exit tax, no scraping your own comments back out.

Privacy by default

IP addresses are hashed with HMAC-SHA-256 before they touch storage. No third-party trackers, no ad networks, no surprise PII collection. What you log is what you see.

Predictable cost

One Cloudflare Worker per site. No per-comment billing, no per-pageview surcharges. Small blogs typically sit entirely inside Cloudflare's free tiers.

Talk to us

This is a live Garrul instance — the same code you'd deploy, running on one Worker. Post something and see for yourself. Yes, really.

What a self-hosted comment system needs to do

Reading & writing

  • Threaded markdown

    Nested replies with full markdown, reactions, edit/delete windows, and collapsible threads with configurable pagination. Readers sort by top, newest, or oldest — and you set which order a thread opens in.

  • OAuth + anonymous

    Sign in with GitHub, Google, Discord, Facebook, or X — or post anonymously with Turnstile and rate-limiting.

  • Votes + reader reports

    Upvotes and downvotes, each independently switchable, plus one-tap reporting with per-network dedup. Report counts stay operator-only.

  • Feeds + permalinks

    RSS feeds for any thread or the whole site, plus comment counts and stable permalinks you can link straight to.

  • Accessible + international

    The widget ships translations and full right-to-left support, real keyboard shortcuts, and relative timestamps in the reader's own timezone.

Notifications

  • Reply notifications by email

    A reader ticks a box in the composer — or presses the 🔔 to follow a thread without commenting at all — confirms by double opt-in, and gets a debounced digest of new comments. Plain email; there is no webhook anywhere in that path. Needs a Resend API key.

  • Moderator email

    A digest of whatever is waiting for approval or has been reported, sent to your admin addresses. Off by default — an upgrade shouldn't start sending mail unasked — and toggled from /admin/settings without a deploy.

  • Webhooks

    A signed POST on every comment event, for anything custom. Pipe it into Discord, Slack, or your own service.

  • Telegram

    Moderate from your phone: approve, delete, and ban inline. Query the queue and stats with slash commands, plus an optional daily digest.

  • One-press unsubscribe

    Digests carry RFC 8058 headers, so Gmail and Apple Mail show their own Unsubscribe button — no page to visit. The link in the footer also lists every other thread that address follows, with an unsubscribe-from-all.

Moderation & safety

  • Admin moderation

    Built-in queue, user management, banning, and bulk actions — no extra service.

  • Layered anti-spam

    Turnstile, rate limiting, markdown sanitizing, and a honeypot are always on, and one setting extends the Turnstile challenge to signed-in commenters when someone starts scripting throwaway accounts. Akismet or Workers AI optionally stack on top — flagged comments queue, never silently dropped.

  • Muted words

    One term per line, matched against the comment body, the author's name, and the page URL — whole words by default, with * if you want it. Maintained from /admin/settings, because this is the one filter you tune by reading your own queue.

  • Reply from the panel

    Answer a comment straight from the queue or its own page, with a markdown preview before you post. Saved replies prefill the box for the answers you'd otherwise type fifty times, and Save for reuse turns anything you've just written into one.

Deploy & operate

  • Shadow-DOM embed

    One ~17 KB gzipped script, under a 30 KB ceiling enforced in CI. The widget cannot leak styles into your page or inherit them — a class of bug the plain script-tag competitors ship with. Iframe fallback if you'd rather.

  • Two requests to mount

    A thread loads on two API calls — one /bootstrap for the config, your session, and the first page of comments, plus a form token the anti-spam timing check needs to be per-reader. Down from four, or six with reactions and votes on.

  • Drop-in guides

    Worked examples for Astro, Hugo, Jekyll, WordPress, and plain HTML — with iframe and lazy-load variants.

  • Disqus import

    Point your Disqus XML export at the importer — from the admin UI or the CLI — and bring your existing threads with you.

  • Themeable, on contract

    21 --garrul-* variables under a semver stability guarantee, plus shipped minimal, soft, and contrast presets — so it looks intentional before you write a line of CSS.

  • Runtime feature flags

    Toggle comments, reactions, votes, and page-level engagement from the admin settings page. A saved setting beats the env var — no redeploy.

  • Workers analytics

    First-class metrics via Cloudflare Workers Analytics Engine. Tail logs in real time.

  • Free-tier usage HUD

    The admin dashboard charts today's Workers, D1, and KV usage against the free-tier ceilings, so you see a bill coming before it arrives.

  • Hashed IPs

    HMAC-SHA-256 with a server-side secret. Rate limit without storing raw addresses.

How Garrul works on Cloudflare

Edge runtime. Garrul is a single Cloudflare Worker. Requests are served from the nearest Cloudflare data center — usually under 50 ms — without a long-running server to keep alive.

Your data, your account. Comments live in a D1 SQLite database; sessions and rate-limit counters live in KV. Both belong to you, billed to you, exportable by you.

Your domain. Bind the Worker to a custom hostname (e.g. comments.yourblog.com). The embed talks to your domain, not a third party — no CORS surprises, no tracker blocklists.

How to install Garrul in 20 minutes

Twenty minutes once. After that, one dry-runnable command per release.

npm run upgrade detects config drift, prints the release notes, and shows you the plan before it changes anything. There is no server to patch between those runs, because there is no server.

Once Garrul is deployed to your Cloudflare account, embedding comments on any page is two lines of HTML:

<div id="garrul" data-slug="my-post"></div>
<script async src="https://comments.example.com/embed.js"></script>

The script is roughly 17 KB gzipped, loads asynchronously, and renders inside a Shadow DOM so your page styles stay isolated. Pick one of the shipped minimal, soft, or contrast presets, or override the theme variables to match your blog — either way, without a custom build.

Try it without a domain

npm run setup walks you through it and puts a working instance on a *.workers.dev URL in a few minutes. Point a custom domain at it later, once you've decided to keep it. DNS is not a prerequisite for trying this.

What you actually have to sign up for

Two credentials: a Turnstile site key and secret key. That is the entire required list — everything else is optional or generated for you. OAuth providers, a Resend API key for email notifications, Akismet, a Telegram bot, and a custom domain are all things you add when you want them, not hurdles between you and a working comment thread.

The full path — Worker, D1, KV, and the optional extras — takes about twenty minutes end to end. The INSTALL guide on GitHub walks through every step with copy-paste commands.

What self-hosted comments cost on Cloudflare

Free until you're big — and then it's Cloudflare's prices, not ours. There is no Garrul tier, no per-seat count, and nothing to upgrade to.

  • Workers: 100,000 requests/day free. A page view spends two of them to mount a thread, so that ceiling is roughly 50,000 views a day before you pay anything — and the lazy-load snippet in the docs drops it to zero for readers who never scroll that far. Beyond the free tier, the Workers Paid plan is $5/month and includes 10 million requests, then $0.30 per additional million.
  • D1: 5 GB storage and 5 million row-reads/day free.
  • KV: 100,000 reads/day and 1,000 writes/day free.
  • Turnstile: Free, with unlimited challenges. The free plan caps you at 20 widgets per account, which one comment section will not trouble.

If you start paying, it's because you're getting a lot of traffic — and the bill is still pennies per thousand comments.

Checked 1 August 2026 against Cloudflare's own docs: Workers, D1, KV, and Turnstile. Cloudflare revises these limits from time to time — check the source before you budget on them.

Frequently asked questions about self-hosting comments

How is this different from Disqus?

Disqus is a SaaS — your comments live on their servers, alongside their ad network and trackers. Garrul is something you deploy yourself. The comments live in your Cloudflare account, the embed loads from your domain, and there are no third-party scripts to block.

Is it GDPR-friendly?

Garrul does not log raw IP addresses (they're HMAC-hashed before storage), does not set ad-tracking cookies, and ships no advertising or analytics trackers in the embed. You remain the data controller, so GDPR obligations are yours, but Garrul gives you the levers — full export, full delete — to honour user requests. This site's own privacy policy is a worked example you can crib from.

What do reply notifications actually send?

Plain email, not a webhook. A reader ticks a box in the composer, or presses the 🔔 in the thread toolbar to follow a thread without commenting at all. They confirm by double opt-in — nothing is ever delivered to an unconfirmed address — and from then on receive a debounced digest rather than a message per comment. A reader signed in with a provider-verified address is confirmed on the spot, since the provider already proved they control the inbox. It needs a Resend API key and nothing else.

Worth being precise about the scope: subscriptions are post-scoped. You get every new comment on a thread you subscribed to, not only the direct replies to your own comment. Per-parent-author scoping is on the backlog, not shipped. Moderators are separate — they can opt into their own digest of whatever is queued or reported, off by default.

Getting out takes four routes, and three of them need no account: the Unsubscribe button your mail client shows next to the sender, because digests carry RFC 8058 List-Unsubscribe headers; the link at the bottom of every digest; that link's landing page, which lists every other thread the address follows with a per-row unsubscribe and an unsubscribe-from-all; and, for a reader signed in with a verified address, a Manage subscriptions panel in the widget itself.

What gets stored about commenters?

For OAuth users: provider, provider user ID, display name, avatar URL, and the email address if the provider reports it as verified. For anonymous: display name and a hashed IP. Every comment also carries a hashed IP and the browser user-agent string for spam triage. Comment bodies are stored as markdown plus rendered HTML. Rate-limit counters live in KV with short TTLs; sign-in sessions expire after 30 days. Full detail in the privacy policy.

Can I migrate away later?

Yes. D1 is SQLite — you can dump the database to a file at any time. The schema is documented in the repository. Getting in is covered too — Garrul ships a Disqus XML importer, so you can bring your existing threads with you.

Who maintains Garrul?

One person: KingPin. Garrul started as a replacement for Disqus on my own site — the ads were embarrassing, and every alternative wanted a server I didn't want to run. It has been developed in the open on GitHub ever since. Issues and pull requests get read; you can also reach me at [email protected]. If it's useful to you, GitHub Sponsors and Ko-fi keep it going.

What can't Garrul do?

The thread doesn't update itself — there are no live updates, so a reader sees new comments on refresh rather than as they land. There are no image uploads, no @mentions, and no search within a comment thread.

One Worker serves one site; running several sites means several deploys, and proper multi-tenancy is a v2 question rather than a setting. The widget your readers see is translated and handles right-to-left layouts, but the admin UI and the Telegram bot are English-only by design.

None of these are secret — they're just easier to hear now than after you've deployed.

Is it production-ready?

It has been in steady development since the first tagged release in May 2026, with new releases landing most weeks — the release history is the honest record of the pace. The deployment path is documented end-to-end (Workers, D1, KV, OAuth, Turnstile, custom domain), and the live comment section further up this page runs the same code you would deploy. Two things worth being straight about: it is maintained by one person, and it is young. Production use is encouraged; please open issues for anything that bites.

Do I need Cloudflare? Can I run Garrul on Docker or a VPS?

You need Cloudflare. Garrul isn't a Node app that happens to deploy to Workers — it's built on Workers directly, with comments in D1, sessions and rate limits in KV, and wrangler as the deploy tool. There's no Docker image and no VPS install path, and adding one would mean rewriting the storage layer. If you specifically want comments on a box you own outright, Commento, Isso, and Remark42 are the honest recommendations. If Cloudflare is fine by you, that same constraint is what keeps the bill near zero and the ops work near nil.