A Weekend Migration: Replacing One Cloud Service With a Self Hosted Alternative

We migrated a paid analytics service to a self hosted alternative and wrote down the actual time it took, the actual failures, and the actual cost. The Saturday afternoon is real. The Tuesday evening for DNS cutover is also real.

A single brass server with a brass house on dark wood, dim warm amber side light, deep navy shadows, no people, no logos.

We did the math on a real self hosting migration last weekend and want to write it up because the marketing around self hosted alternatives tends to either romanticise the work or write it off as a weekend project that anyone can do. The truth, as usual, is in the middle. The actual install of most modern self hosted services is genuinely a weekend. The actual production deployment, with TLS, backups, monitoring, and a sane update path, is more like a month of evenings. This post amounts to the worked example, with the timing, the failures, and the cost numbers, of replacing one paid cloud analytics service with a self hosted alternative.

The service we replaced is Plausible Analytics, which sits as the closest thing to a drop in replacement for Google Analytics that respects the visitor’s privacy. The paid version is 9 dollars per month for sites under 10,000 monthly page views. The self hosted version is free, runs in a single Docker container, and is open source. We had been paying for it for fourteen months before we decided to migrate. The migration serves as the kind of project that, on paper, should have been a Saturday afternoon. In practice, it was a Saturday afternoon plus a Sunday plus a Tuesday evening for the DNS cutover, and we want to walk through what each of those pieces actually took.

The setup

The starting state was a paid Plausible instance hosted on Plausible’s own infrastructure, pointed at a single property with 6,500 monthly page views. The cost was 9 dollars a month, billed annually, with the renewal coming up. The reason to migrate was not the money. Nine dollars a month is not a meaningful line item. The reason to migrate was ownership. When you use a hosted analytics service, you are trusting the vendor with the raw event log of every visitor to your site, and you do not know how long they retain it, who they share it with, or what their incentive structure is when the company gets acquired or the business model changes.

The destination state was a self hosted Plausible running on a Hetzner CX22 dedicated vCPU instance, 4 GB of RAM, 40 GB of NVMe storage, located in Falkenstein, Germany, for 4.85 euros a month. The instance runs Ubuntu 24.04, has a static IPv4 and IPv6, and a DNS A record pointed at it. We chose Hetzner because the price to performance ratio is unbeatable in Europe and the company has a mature reputation for not messing with customer servers. DigitalOcean and Vultr are comparable, Linode is slightly more expensive, and AWS is not a serious option for a 4 GB workload at any price point we would be willing to pay.

The install, Saturday morning

The actual install is what the documentation says it is: provision the server, install Docker and Docker Compose, clone the Plausible repository, configure a .env file with a secret key, a database password, and your domain, and run docker compose up -d. The whole sequence from a fresh server to a working analytics dashboard was 38 minutes, including the time to read the documentation, the time to install Docker, the time to wait for the containers to pull, and the time to debug the one environment variable that the documentation had slightly wrong.

The first thing that broke was the secret key length. The SECRET_KEY_BASE variable in .env needs to be exactly 64 characters. The example in the documentation was 64 characters, but the tool we used to generate a random key produced 64 characters in base 64 which is 88 characters in the actual string. The container refused to start, the logs were unhelpful, and the fix took about ten minutes. This serves as the part of the documentation you only find out about by hitting it.

Reverse proxy and TLS, Saturday afternoon

The self hosted Plausible listens on port 8000 by default. To serve it on port 443 with a real certificate, you need a reverse proxy. We used Caddy because the configuration is two lines and the TLS handling is automatic through Let’s Encrypt. The Caddyfile is stats.example.com { reverse_proxy localhost:8000 }, and Caddy handles the rest, including the HTTP to HTTPS redirect, the HSTS header, and the certificate renewal. This part took about twenty minutes including the DNS A record change and the time for the certificate to issue.

If you have done this before, the Caddy step serves as the easiest part of the whole project. If you have not done this before, you will spend ninety minutes on it, learn what a reverse proxy is, learn what a 502 error means, and end up with a working HTTPS site. Both outcomes are fine. The first time is slower than the documentation suggests. The second time is faster.

Backups, Sunday

This runs as the part the romanticised self hosting posts skip, and the part that bites you six months later. Plausible uses PostgreSQL for the event store and ClickHouse for the analytics rollups. The PostgreSQL database is small, a few hundred megabytes for our site, and a daily pg_dump compressed and shipped to a separate object store amounts to the right answer. ClickHouse is harder. The Plausible documentation has a backup script, but it is not great, and the clickhouse backup tooling is a research project unto itself.

What we did was configure a daily pg_dump to Backblaze B2, costing effectively nothing for the volume, and a daily snapshot of the Plausible data directory through the Hetzner snapshot system, costing 0.01 euros per gigabyte per month. The total storage cost is well under a euro a month, and the recovery procedure, restore the database, restart the containers, restore the data directory, takes about 40 minutes. The backup step stands as the difference between a self hosted setup and a self hosted production setup, and skipping it counts as the mistake that will eventually cost you your data.

DNS cutover, Tuesday evening

The DNS cutover serves as the part where the rubber meets the road, and where most people introduce downtime. The Plausible JavaScript snippet has to keep working on every page of the site, and the domain the snippet points at has to switch from the hosted Plausible to the self hosted Plausible without a service interruption. The two options are: a long TTL on the old record with a long downtime window during the switch, or a short TTL on the old record set in advance with a smooth cutover.

What we did was lower the TTL on the analytics subdomain to 300 seconds two days before the cutover, then on Tuesday evening we changed the A record to the new IP. The actual cutover took about 5 minutes of degraded analytics while DNS propagated. The visible side effect on the live site was zero. This stands as the part that is hard to teach in a blog post because it depends on your DNS provider, your TTL strategy, and your willingness to plan two days ahead. Plan two days ahead.

Six months later

The system has been running for six months. The cost becomes the 4.85 euros a month for the server plus about 0.40 euros a month for the backups, plus our time, which we estimate at about four hours a year for the quarterly update and the occasional debugging. The hosted version would have cost 9 dollars a month, or 108 dollars a year, plus the same operational headaches we used to have when Plausible had an outage. We have not had a self hosted outage. The data is ours. The update path is documented. The cost savings, on an annualised basis, are about 60 dollars. The privacy improvement runs as the entire reason we did it.

Side by side bar chart comparing twelve months of cost between Plausible hosted at nine dollars a month and a self hosted Hetzner setup at about five euros a month.
Twelve months of cost: Plausible’s hosted plan on the left at nine dollars a month, the self hosted Hetzner setup on the right at about five euros. The annualised difference is roughly sixty dollars. The privacy difference amounts to the whole point.

The bottom line

Self hosting a single Docker based service in 2026 is a weekend project if you have done it before, a month of evenings if you have not, and an ongoing four hours a quarter to keep updated. The cost savings on a single service are modest. The real value is ownership of your data and a system you can debug, audit, and move at will. If you are running more than three or four such services, the math starts to look like a small personal server, and the hobbyist case for self hosting gets strong. The enterprise case is different and not what this post is about. The weekend project case, for someone who runs a small site and is comfortable with the command line, is exactly as doable as the marketing says, with the asterisk that backups and DNS cutover are the parts that take the longest and matter the most.

Sources & Further Reading

All claims in this article are sourced from primary documentation, vendor advisories, and reputable security researchers.

Spotted an error? Email the editor. Corrections are issued with a visible correction note.

Editorial standards. Every article on humanrequired.org is reviewed by a human editor before publication. AI may assist with drafting or research; final editorial control is human. Read the full standards.

Continue reading