Enter your email — we'll send a reset link to the garrison.
Remember it?
Two-factor
Enter your code.
Open your authenticator app and enter the current 6-digit code.
How-to · Analytics·18 min read · 40 min hands-on
You installed one script to watch your readers. It watches back, and it reports elsewhere.
Analytics is the last third-party tag most people keep, and it is the one that sees everything: every page, every reader, every hour, on a system you do not own and cannot audit. Six steps to replace it with a counter of your own — Umami, Plausible Community Edition, Matomo or nothing but the server log — running on a Nordic box rented without an identity document. Cookieless by default, no banner to argue about, no transfer of your readership to a third country. From $3.90 a month. Tested on Debian 12.
The six steps
01
Choose
Four tools, one honest table
02
Provision
One subdomain, three ports
03
Deploy
Compose, Caddy, first login
04
Instrument
One tag, and the first-party path
05
Comply
Two laws, not one
06
Keep
Retention, backups, the forgotten log
Before you start · What the tag is
It is not a counter. It is a reporting line.
Counting visitors is genuinely useful and there is nothing sinister about wanting to. You need to know whether the rewrite helped, which article people actually finish, which referrer sends readers rather than bounces, and whether last night's deploy quietly broke the checkout on one browser. Every one of those questions is answered by a table of page views, and every one of them is a legitimate reason to measure.
What is worth looking at squarely is the arrangement almost everybody accepted to get those answers. The standard analytics tag is a piece of code, written by an advertising company, that you place on every page of your site and instruct to run in your reader's browser. It reads the page address, the referrer, the screen, the language, the device; it sets or reads an identifier on the device; it sends all of that to a system you cannot inspect, in a country whose rules you did not choose; and it hands the resulting file to a company whose revenue comes from knowing what people do. The site owner gets a dashboard. The dashboard is the smaller half of the transaction.
The bill arrives in four instalments, and only one of them is obvious. There is the consent banner, which exists almost entirely because of this one tag, which costs you a measurable share of visitors at the door, and which you then pay a second vendor to manage. There is the transfer question, because your readers' data leaves for a third country and somebody eventually has to write down why that is lawful. There is the data itself, which is your readership, in a database you cannot query except through the reports you are offered. And there is the dependency: the product is retired, renamed or restructured on somebody else's schedule, and the last time that happened the old measurements went with it.
Self-hosting removes all four at once, and it is not a big job. The software is mature, free and boring; the box is a few dollars a month; the work is an evening. What you get back is a table you own, on hardware in a jurisdiction you picked, with no third party to disclose and — for most configurations — no banner to argue about. Four ways to know how many people read your site, and what each one costs:
Property
The big free analytics tag
A privacy-first analytics SaaS
The same software, your VPS
Who holds the readership table
An advertising company
A vendor, under contract
You, on disk
Consent banner needed
Yes, in practice always
Usually not
Usually not — see step 05
Data leaves the EEA
Yes, by design
Depends on the vendor
Only if you choose a box outside it
Third parties to disclose
At least one, plus its subprocessors
One processor
None
Blocked by filter lists
Heavily
By hostname, often
Rarely, once it is first-party
Raw data available to you
No — reports only
Through an API, while you pay
It is your database
Monthly cost at 100k views
Nothing in money
Tens of dollars, rising with traffic
$3.90–$7.90, flat
Pick it when
You need the ad-platform integration and accept the rest
You want the ethics without running anything
The readership is yours and so is the table
The third column is a real answer, not a straw man: a hosted privacy-first vendor removes most of the same problems for people who should not be running servers, and paying one is a better outcome than keeping the ad tag. This guide is for the case where you already rent a box, or were going to.
Step 01 · Choose
Four tools. The database decides the bill.
These four are not four flavours of the same thing. They differ in what they store the data in, and that single choice sets the memory footprint, the disk growth, the upgrade risk and the depth of the reports. Read the storage column first and the feature column second; it is the order in which the decision actually gets made.
Tool
Stack and storage
Sensible tier
Pick it for
Umami
Node application + PostgreSQL or MySQL. Two containers.
Sentinel · $3.90
The default answer. Light, legible, cookieless, multi-site, MIT-licensed.
Plausible Community Edition
Elixir application + PostgreSQL + ClickHouse. Four containers.
Garrison · $7.90
Goals, funnels and public dashboards; fast queries at high volume.
Matomo
PHP application + MariaDB, plus a scheduled archiving job.
Garrison · $7.90
Feature parity with the incumbent — segments, cohorts, e-commerce, log import.
GoAccess
One binary reading the web server access log. No database.
Whatever you already run
No JavaScript at all — static sites, documentation, APIs.
Umami is the one to reach for unless you have a reason not to. It is a Node application over a single relational database, it is content with a gigabyte of memory, the schema is a handful of tables you can query yourself, and the reports cover what most people open a dashboard to see: pages, referrers, countries, devices, plus custom events when you want them. It is cookieless by default and it does not ask you to configure that.
Plausible Community Edition is the self-hostable release of the hosted product, and the extra piece is ClickHouse — a column store that makes aggregate queries over tens of millions of rows feel instant, and that will use as much memory as you let it. That is a fair trade above a few million monthly views and an odd one below it. Read the project's licence terms before you deploy: the community edition is meant for running your own analytics, not for reselling as a service.
Matomo is the only one of the four that genuinely replaces the incumbent feature for feature, and the honesty of that statement cuts both ways: you get segments, funnels, cohorts, e-commerce attribution, heatmaps through plugins and the ability to import your own server logs, and you also get a large PHP application whose raw tables grow quickly and whose reports are pre-computed by an archiving job you must schedule yourself. Two things earn it a look regardless: it can be configured to set no cookies, and audience-measurement configurations of it have appeared on regulators' published lists of tools exempt from consent — check the current list rather than a blog post, but it is a real advantage on paper.
GoAccess is the option nobody suggests and several people should take. It reads the access log your web server already writes and renders it as a terminal report or a single self-contained HTML page. Nothing runs in the browser, nothing is stored on the device, and there is no consent question to answer because there is no collection beyond the request itself. What you give up is everything that happens inside the page and any hope of counting people rather than requests. For a documentation site or a static blog, that is often a fair trade — and it pairs well with one of the others rather than replacing it.
The decision in one line
If you cannot name the feature you need from Plausible or Matomo, you need Umami. Deploy it in twenty minutes, run it for a month, and let the thing you find yourself unable to answer tell you whether to move. Migrating later costs you the history and nothing else, and the history you would be losing is a month.
Step 02 · Provision
One box, one name. Fix the name first.
Order the VPS and pick the bastion by where your readers are and where you want the data to sit — Stockholm, Helsinki, Oslo or Reykjavík are all inside the EEA, which is the point. Then run the first-hour hardening pass before anything else touches the box: key-only SSH, a moved port, a default-deny firewall, unattended upgrades. It is the same forty minutes for every guide in this series and it is not optional on a machine that will hold a year of somebody's readership.
Then settle the hostname, because it is baked into the tracking snippet on every page of every site you measure and changing it later means editing all of them while the old name still receives hits. Two shapes work. A dedicated subdomain of the site you are measuring — the reverse proxy on the analytics box answers for it, and it is the simplest thing that works. Or a neutral name on a domain of its own, if one instance will serve several unrelated sites and you would rather each site's source not advertise the others. Create the record and let it resolve before you go further: the proxy asks a certificate authority for a certificate on first start, and the authority checks the name from the outside.
dig +short stats.example.com # must return the VPS address before you deploy
ufw default deny incoming
ufw allow 47222/tcp # administrative SSH, moved off 22
ufw allow 80/tcp # ACME challenge and the redirect to 443
ufw allow 443/tcp # the dashboard and the collection endpoint
ufw enable && ufw status numbered
Three ports, and nothing else. The database must not be published: it lives on the internal Docker network, reachable by the application container and by nobody on the internet. This is the single most common way a self-hosted analytics box ends up in a scan result — a PostgreSQL or MySQL port bound to the public address because a Compose file from a tutorial had a ports mapping in it that was there for local development.
On sizing, resist the urge to over-buy. A Sentinel — 2 vCPU, 4 GB, 120 GB NVMe, $3.90 a month — runs Umami plus Postgres with room to spare, and will hold years of events for a site doing six figures of monthly views. Step up to a Garrison — 4 vCPU, 8 GB, 240 GB — if you chose Plausible, because ClickHouse is happier there, or Matomo, because the archiving job wants the cores. Bandwidth is unlimited on every tier and analytics traffic is a rounding error against it: each hit is a few hundred bytes.
Step 03 · Deploy
Two containers and a proxy. Then close the door.
The reference deployment below is Umami, because it is the one most readers should run and because the shape generalises: an application container, a database container it can reach and nobody else can, and a reverse proxy holding the certificate. Plausible and Matomo differ in the number of containers and the names of the variables, not in the structure — take their official Compose files and apply the same three rules.
The three rules, in order of how much they will cost you if ignored. Pin every image to an exact tag, never to a floating one: an analytics stack that silently pulls a new major version during an unrelated reboot is a schema migration you did not schedule. Generate the application secret from a real random source and keep it — it encrypts session material, and changing it later logs everybody out at best. Publish no database port: the two containers share a network and that is the whole of the access control they need.
mkdir -p /srv/analytics && cd /srv/analytics
openssl rand -base64 32 > .app_secret # keep this file; back it up with the database
openssl rand -base64 24 > .db_password
chmod 600 .app_secret .db_password
The Compose file. Two services, one private network, one named volume for the database, and not a single published port — the proxy is the only thing that talks to the outside world:
Caddy is the shortest path to a correct TLS setup here: two lines of configuration, a certificate obtained and renewed without a cron job, and modern defaults you do not have to research. If you already run nginx on the box, keep nginx — the requirement is a proxy in front, not this particular proxy. Either way the analytics application must never be the thing listening on 443 directly.
# Caddyfile — Caddy runs as a third service in the same Compose project, so
# "umami" resolves on the internal network. It is the only service that
# publishes ports (80 and 443).
stats.example.com {
encode zstd gzip
reverse_proxy umami:3000
}
Bring it up, then do the one thing that is genuinely time-critical. docker compose up -d, wait for the migrations, and open the dashboard. Every one of these tools ships either a documented default administrator account or an open first-registration screen, and the hostname became public knowledge the instant the certificate was issued, because certificate transparency logs are public and are scraped continuously. Change the default password and close registration within minutes of the first successful load, not tomorrow.
docker compose up -d
docker compose logs -f umami # wait for the schema migration to finish
docker compose ps # both services healthy, no published ports listed
ss -tlnp | grep -E ':(5432|3306)' # must print nothing at all
The five minutes that decide who owns the instance
Umami ships with a documented administrator account whose password is the same for every installation on earth. Plausible's first visitor becomes the owner unless registration is disabled in the environment. Matomo walks the first visitor through an installer that creates the superuser. In all three cases the window between "the certificate exists" and "you logged in" is a window in which somebody else can log in instead — and they do not need to guess the hostname, because it is published for them. Log in first, change the credential, then set the registration switch to closed and reload the page to confirm it took.
Step 04 · Instrument
One tag. Then decide whose domain it lives on.
Register the site in the dashboard, copy the identifier it gives you, and put one deferred script tag in the template — in the head or at the end of the body, it does not matter much, but deferred either way so it can never delay the page. This is the whole integration. There is no tag manager, no container, no data layer and no consent-mode wiring, and that absence is most of what you came for.
Load a page of the site, then look at the dashboard: the hit should be there within seconds. If it is not, the browser console and the network tab answer it in under a minute — the two failure modes are a mistyped identifier, which produces a request that is accepted and discarded, and a request that never leaves at all, which means an extension ate it. Exclude your own traffic before you start reading the numbers, or your own reloads will be your top referrer for a week.
Now the decision worth thinking about. The snippet above makes a request to a hostname that is not the site's own, and a share of your visitors run software that blocks such requests by pattern. The lists catch the well-known file names — anything ending in the vendor's own script name — and the well-known hosted domains, regardless of which server they point at. The fix has two halves: rename the script file and the collection endpoint to something that is not a tracker name, which every one of these tools supports, and serve them from the site's own origin through a path your existing proxy forwards. There is then no third-party request left to block.
# On the site's own web server — the two paths the tracker needs, first-party:
example.com {
handle /p/s.js { reverse_proxy https://stats.example.com { header_up Host stats.example.com } }
handle /p/collect { reverse_proxy https://stats.example.com { header_up Host stats.example.com } }
# ... the rest of the site
}
Two details make this work rather than half-work. The proxy must forward the visitor address in a header the analytics application is configured to trust, or every hit will be attributed to your own web server and the country report will be a single bar. And the tracker must be told where to send its data, which is a data attribute on the script tag in all three tools — renaming the file without repointing the endpoint gives you a script that loads and reports nowhere.
That is also the honest end of the measurement story. A visitor with JavaScript disabled, or with a strict list plus a first-party cosmetic rule, will not be counted, and no amount of engineering should be spent chasing them — a network-level filter is a stated preference and the polite response is the server log, which counts them without collecting anything extra. Events and single-page applications are the other half of instrumentation: every tool exposes a small function for custom events, and every one of them needs an explicit call on route change, because a framework that swaps the view without a navigation produces no page load for the script to notice.
Step 05 · Comply
Two laws, not one. Most of the internet conflates them.
The reason people cannot agree about whether analytics needs a banner is that they are arguing about two different rules as though they were one. Separate them and the answer becomes tractable. Article 5(3) of the ePrivacy Directive — transposed into national law in every Member State, and the actual source of the cookie banner — says that storing information on, or gaining access to information already stored on, a subscriber's terminal equipment requires their consent, unless it is strictly necessary to provide a service they explicitly requested. Note what it does not say: it does not say cookie, and it does not say personal data. It catches localStorage, it catches device fingerprinting, and it applies even to a value that identifies nobody.
The GDPR is the second rule and governs a different question: what you may do with personal data once you have it. It applies to your analytics whether or not you store anything on the device, because a web request delivers an IP address and the Court of Justice settled in Breyer (C-582/14) that an IP address can be personal data in the hands of a website operator. So the two rules stack: 5(3) asks did you write to the device, the GDPR asks what did you do with what arrived.
A cookieless analytics server is built to answer both narrowly. It stores nothing on the device — no cookie, no localStorage entry, no identifier of any kind — which takes 5(3) out of play, because there is no storage and no access to storage. And it takes the IP address it necessarily receives, combines it with the user agent and the site and a secret salt that is rotated every day, hashes the result, keeps the hash and discards the address. That hash lets the tool tell two visitors apart within a day and stops being useful the moment the salt rotates, which is precisely why the same person tomorrow counts as a new visitor. The processing is real, and it is about as small as processing gets.
What remains is a lawful basis for that narrow operation, and in practice operators rely on legitimate interests under Article 6(1)(f), documented with a balancing assessment that is short because the facts are simple: no profiling, no sharing, no cross-site tracking, a pseudonym that dies daily, a defined retention period. Several supervisory authorities have gone further and published criteria under which strictly limited audience measurement is exempt from consent altogether — the criteria differ by country, they are updated, and the one that matters to you is your own. This is not legal advice. It is the map of which instruments apply, so that the advice you take can be about your situation instead of about the general shape of the problem.
The question
Ad-tech analytics tag
Cookieless, self-hosted in the EEA
Writes to the visitor's device?
Yes — so Article 5(3) consent applies
No — 5(3) is not triggered
Durable identifier kept?
Months to years, across sites
A salted hash that expires daily
Third-country transfer to document?
Yes, with a transfer mechanism to maintain
None — the box is in the EEA
Processors to name and contract
The vendor and its subprocessors
Your host, for the hardware, and nobody else
Who can answer a subject access request
You, via whatever export exists
You, with SQL — and often there is nothing to return
One more line belongs in the file, because it is the quiet argument for doing any of this. The lawfulness of sending European visitors' data to a US analytics service has been litigated for a decade: the Court of Justice struck down the previous transfer framework in Schrems II (C-311/18) in 2020, several national supervisory authorities ruled against the standard analytics tag on that basis in 2022 — Austria in January, France in February, Italy in June, with others following — and a new adequacy decision, the EU-US Data Privacy Framework, restored a transfer route in July 2023 and is itself under challenge. You do not have to have a view on how that ends. A box in Helsinki removes the question from your file permanently, and that is worth more than being right about the current state of it.
Step 06 · Keep
Retention, backups, upgrades. And the log you forgot.
Set the retention period on the first day, while it is still an abstract number rather than a year of data you are attached to. Every one of these tools can delete raw events on a schedule and keep the rolled-up summaries; twelve months of detail is generous for almost any purpose, and it means the honest answer to "how long do you keep it" is a number rather than "indefinitely". It also solves the disk problem before it starts, which matters most for Matomo, whose raw tables are the ones that grow.
Back the database up somewhere that is not this machine and encrypt it on the way out — the backups guide is the full treatment, and the short version is a nightly dump piped into an append-only repository on a second box, ideally in a second jurisdiction. Back up the application secret alongside it: a database you can restore and a secret you cannot is a dashboard nobody can log in to. Then restore it once onto a scratch VPS, on purpose, before you need to.
Upgrades deserve slightly more care than an ordinary container bump, because these applications own a schema. Read the release notes for the major versions you are stepping over, take the backup first rather than after, then change the pinned tag and bring the stack up while watching the logs — a migration that fails half-way is recoverable from last night's dump and very unpleasant without it. There is no reason to be on the newest release the week it appears, and a good reason not to be.
The log that undoes the whole exercise
You have just deployed a stack that deliberately never stores an IP address. Two metres away, your reverse proxy is writing the full IP address of every single visitor into an access log, in plain text, and keeping it for as long as the default rotation says — on both the analytics box and the web server in front of your actual site. That log is a larger and more sensitive dataset than the analytics database, it is a processing operation you have to account for, and almost nobody remembers it. Decide what it is for: if it is only for debugging, truncate or mask the last octet in the log format and rotate aggressively; if you want it for log-based measurement, say so in the policy and give it a real retention period. What you must not do is leave it at the default and describe your site as not collecting IP addresses.
Finally, the limits worth being honest about, because a self-hosted counter is not a strictly better version of what you removed. You lose cross-device and cross-day identity, so returning visitor means something narrower than it did. You lose the ad-platform integration, which matters if you buy traffic and is irrelevant if you do not — search console data is a separate product and is unaffected. You take on an upgrade and a backup, which is a real recurring cost measured in evenings. And the box itself becomes something worth protecting: it holds your readership, it is on the public internet, and the hardening checklist plus a private route to the dashboard are the two things that keep it boring.
Field notes · Six traps
Six ways this goes wrong. Two of them are silent.
Trap 01 · Fatal
The default password everybody knows
One of these tools ships the same administrator credential on every installation on earth; the others hand ownership to whoever registers first. The hostname is published in certificate transparency logs the moment TLS works. Log in, change it, close registration — in the first five minutes, not tomorrow.
Trap 02 · Fatal
The database port from the tutorial
Compose files written for a laptop publish 5432 or 3306 so the author could connect from a GUI. Copy one onto a public box and the readership table is a password-guess away. The application container reaches the database over the internal network; nothing needs to be published but the proxy.
Trap 03 · Legal
Assuming self-hosted means exempt
Moving the software to your own box answers the transfer question and the processor question. It does not answer Article 5(3) if you then switch on a feature that writes to the device, and it does not write your privacy policy. The exemption is a property of the configuration, not of the hosting.
Trap 04 · Silent
Every visitor arrives from the same country
A reverse proxy that does not forward the client address makes every hit look like it came from the proxy itself. The dashboard fills up, the totals look right, and the geography and uniqueness columns are quietly fiction. Check the country breakdown on day one, not in the quarterly review.
Trap 05 · Silent
The access log you are still keeping
The stack was chosen because it never stores an IP address. The proxy in front of it stores every one of them in plain text by default, and so does the web server in front of your actual site. Mask or rotate them deliberately, and describe them in the policy — or stop claiming you do not collect them.
Trap 06 · Avoidable
Chasing the old numbers
The new totals will not match the old ones and no configuration will make them. Different counting rules, a different blocked share, and no banner filtering the sample. Spend the fortnight learning your own ratio, then treat the new figure as the baseline and stop reconciling two definitions.
FAQ · Self-hosted analytics
Questions, answered.
Eleven questions that come up while doing this — beginning with the one everybody gets wrong in both directions.
Can I really drop the cookie banner if I self-host?
Often yes, and the reason is more precise than "self-hosted is fine". Two separate rules are in play. Article 5(3) of the ePrivacy Directive — the one everyone calls the cookie law — requires consent to store information on, or read information from, a visitor's device, unless it is strictly necessary for a service they asked for. It applies to cookies, but also to localStorage, to sessionStorage and to anything else you leave behind, and it applies whether or not the data is personal. A tool that writes nothing to the device simply does not trigger it. The GDPR is the second rule and it does not go away: you still receive an IP address and a user-agent string on every request, and an IP address is personal data in the hands of a website operator (Court of Justice, Breyer, C-582/14). What a cookieless analytics server does is hash that IP with a salt that is rotated daily and then discard it, so what lands in the database is a per-day, per-site pseudonym rather than a person. That is a narrow, well-documented processing operation which most operators rest on legitimate interests, and several supervisory authorities have published criteria under which pure audience measurement is exempt from consent. It is a defensible position, not a magic word: write down what you collect, why, and for how long, and read your own regulator's guidance rather than a vendor's landing page. Nothing on this page is legal advice.
Which one should I pick if I do not want to think about it?
Umami. It is a single Node application and one PostgreSQL database, it is comfortable inside 1 GB of memory, the dashboard is legible on the first visit, it is cookieless out of the box, and one instance happily serves every site you own. For the overwhelming majority of people replacing Google Analytics on a blog, a documentation site, a shop or a company site, it is the right answer and the other three are an argument you did not need to have. Pick Plausible Community Edition instead if you want funnels, revenue goals and a stronger public-dashboard story and are willing to run ClickHouse for them. Pick Matomo if you genuinely need the Google Analytics feature set — segments, cohorts, custom reports, e-commerce attribution, raw log import — and accept that you are running a large PHP application with a scheduled archiving job. Pick GoAccess if you want no JavaScript on the page at all.
Why do the numbers not match Google Analytics?
Because they are counting different things, and the gap runs in both directions at once. Upwards: Google Analytics behind a consent banner only ever sees the visitors who pressed accept, which in Europe is routinely half of them, while a banner-free cookieless counter sees everybody. Upwards again: your new endpoint is on your own domain and is not on the filter lists that discard requests to well-known trackers. Downwards: a daily-rotating hash cannot recognise the same person tomorrow, so a reader who visits on Monday and Thursday is two visitors, not one returning visitor — sessions are stitched within a day and never across days or devices. Downwards again: most self-hosted tools ship a conservative bot filter and no Discover-style inflation. The practical advice is to stop reconciling. Run both for two weeks to learn your own ratio, write it on a sticky note, then delete the Google tag and use the new numbers as the baseline they now are. What matters for decisions is the shape of the trend and the relative ranking of pages and sources, and both survive the move intact.
Will ad blockers block my own instance too?
Partly, and it is worth knowing exactly how, because the mechanism is dumber than people assume. The large filter lists work on patterns: the hostnames of the hosted services, and a set of famous file and path names — a request for something ending in plausible.js, umami.js or matomo.php is recognisable no matter which server it goes to. Move the software to stats.example.com with the stock file names and you will still lose a slice of technical audiences. There are two honest fixes and they stack. Rename the script and the collection endpoint to something that is not a tracker name — every one of these tools lets you, and it costs one line of configuration. Serve them from the same origin as the site itself, through a path on your main domain that the reverse proxy forwards, so there is no third-party request left to block. Between them you recover most of the loss. You will not recover all of it, and you should not want to: a visitor running a strict list plus JavaScript disabled has made a choice, and the correct response to that is the server log, not an arms race.
Is proxying the script through my own domain a dark pattern?
It deserves the question, and the answer turns on what the blocker was protecting. A filter list has one blunt tool and two very different targets: the ad-tech network that follows a person across ten thousand sites and builds a profile, and the counter that tells one site owner that its documentation page was read four hundred times yesterday. Blocking the first is a person defending themselves against surveillance. Blocking the second is collateral damage from a rule that cannot tell them apart. Serving your own counter from your own domain is not smuggling a tracker past a defence; it is removing a third-party request that never should have existed, and the same visit generates a line in your access log regardless. Where it stops being defensible is the moment the thing you are proxying is not the thing described here — if you add cross-site identifiers, fingerprinting, or a pipeline to an ad platform, the blocker was right and the proxy is a lie. The test is simple and worth applying to yourself once: could you publish a page describing exactly what this endpoint collects, and would a reader shrug? If yes, proxy it. If not, the problem is not the proxy.
How big does the VPS actually need to be?
Smaller than you fear, with one exception. Umami is a Node process and a Postgres database: it settles under 1 GB of memory for a normal site and the database grows by roughly the size of a text line per pageview, so a Sentinel — 2 vCPU, 4 GB, 120 GB NVMe, $3.90 a month — has years of headroom. GoAccess is a rounding error. Matomo sits in the middle: PHP and MariaDB are undemanding until the archiving job runs, which is the CPU spike you have to size for, and its raw tables grow faster than the others because it keeps more per hit. The exception is Plausible Community Edition, and the reason is ClickHouse rather than Plausible: a column store is built to trade memory for query speed and will help itself to whatever you leave lying around. It runs in 4 GB if you cap its caches in the shipped configuration, and it is comfortable at 8 GB — which is why the Garrison tier at $7.90 is the sensible floor for that one. In every case the number that eventually forces an upgrade is disk, not memory, and step 06 is how you stop it.
Can one instance measure several different sites?
Yes, and it is the normal way to run these. All three of the JavaScript tools are multi-site by design: you register each domain in the dashboard, each gets its own identifier, and the same script endpoint accepts hits for all of them while keeping the reports separate. One box, one certificate, one backup, one upgrade — twenty sites. Two things to keep in mind as the list grows. Reports are separated by site, so if you administer sites for clients, use the tool's own user and team permissions rather than handing everybody the administrator account, and check what a shared instance implies before you agree to it in a contract: you become the party holding their visitors' data. And the tracking endpoint is a shared name, which means the domain that serves it appears in the source of every site on the list — a fact worth mentioning to a client whose competitors read page source, and an argument for the first-party path from step 04.
Do I even need JavaScript? What about analysing the server log?
For a large class of sites, the log is enough, and it is the most honest measurement available: it collects nothing extra, because the lines already exist. GoAccess reads a web server access log and produces a terminal report or a self-contained HTML dashboard — top pages, referrers, status codes, bandwidth, response times, user agents — for a static site, a documentation set or an API, that is most of what a script tag was going to tell you. Two limits decide whether it is enough for you. A log cannot see what happens inside the page: no scroll depth, no outbound-link clicks, no button events, no single-page-application route changes, because none of those touch the server. And a log counts requests rather than people, so you are at the mercy of caching and of a bot population that is now a majority of raw traffic — filtering is a permanent maintenance task rather than a setup step. There is also a privacy trap in the opposite direction from the one you expect, and step 06 covers it: the log you are analysing holds full IP addresses, which is more personal data than the JavaScript tool you rejected was keeping.
Can I import my Google Analytics history?
Realistically, no, and planning around that is better than fighting it. Matomo is the only one of the four with any serious import story, and even there the practical routes are importing your own web server logs and using the migration tooling for older Matomo instances rather than a clean transfer of Google's processed reports. Umami and Plausible Community Edition can accept a bulk load through their APIs if you are prepared to write the mapping, but you would be inventing sessions and visitors out of aggregated exports, and the result is a chart that looks continuous and is not comparable across the join. What actually works: export the reports you care about from Google before the property is gone — the monthly totals, the top pages, the top sources, the conversion counts — as CSV, keep them in the same place as your other records, and start the new tool as a fresh baseline on a date you write down. You will consult the old export twice in the first year and never again, and every number in it was measured under a different definition anyway.
What do I actually have to write in the privacy policy?
Less than you think, and the shortness is the point — a paragraph you can defend beats a page you copied. Say what the tool is and that you run it yourself; name the server's location and legal operator, since being able to say "on a machine we rent in Finland" is the whole benefit of doing this. Say what is recorded per visit: the page, the referrer, the country, the device and browser family, and a pseudonymous identifier derived from the IP address and user agent with a salt that changes daily, from which the original IP cannot be recovered. Say what is not recorded: no cookies, no cross-site identifier, no profile, no data sold or shared. State the lawful basis you rely on and the retention period, and make sure the number you write is the number the tool is configured with. If your reverse proxy keeps access logs, that is a separate processing operation with its own retention — mention it, because it is the one people forget. Then keep the paragraph in sync with the configuration: a policy that describes the setup you had two years ago is worse than a vague one.
Why does the host matter for a box that only holds page views?
Because "only page views" understates what an analytics database is. Aggregated over a year, it is the readership of your site: which pages, in what order, from which countries, at what hours, arriving from which links — and for a small publisher or a niche shop, that is a business plan and a source list in one table. It is also, jurisdictionally, exactly the kind of data an adversary asks a provider for rather than asking you. Two properties follow. Where the box sits decides which legal process reaches it: a machine in Stockholm, Helsinki, Oslo or Reykjavík sits inside the EEA, which removes the international-transfer question from your compliance file entirely, and inside four constitutional regimes with strong press-freedom traditions. And whose name is on the account decides what the account itself reveals: on NordBastion there is no identity document, no legal name and no card, the invoice settles in Monero or Bitcoin, and the machine holding your readership is not a row in a customer database somewhere with your passport attached to it. The jurisdiction guide covers the legal floor of each of the four in detail.
Get the box
Your readership, on a machine in the EEA that is not registered to your name.
Sentinel — 2 vCPU, 4 GB and 120 GB of NVMe for $3.90 a month, in Helsinki, Stockholm, Oslo or Reykjavík — runs Umami and its database with room for years of events. Step up to Garrison at $7.90 if you chose Plausible or Matomo. No email at signup, no identity document, no card, and no third party holding the list of everyone who has ever read your site.
Last reviewed · 2026-10-12 · Sources · Umami, Plausible Community Edition, Matomo and GoAccess documentation; ePrivacy Directive 2002/58/EC Article 5(3); Regulation (EU) 2016/679; Court of Justice judgments in Breyer (C-582/14) and Schrems II (C-311/18); EU-US Data Privacy Framework adequacy decision of July 2023 · Cadence · yearly
Part of the series
Anonymous VPS hosting in 2026 — the cluster.
This guide is one spoke of a larger series. The pillar walks the three privacy layers end to end — the sibling spokes below dive into the specifics.