Google Analytics comes with a cookie banner nobody wants to build, visitor data sitting on someone else's servers, and a reputation for being blocked by roughly every second ad blocker. Umami and Plausible are the two most common self-hosted answers, and they are built very differently: different stack, different appetite for memory, different license. We break down the real differences and deploy the lighter of the two on a VPS - docker compose, nginx, and an honest note on why self-hosting does not make you invisible to ad blockers.
Every "how do I get rid of Google Analytics" thread eventually turns into an Umami vs Plausible argument. The reason is rarely "I want prettier charts" - it's usually the cookie banner you have to bolt on for one line of gtag.js, plus the nagging feeling that data about your own visitors sits on servers owned by a company whose business model is data. Umami and Plausible are the two most common answers: both open source, both self-hosted, both drawing charts without a single tracking cookie. Past that point they stop being similar. Different stack. Different appetite for RAM. Different license, with different consequences depending on what you plan to do with the tool. We'll go through it in order and then actually deploy one of the two on a VPS.
Short version. Umami is a Node.js app backed by PostgreSQL - light, simple to run, MIT-licensed. Plausible Community Edition is Elixir/Phoenix plus ClickHouse (for events) and PostgreSQL (for everything else) - noticeably heavier on resources, licensed under AGPLv3. On a small 1-2 GB VPS, Umami is the more practical pick: fewer services, fewer things that can break at 3am. People choose Plausible for the built-in funnels, revenue tracking and public dashboards - if giving ClickHouse an extra 2+ GB doesn't hurt. Below: deploying Umami with
docker compose, nginx with HTTPS on top, and a blunt section on why self-hosted analytics doesn't automatically dodge ad blockers, contrary to what a lot of marketing copy implies.
Why bother leaving Google Analytics at all
Start with the legal angle, because it hits conversion directly. Google Analytics uses a cookie to recognize the same visitor across sessions, and under GDPR and similar laws that requires explicit consent before the cookie gets set. Hence the full-screen banner every EU visitor sees - and the one a chunk of them dismiss without reading, rejecting everything, which quietly pollutes your data before it's even collected. Umami and Plausible, in their default configuration, don't identify visitors with a persistent cookie at all: uniqueness gets computed statistically, from a daily-rotating salted hash of IP and user agent. This isn't legal advice - we're not your lawyer - but in practice that's exactly what lets most sites skip the consent banner entirely, and it's the single biggest practical reason people go looking at self-hosted analytics in the first place.
Reason two is simpler: ownership. The server is yours, the database is yours, nobody third-party gets to see your traffic in order to train an ad model on it.
Reason three is the one marketing pages love to put first - "self-hosted analytics slips past ad blockers" - and it's not quite true anymore. We'll get to that later in its own section, in detail, because glossing over it in an article about privacy would be a little rich.
Umami vs Plausible: what's actually under the hood
Umami runs on Node.js, a server-side JavaScript runtime, and stores data in PostgreSQL (as of version 3, that's the only option - MySQL support got dropped). The whole thing is basically one application process plus a database. License is MIT, about as permissive as open-source licenses get: bundle it into a commercial product, fork it, do almost anything, with no obligation to publish your changes.
Viewed from a distance, that's a small setup. Plausible Community Edition is a bigger animal. Its core runs on Elixir, a language built on the Erlang VM specifically for handling huge numbers of concurrent connections, using the Phoenix web framework on top. Event data - pageviews, clicks, conversions - lives in ClickHouse, a column-oriented database built for fast aggregation over massive analytical datasets. Account and site settings live in a separate PostgreSQL instance. That's three services instead of two, and a stricter license: AGPLv3, a copyleft license that requires you to publish source code for your modifications if you make a changed version of the software available to others over a network - no binary distribution required, just network access. For running it yourself, on your own server, none of that changes anything. For an agency planning to resell a modified Plausible as a white-labeled analytics service, it changes quite a bit, and reading the actual license text before building a business on it is worth the twenty minutes.
Umami vs Plausible: a table
Aspect | Umami | Plausible Community Edition |
|---|---|---|
Stack | Node.js + PostgreSQL | Elixir/Phoenix + ClickHouse + PostgreSQL |
Services in the compose file | 2 (app + database) | 3 (app + ClickHouse + PostgreSQL) |
License | MIT | AGPLv3 |
Official minimum RAM | not stated officially; a modest VPS handles it fine in practice | ≥2 GB - official recommendation, just for ClickHouse at idle |
Custom events | yes | yes |
Funnels and revenue tracking | present in recent versions, less mature (verify current state) | more developed, generally seen as more polished |
Public dashboard sharing | yes | yes |
Built-in HTTPS without an external proxy | no, run nginx/Caddy yourself | yes, since 2.1.2 - issues its own Let's Encrypt cert on ports 80/443 |
Self-hosting difficulty | lower - one process, one database | higher - three services, ClickHouse has its own operational quirks |
How much VPS it actually takes
Two separate questions here: what the docs claim, and what shows up in practice. Plausible states its number plainly in the README - at least 2 GB of RAM for ClickHouse and Plausible together, and that's a floor for an idle instance, not a promise under load. ClickHouse is the heavy part of the stack for a structural reason: column-oriented databases tend to reserve memory for buffers and indexes ahead of time, not just for the data itself. Add a city-level geolocation database (MaxMind's GeoLite2-City instead of the country-level one) and independent estimates put another gigabyte on top of that.
Umami is the simpler story - and the official docs don't publish a number precisely because there isn't much to publish: one Node.js process plus an ordinary Postgres instance, both comfortable on resources nobody would call generous. We're deliberately not throwing out exact megabytes here - it's too easy to quote a number nobody actually measured on a WeaselCloud box, and that's flagged in the "to verify" checklist on this article for a reason. The practical takeaway holds regardless: on a 1-2 GB VPS that already runs a site, adding Umami and adding Plausible-with-ClickHouse are two very different bets. One you probably won't notice. The other is worth checking with free -h first (see the sizing article linked below for how to read that output).
So we're deploying Umami below. Not because Plausible is worse - it has real strengths in analytics depth - but because on a VPS that costs a couple of dollars a month, an extra heavyweight service like ClickHouse often doesn't pay for itself.
Deploying Umami with docker compose
Install Docker from its own official repository rather than Ubuntu's - the version there is current, and docker compose ships as a proper plugin instead of the older hyphenated command.
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
$(. /etc/os-release && echo "$VERSION_CODENAME")fills in your Ubuntu codename automatically - no manual edit needed.docker-compose-pluginis the moderndocker compose(two words). The old hyphenateddocker-composeis a separate, deprecated tool you don't need here.
Check the install worked: docker compose version should print something like Docker Compose version v2.x.x. If you get docker: 'compose' is not a docker command instead, the plugin didn't land - rerun the last install line.
Next, two random strings the compose file needs. One protects application sessions, the other encrypts two-factor auth data even if you never turn 2FA on - Umami treats it as a required variable regardless.
openssl rand -base64 32
openssl rand -hex 32
Each command prints one random string - keep both, they become APP_SECRET and TWO_FACTOR_ENCRYPTION_KEY below, in that order.
Create a working directory and the compose file itself:
sudo mkdir -p /opt/umami
cd /opt/umami
sudo nano docker-compose.yml
Contents - the minimal official two-service layout:
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: umami
restart: always
ports:
- "127.0.0.1:3000:3000"
environment:
DATABASE_URL: postgresql://umami:replace-this-password@db:5432/umami
APP_SECRET: paste-the-first-openssl-string-here
TWO_FACTOR_ENCRYPTION_KEY: paste-the-second-openssl-string-here
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat || exit 1"]
interval: 5s
timeout: 5s
retries: 5
db:
image: postgres:15-alpine
container_name: umami-db
restart: always
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: replace-this-password
volumes:
- umami-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami -d umami"]
interval: 5s
timeout: 5s
retries: 5
volumes:
umami-db-data:
127.0.0.1:3000:3000publishes the port on the server's own loopback address only - it doesn't exist on the outside at all. That's deliberate: nginx will face the internet, not the container itself, and we'll get to why that matters.replace-this-passwordin both places - insideDATABASE_URLand asPOSTGRES_PASSWORD- needs to be the same string, chosen by you. It's a private password between two containers and never leaves the Docker network.APP_SECRETandTWO_FACTOR_ENCRYPTION_KEYtake the two stringsopensslprinted above. Skip them and Umami either refuses to start, or generates its own values silently - and then migrating to a new server later means losing access to anything encrypted with a key you never saw.umami-db-datais a named Docker volume - all the real Postgres data lives here. A plaindocker compose down(no-vflag) leaves it untouched.
Bring both services up and check their status:
cd /opt/umami
sudo docker compose up -d
sudo docker compose ps
Both services should show running in the STATUS column, not stuck cycling through restarting. The first boot takes maybe ten seconds - Umami needs to create tables in the empty database before it answers its first request.
Confirm the app actually responded, no browser needed:
curl -s http://127.0.0.1:3000/api/heartbeat
A live instance returns a short JSON response with a 200 status. If curl hangs or errors out instead, check the logs: sudo docker compose logs -n 50 umami.
First login and the tracking snippet
Port 3000 is bound to localhost only, and there's no domain or HTTPS yet - so the first login happens over an SSH tunnel, without exposing anything to the internet ahead of schedule.
ssh -L 3000:127.0.0.1:3000 root@YOUR_IP
While that session stays open, http://127.0.0.1:3000 in your local browser is really a port on the server. Default credentials are documented, not secret: username admin, password umami. Change that password in your profile settings the moment you're in, before you touch the nginx step below - not after.
Add your site from the dashboard next (whatever domain it's known by), and Umami generates a snippet along these lines:
<script defer src="https://umami.example.com/script.js" data-website-id="your-site-id"></script>
Drop that line before the closing </head> on every page you want tracked - it works on essentially any framework that lets you inject arbitrary HTML into the head.
nginx and HTTPS in front of Umami
Exposing port 3000 directly would be a bad idea for two separate reasons: without TLS, the admin login travels over the network in plain text, and ufw doesn't filter ports Docker publishes on its own anyway - the 127.0.0.1:3000:3000 binding above already made that second problem moot, since the port simply doesn't exist externally. Baseline firewall rules for everything else on the box are covered in the ufw on a VPS guide.
The nginx server block is a plain reverse proxy, no Upgrade/Connection headers required - those matter for tools with live WebSocket UI updates, and Umami is simpler than that:
server {
listen 80;
server_name umami.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Validate the config and reload rather than restart - reload doesn't drop connections already in flight:
sudo nginx -t
sudo systemctl reload nginx
The certificate comes as a separate step once HTTP already answers on the domain, typically via certbot:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d umami.example.com
A successful certbot run rewrites the server block with an HTTPS redirect and wires up the cert automatically. If the site still won't load afterward and the server itself is fine, the nginx errors article covers 404/502/504/413 one at a time.
Ad blockers: the self-hosted myth, corrected
Worth stopping here for a second. The line "self-hosted analytics dodges ad blockers because it's not Google" used to be true. Five years ago, roughly. Not quite anymore.
Filter lists like EasyPrivacy - the backbone of a chunk of uBlock Origin's rules and similar blockers - stopped matching specific Google domains a long time ago and started matching patterns instead. Both Umami and Plausible have ended up in those pattern lists, specifically by hostname shape. Name your subdomain umami.example.com, analytics.example.com, or anything containing an obvious word like track, and a real slice of visitors running an ad blocker simply won't show up in your numbers. Not all of them. But a real slice, not zero.
What actually helps, and what doesn't. Self-hosting by itself buys you nothing here - what matters is the hostname and the script filename. Pick something neutral for the analytics subdomain: not umami., not stats., not analytics., just an arbitrary technical-sounding label with no obvious meaning. Umami also ships an environment variable specifically for this, letting you rename the script file itself away from the default script.js - documented under its own "bypass ad blockers" page.
None of this is shady, and it's not hiding tracking from anyone who cares to look - you're simply not matching a pattern list written for advertising trackers, a category self-hosted privacy analytics tools don't technically belong to. But promising readers "install Umami and ad blockers will never touch you again" would be false, and an article about privacy tools is a strange place to start bending the truth.
Which one to actually pick
One site, or a handful of small ones, on a 1-2 GB VPS: go with Umami. Fewer services means fewer things that can fail overnight, and MIT gives you room to fold the tool into something else of yours later without asking permission.
Need built-in conversion funnels, order revenue tracking, and public dashboards that look polished without extra work, and an extra gigabyte or two for ClickHouse isn't a problem? That's Plausible's case to make, and it makes it honestly - more analytics out of the box, paid for with heavier infrastructure underneath.
For a typical VPS in the WeaselCloud price range, we'd default to Umami. Not because Plausible is inferior - it isn't - but because for the vast majority of sites with traffic small enough to even consider self-hosting the analytics, the extra depth doesn't earn back the cost of a third service sitting in your compose file, burning RAM around the clock for no other reason than existing.
FAQ
What is the difference between Umami and Plausible?
Stack and weight, mostly. Umami runs Node.js plus PostgreSQL, two services, MIT license. Plausible Community Edition runs Elixir/Phoenix with ClickHouse and PostgreSQL together, three services, AGPLv3 license, and a noticeably heavier memory footprint because of ClickHouse.
Can I replace Google Analytics with a self-hosted tool?
Yes, and legally it's often simpler, not harder: Umami and Plausible don't set a tracking cookie by default, which in many jurisdictions removes the main reason GDPR requires a consent banner for Google Analytics in the first place. That's not a substitute for legal advice on your specific site, but it is the actual reason most people go looking for an alternative to begin with.
How much VPS RAM does self-hosted analytics need?
Depends which tool. Plausible's official recommendation is at least 2 GB of RAM for the ClickHouse-plus-app combination, and that's a floor for idle, not for load. Umami needs a lot less - the project doesn't publish an official number, but structurally it's one Node.js process next to an ordinary Postgres instance, and that combination sits comfortably alongside a small site on the same cheap VPS.
Does self-hosted analytics bypass ad blockers?
Not reliably. Filter lists such as EasyPrivacy match both Umami and Plausible by hostname pattern - a subdomain named something like umami. or analytics. still gets a chunk of ad-blocker traffic filtered out before it ever reaches your dashboard. A neutral subdomain name, and for Umami a renamed script file, reduce the risk. They don't eliminate it permanently, because filter lists keep getting updated.
What license do Umami and Plausible use?
Umami is MIT, about as permissive as mainstream open-source licenses go - you can do almost anything with it, including bundling it into a commercial product, with no obligation to release your own code. Plausible Community Edition is AGPLv3, a copyleft license: modify it and offer the modified version to others over a network, and you're required to publish the source of your changes.
Do I still need a cookie banner with Umami or Plausible?
In their default setup, neither tool sets a cookie to identify visitors - uniqueness gets computed statistically instead, without a persistent identifier - which often removes the need for a GDPR consent banner. The precise answer depends on your jurisdiction and whatever else you've configured on top, so treat this as a strong starting point, not a substitute for checking current GDPR guidance for your specific site.
Takeaways
- Umami: Node.js + PostgreSQL, two services, MIT license, easier to run on a small VPS.
- Plausible Community Edition: Elixir/Phoenix + ClickHouse + PostgreSQL, three services, AGPLv3, officially recommends at least 2 GB of RAM just for ClickHouse at idle.
- Neither tool sets an identifying cookie by default, which often removes the need for a GDPR consent banner.
- On a 1-2 GB VPS, Umami is the more practical default: fewer services, fewer failure points. Plausible earns its keep through built-in funnels, revenue tracking, and more developed analytics out of the box.
- Umami deploys as a two-service docker compose stack, port bound to loopback only, nginx and certbot handling HTTPS in front.
- Self-hosting doesn't guarantee dodging ad blockers - filter lists match hostname patterns like
umami.oranalytics., and a neutral subdomain reduces but doesn't remove that risk. - The license difference is practical, not just theoretical: Plausible's AGPLv3 requires publishing changes when you offer a modified version as a network service; Umami's MIT does not.
Where to go next
Before deploying anything, it's worth sizing the VPS first - the how much CPU and RAM do you need article covers estimating that by workload type and checking it on a running server. The general path for picking a server in the first place is in the how to choose a VPS pillar. Baseline firewall rules before opening ports 80 and 443 live in the ufw on a VPS guide.
