Self-Hosting the MarkReply Core Engine
Deploy your own blazing-fast, SQLite-backed comment engine in minutes using Docker Compose.

MarkReply is a blazing-fast, self-hosted comment engine built in Rust. It provides a universal JavaScript widget and a fully automated SQLite backend, completely eliminating the need for heavy external dependencies.
If you prefer managing your own infrastructure over using a managed cloud, you can spin up the core engine on any VPS in minutes.
β¨ The Core Features
- Zero-Touch Database: Fully automated SQLite setup with build-time and run-time migrations.
- Threaded Conversations: Built-in support for nested replies to keep discussions organized.
- Spam Protection: Invisible honeypot fields silently trap basic bot submissions.
- Admin Auto-Approval: The site ownerβs comments bypass moderation and are instantly published.
π Customizing Caddy for SSL
To serve your widget securely, we use Caddy as a reverse proxy. While the standard Caddy image works great, adding a DNS module (like Cloudflare) allows you to automate Letβs Encrypt ACME challenges without exposing port 80, which is highly recommended for production environments.
First, create a Dockerfile.caddy to build a custom Caddy binary with the Cloudflare DNS module:
Note: We are using Cloudflare in this example, but you should swap out the GitHub URL for the specific plugin that matches your domain's DNS provider (e.g., Route53, DigitalOcean). You can find the full list of available modules on the Caddy DNS GitHub page.
FROM caddy:2-builder AS builder
RUN xcaddy build \ --with github.com/caddy-dns/cloudflare
FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddyNext, define your routing and SSL configuration in a Caddyfile:
A quick note on syntax highlighting: Our blog engine's syntax highlighter doesn't natively support Caddyfiles yet. We are using Nginx highlighting here as a fallback since the structure is similar enough to keep the code easily readable!
{ email user@email.com cert_issuer acme}
comments.example.com { encode { zstd gzip 6 }
# Proxy requests to the MarkReply container reverse_proxy markreply-engine:3000}π Docker Compose Deployment
MarkReply is designed to be entirely self-contained. The SQLite schema is automatically generated on startup.
Create a docker-compose.yml file in the same directory as your Caddyfile and Dockerfile.caddy:
To receive moderation emails and enable admin auto-approval, simply provide your SMTP credentials and admin email in the environment variables.
services: caddy: build: context: . dockerfile: Dockerfile.caddy container_name: markreply-proxy ports: - "80:80" - "443:443" environment: # Inject your Cloudflare API token for the DNS challenge - CLOUDFLARE_API_TOKEN=your_cloudflare_token volumes: - ./Caddyfile:/etc/caddy/Caddyfile - ./caddy_data:/data - ./caddy_config:/config restart: unless-stopped depends_on: - markreply-engine
markreply-engine: image: ghcr.io/rajasekhar-gundala/markreply:latest container_name: markreply-engine environment: - DATABASE_URL=sqlite://data/comments.db?mode=rwc - ADMIN_TOKEN=your-super-secret-admin-key # Admin email for auto-approvals and moderation notifications - ADMIN_EMAIL=user@example.com # SMTP Configuration for outgoing emails - SMTP_HOST=smtp.yourprovider.com - SMTP_PORT=465 - SMTP_USER=your-smtp-username - SMTP_PASS=your-smtp-password volumes: - ./data:/app/data restart: unless-stoppedStart the engine by running docker compose up -d. The ./data volume ensures your SQLite database persists safely across container restarts.
π» Frontend Integration
Once your backend is running, adding comments to your site is as simple as injecting the widget. Place this snippet exactly where you want the discussion thread to appear:
Replace comments.example.com with your real domain name.
<!-- The data-slug attribute groups comments for specific pages or blog posts --><div id="markreply-comments"></div><script src="https://comments.example.com/widget.js" async></script>