One command, one machine
The self-hosted build is the whole build: no feature gate, no community edition with the good parts removed, and no outbound call at runtime for anything, including geolocation.
Three commands, in three places
git clone https://github.com/micaforge/micaforge
cd micaforge
./install.sh --domain analytics.example.com --email you@example.comIt checks the machine, generates three secrets with openssl, writes .env at mode 600, pulls the store images, builds the server, waits until it reports healthy, and prints your dashboard address. Run it with no arguments to be asked instead, or with --no-tls on a laptop. Running it twice is safe: an existing .env is never overwritten, because the key inside it is the key every stored secret is sealed with.
<script defer
data-site="1"
data-host="https://analytics.example.com"
src="https://analytics.example.com/mf.js"></script>Under 3 KB gzipped, no dependencies, never throws into your page. The setup screen shows the real site id.
micaforge-shipper --host https://analytics.example.com \
--site 1 --key-file ingest.key /var/log/nginx/access.logThe third one is what makes the machines visible. Crawlers never run JavaScript, so the snippet above will never see one; their fetches only exist in the access log of the server that serves your site. Run the shipper beside it, or the Citation Gap has only one of its two numbers. The dashboard's setup screen prints this command with your site id and key already filled in.
What is actually running
docker compose file, no message broker and no second service to operate. The API, the tracker file and every background worker live in the same Rust process, so there is one thing to restart and one log to read.What you need
- A machine with Docker, the Compose v2 plugin and
openssl. - 4 GB of RAM and two vCPUs. That is the size the shipped configuration is tuned for. The installer warns below 1.8 GB, and it is right to: ClickHouse alone is capped at 1.4 GB, so under about 2 GB the kernel starts killing things.
- At least 5 GB of disk. The images are around 1.5 GB before a single event is stored.
- For HTTPS: a domain whose DNS already points at the machine, and port 80 reachable from the internet, because that is how the certificate is ordered.
Nothing else. No account, no licence key, no phone-home, and no outbound request at runtime, which is also why geography needs a MaxMind-compatible database on disk rather than an API call.
Where the memory goes
ClickHouse is the component that will notice a small machine first, and the shipped configuration reflects that. Postgres holds identity, sites, goals, dashboards and the job queue, all of which are small, and it is kept off the ingest path entirely: sites are resolved from a one-minute in-process cache, policy files from a five-minute one. A Postgres stall degrades the dashboard; it does not stop collection.
Accepting a hit is a parse, a classify, an enrich and a push into an in-memory queue. The writer runs one lane per table, each with its own buffer, so a pathological replay payload cannot delay a pageview and a failing insert on one table cannot stall another.
The honest limits
A single VPS is the shape of this deployment, and there are things it is not.
- There is no cluster
- No multi-node deployment, no sharded ClickHouse, no horizontal scale-out, and nothing in the compose file pretending otherwise. The API and ingest paths are stateless apart from the writer's in-memory queue, so more than one server against shared stores is architecturally plausible. It has not been built or tested, and this page is not going to describe a deployment nobody has run.
- Replay is the disk outlier
- One recorded session outweighs a month of events, which is why it has a retention clock of its own, defaulting to thirty days. Events default to keeping everything.
- No bytes-per-event figure is offered
- This project has not measured one, so quoting one would be an invention. Two weeks of your own traffic, read from the usage endpoint and the ClickHouse volume, is a better forecast than anyone else's benchmark.
- Mail is optional, and degrades rather than fails
- With no SMTP URL, invitations become links you copy by hand, password resets become links an admin hands over, and scheduled reports do not run. Nothing else changes.
- The store images are not pinned for you
- The defaults use
latest-alpine, which is convenient for a first run and is not a version. Pin them in.envbefore you depend on the install. - Backups are yours
./backup.shexists and works. Nobody is running it for you, and nobody is watching the disk.- It is pre-release
- There is no tagged release, no production install anywhere, and no benchmark. If that is not an acceptable trade for the reporting, it is the right decision to wait.
An open signup form on a reachable dashboard is how a self-hosted install acquires strangers. Create your account, then set MICAFORGE_DISABLE_REGISTRATION=true and bring the server back up.
Operating it
- Watch what it is doing
docker compose logs -f micaforge- Update to a newer build
./update.sh- Take a backup
./backup.sh- Stop it without losing anything
docker compose down, which keeps every volume.