Self-hosting a CMP
Most cookie consent tools are SaaS: you drop in a script tag, and consent records, banner config, and traffic data all live on the vendor's servers — see how Cookiebot worksfor a concrete example of that shape. Self-hosting flips that — the banner, the storage, and the audit trail all run on infrastructure you control. Here's what that actually involves.
Why teams self-host
- No monthly fee, no pageview caps. Most hosted CMPs price by domain or monthly pageviews; a self-hosted, open-source CMP has no such meter running.
- Consent data never leaves your infrastructure.No third party sees who consented to what, which matters if you're already minimising vendor data flows for compliance reasons.
- No cross-origin script dependency.A vendor-hosted banner script is a runtime dependency on someone else's uptime. Self-hosted code ships with your build.
- Full control over storage and retention. You choose the database, the retention window, and who can query the audit log.
The four pieces you actually need
- A banner/widget— the frontend piece that shows the notice, records the visitor's choice, and exposes it to your own scripts and tag managers.
- Storage — somewhere the consent decision persists beyond a single page load. SQLite is enough for most sites; move to PostgreSQL, MySQL, or MongoDB once you need multi-instance writes or heavier reporting.
- An append-only audit log— a record that's written once and never edited or deleted, which is what makes a consent trail defensible under GDPR-style evidentiary requirements.
- An admin surface — somewhere to edit banner copy, review consent records, and manage who on your team can do that (RBAC), without touching the database by hand.
Example — a self-hosted stack in two installs
Consenti is one option built specifically to cover all four pieces as a Node.js Consent Management Platform, with zero external runtime dependencies on either side:
npm install @consenti/ui @consenti/apiimport { createConsenti } from '@consenti/api'
import express from 'express'
const app = express()
const consenti = createConsenti({
storage: { driver: 'sqlite' }, // swap to postgres/mysql/mongo later, same API
})
app.use('/consenti', consenti.middleware) // REST API + admin dashboard, mounted directly
app.listen(3000)The admin dashboard, the REST API, and the audit log all run inside that one process — no separate service to deploy, no vendor console to log into.
Deployment shape
- Single server / container. The common case — the backend runs alongside your app, SQLite file on local disk, no extra infra.
- Multiple instances behind a load balancer. Switch storage to Postgres, MySQL, or MongoDB so every instance reads/writes the same consent table.
- Frontend-only.If you don't need server-side records at all, the widget half can run standalone against
localStorage/cookies with no backend — useful for static sites or a first pass before wiring up storage.
When self-hosting is the wrong call
Self-hosting isn't free of cost, it just moves the cost from a subscription to engineering time — someone has to run migrations, monitor the database, and keep the package updated. If you have no backend team, need built-in IAB vendor-list management for thousands of ad-tech partners, or want a vendor to hold liability for the compliance mapping, a hosted CMP is often the better trade, at least to start.