{"id":492914,"date":"2026-08-31T09:45:22","date_gmt":"2026-08-31T09:45:22","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=492914"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2026-08-31T09:45:22","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=492914","title":{"rendered":"Time for Sentry to retire\u2026 Metric is ready to take over"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/62e\/806\/e90\/62e806e9037f2a7738e491ebfb7be719.png\" alt=\"Sentry vs. Metric\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/62e\/806\/e90\/62e806e9037f2a7738e491ebfb7be719.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/62e\/806\/e90\/62e806e9037f2a7738e491ebfb7be719.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>Sentry vs. Metric<\/figcaption><\/div>\n<\/figure>\n<p><strong>Disclaimer:<\/strong> this article is written with bias, bile, and tables. The author believes infrastructure should work for you, not the other way around. If you\u2019re a Sentry fan \u2014 read to the end; you\u2019ll have plenty to bring up in the comments. <\/p>\n<h3>Act one, in which we buy a ticket to the zoo<\/h3>\n<p>Everyone who has ever stood up a self-hosted Sentry remembers this moment. You clone <code>getsentry\/self-hosted<\/code>, run <code>.\/install.sh<\/code>, and go make coffee. You come back, and Docker is still pulling images. You open <code>docker-compose.yml<\/code>, and the fun begins: time to meet the family.<\/p>\n<p>Here is the official Compose file for release 26.7.2. It contains <strong>65 (sixty-five) service definitions<\/strong>. Sixty-five, Carl! That\u2019s not a typo and not an exaggeration for dramatic effect \u2014 it\u2019s <a href=\"https:\/\/github.com\/getsentry\/self-hosted\/blob\/26.7.2\/docker-compose.yml\" rel=\"noopener nofollow\">a file from the official repository<\/a>; open it and count for yourself. Yes, some of them are profiles and one-shot jobs. But even the \u201cproduction\u201d cast includes: Sentry web, workers (several kinds!), Relay, Snuba (API + consumers), Kafka, ZooKeeper, ClickHouse, PostgreSQL, Redis, Memcached, Symbolicator, Vroom, cron, and so on down the list until your eyes get tired.<\/p>\n<p>Sixty-five services. To catch stack traces. Words fail me.<\/p>\n<p>Let\u2019s put this in perspective. A stack trace is text. A JSON document of a few kilobytes. Humanity learned to accept JSON over HTTP around 2005. And to store that JSON and show it in a web UI, Sentry asks you to run a Kafka message broker (with ZooKeeper included, naturally), the ClickHouse columnar analytical database, a separate PostgreSQL relational database, two different caches, the Relay proxy layer, Python queue workers, a standalone Snuba service that translates internal API queries into ClickHouse\u2019s dialect, and the Vroom profiler \u2014 and that\u2019s still not the whole list.<\/p>\n<p>Now, the hardware. <a href=\"https:\/\/develop.sentry.dev\/self-hosted\/\" rel=\"noopener nofollow\">The official system requirements<\/a>:<\/p>\n<ul>\n<li>\n<p><strong>4 CPU cores<\/strong>;<\/p>\n<\/li>\n<li>\n<p><strong>16 GB RAM<\/strong> \u2014 and that\u2019s the minimum; below it the installer flat-out refuses to run;<\/p>\n<\/li>\n<li>\n<p><strong>16 GB of swap<\/strong> (yes, swap the size of your RAM \u2014 a quiet moment of self-irony);<\/p>\n<\/li>\n<li>\n<p><strong>20 GB of disk<\/strong> \u2014 to start with; after that, ClickHouse will decide for itself.<\/p>\n<\/li>\n<\/ul>\n<p>Plus a gentle footnote: \u201c32 GB recommended\u201d. Battle-scarred operators on the <code>getsentry\/self-hosted<\/code> issue tracker nod along: under real load, a comfortable life starts at 24\u201332 GB, because ClickHouse alone can bite off 8\u201312 GB without blinking.<\/p>\n<p>Thirty-two gigabytes of RAM. To watch your side project crash.<\/p>\n<p>For comparison: a machine like that could run half the microservice infrastructure of an average startup! But no \u2014 it will be keeping 65 containers warm, so that you can learn about <code>TypeError: undefined is not a function<\/code> in a timely fashion.<\/p>\n<h3>Act two, in which the author puts on a tinfoil hat<\/h3>\n<p>Now, the million-dollar question (literally \u2014 Sentry\u2019s revenue is estimated in the hundreds of millions): <strong>is this an accident?<\/strong><\/p>\n<p>Look at this beautiful mechanism:<\/p>\n<ol>\n<li>\n<p>Sentry is an excellent product. No argument there. The UX is gold-standard, the SDKs are lovely, the ecosystem is huge. You genuinely want to use it.<\/p>\n<\/li>\n<li>\n<p>The SaaS version costs money. Prices grow with event volume, and at some point the bill starts resembling the rent on a small property.<\/p>\n<\/li>\n<li>\n<p>\u201cBut we\u2019re open source!\u201d Sentry says. \u201cTake the self-hosted version!\u201d And it hands you a 65-container leviathan that demands a dedicated engineer, 16+ GB of RAM, and nerves of steel.<\/p>\n<\/li>\n<li>\n<p>After a month of operating the leviathan, the average team reaches the only rational conclusion: \u201cIt\u2019s easier to just pay for SaaS.\u201d<\/p>\n<\/li>\n<\/ol>\n<p>A heretical thought occurs: what if this homemade monster <strong>is part of the sales funnel<\/strong>? The perfect inoculation against self-hosting as a concept. \u201cSee how hard it is? Now look at our Team plan \u2014 just $26 a month, zero pain.\u201d<\/p>\n<p>Let\u2019s spice it up. Sentry hasn\u2019t been open source in the strict sense for a while: its license is the <strong>Functional Source License (FSL 1.1)<\/strong> \u2014 \u201csource available\u201d, with a ban on competing with Sentry\u2019s cloud. So you may look at the code, but building your own sensible service on top of it is legally risky. Convenient, right? A shop window made of bulletproof glass.<\/p>\n<p>And the cherry on top: the <a href=\"https:\/\/github.com\/getsentry\/self-hosted\" rel=\"noopener nofollow\">official self-hosted repository<\/a> honestly states that this build is intended for <strong>low-volume deployments and proofs of concept<\/strong>. So the company itself admits: a full-scale production deployment was never the plan. Then why, one might ask, does the \u201csmall\u201d scenario need 65 containers? A good question. A damn good question.<\/p>\n<p>Is that enough to declare a conspiracy? That\u2019s for you to decide in the comments. But admit it: if a SaaS company <em>wanted<\/em> to make self-hosting deliberately inconvenient, it could hardly have done better.<\/p>\n<h3>Act three, in which we look at the alternatives and almost rejoice<\/h3>\n<p>Don\u2019t just criticize \u2014 propose! Saying \u201cSentry is bad\u201d is easy. The question is what to offer instead. The good news: alternatives exist, and they beautifully prove this article\u2019s main thesis \u2014 <strong>ingesting errors doesn\u2019t require the infrastructure of a small nation-state<\/strong>. The bad news: each one comes with its own compromise.<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\n<\/th>\n<th>\n<p align=\"left\"><strong>Sentry self-hosted<\/strong><\/p>\n<\/th>\n<th>\n<p align=\"left\"><strong>Metric<\/strong><\/p>\n<\/th>\n<th>\n<p align=\"left\"><strong>GlitchTip<\/strong><\/p>\n<\/th>\n<th>\n<p align=\"left\"><strong>BugSink<\/strong><\/p>\n<\/th>\n<th>\n<p align=\"left\"><strong>Hawk<\/strong><\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Language\/stack<\/p>\n<\/td>\n<td>\n<p align=\"left\">Python + the whole zoo<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>Rust + MongoDB<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">Django + PostgreSQL (+Redis\/Valkey optional)<\/p>\n<\/td>\n<td>\n<p align=\"left\">Django + PostgreSQL\/SQLite<\/p>\n<\/td>\n<td>\n<p align=\"left\">Node.js microservices + MongoDB + Redis<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Containers out of the box<\/p>\n<\/td>\n<td>\n<p align=\"left\">65 definitions, ~20+ always on<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>2 (Min\/Low) \u2013 4 (Medium\/High)<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">2\u20134<\/p>\n<\/td>\n<td>\n<p align=\"left\">1\u20132<\/p>\n<\/td>\n<td>\n<p align=\"left\">dozens (workers, collectors, API, garage, notifier\u2026)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Minimum machine<\/p>\n<\/td>\n<td>\n<p align=\"left\">4 CPU \/ 16 GB RAM \/ 16 GB swap<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>1 vCPU \/ 1 GB RAM<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">~512 MB RAM<\/p>\n<\/td>\n<td>\n<p align=\"left\">from 64\u2013256 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">noticeably more than a couple of gigabytes<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Sentry SDK compatibility<\/p>\n<\/td>\n<td>\n<p align=\"left\">native<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>full: only the DSN changes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">partial ingest API<\/p>\n<\/td>\n<td>\n<p align=\"left\">partial ingest API (errors only)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>none, custom SDKs<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Errors and grouping<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Logs (structured)<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>no<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Traces \/ transactions \/ spans<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">partial<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>no<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Releases and release health<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">partial<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Cron monitoring<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>no<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Uptime monitors<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Application metrics and dashboards<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>no<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Session Replay<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong> (enabled per project)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>no<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">User Feedback<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">partial<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Source maps \/ debug files<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">partial<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes (source maps)<\/p>\n<\/td>\n<td>\n<p align=\"left\">no<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Alerts (email\/Telegram\/webhook)<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>yes<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">email\/webhook<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<td>\n<p align=\"left\">yes<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>Let\u2019s go through the worthy ones.<\/p>\n<p><strong>GlitchTip<\/strong> is a wonderful project. Django, PostgreSQL, starts on half a gigabyte, handles errors, uptime, and partially transactions. But it is precisely a \u201csimplified Sentry\u201d: no structured logs, no Session Replay, no cron monitoring, no application metrics with dashboards. The GlitchTip team itself says it honestly: we implement a subset of the Sentry API. If you only need errors \u2014 an excellent choice. If you\u2019re used to all of Sentry \u2014 you\u2019ll keep walking into walls.<\/p>\n<p><strong>BugSink<\/strong> is an even more radical minimalism: it catches errors, shows them beautifully, lives in a single container, and is even proud that it <a href=\"https:\/\/www.bugsink.com\/blog\/glitchtip-vs-sentry-vs-bugsink\/\" rel=\"noopener nofollow\">ignores everything except errors<\/a> \u2014 no performance, no replays. A respectable philosophy, but it answers the question \u201cwhat replaces my log folder\u201d, not \u201cwhat replaces Sentry\u201d.<\/p>\n<p><strong>Hawk<\/strong> comes from the CodeX team. A nice interface, an active team. But: custom SDKs (so migrating from Sentry means rewriting your integration), and an architecture in the spirit of \u201cmicroservices for microservices\u2019 sake\u201d: an API, separate collector workers per language, MongoDB, Redis, and a long Compose file. In spirit, it\u2019s the leviathan\u2019s younger brother rather than its antidote.<\/p>\n<p>See the pattern? Every alternative either <strong>cuts functionality<\/strong> (GlitchTip, BugSink), or <strong>breaks compatibility<\/strong> (Hawk), or does both. Changing your DSN and losing half of the Sentry you\u2019re used to \u2014 not much of a deal.<\/p>\n<h3>Act four, in which Metric walks onto the stage<\/h3>\n<p>And now \u2014 the reason all of this was started.<\/p>\n<p><a href=\"https:\/\/github.com\/biosshot\/metric\" rel=\"noopener nofollow\">Metric<\/a> is a Sentry-compatible monitoring system rewritten from scratch in Rust, with one obsessive idea: <strong>functionality doesn\u2019t have to cost 65 containers<\/strong>.<\/p>\n<p>Migration looks like this:<\/p>\n<pre><code class=\"javascript\">Sentry.init({ dsn: \"http:\/\/&lt;key&gt;@your-metric-host:4001\/&lt;project_id&gt;\" });<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>That\u2019s it. That\u2019s the whole migration. The official Sentry SDKs keep working as if nothing happened \u2014 because Metric speaks Sentry\u2019s native protocol: envelopes, stores, releases, sessions, check-ins, replay recordings. And compatibility is backed not by words but by release tests: real processes of <code>@sentry\/browser<\/code> and <code>@sentry\/node<\/code> 10.66.0, <code>sentry-sdk<\/code> (Python) 2.32.0, <code>sentry-java<\/code> 8.50.1, <code>Sentry<\/code> for .NET 6.7.0, <code>sentry-go<\/code> 0.48.0, <code>sentry<\/code> for Rust 0.48.5, and even <code>sentry-cli<\/code> 3.6.2\/2.58.6 for uploading source maps and debug files \u2014 all of this is run against Metric before every release, and a platform is declared supported <strong>only after its test goes green<\/strong>.<\/p>\n<p>Now, the hardware. Metric\u2019s minimal profile:<\/p>\n<blockquote>\n<p><strong>1 vCPU, 1 GB RAM, 15 GB SSD.<\/strong><\/p>\n<\/blockquote>\n<p>Not a typo. One gigabyte. That\u2019s cheaper than a coffee subscription at any VPS host on the planet. The \u201cMedium\u201d profile (4 vCPU \/ 8 GB) is already a full-blown server with Symbolicator and symbolication of native and JS stack traces. Stack composition: <strong>one Rust binary + MongoDB<\/strong> (2 containers); the higher profiles add Symbolicator with a cache cleaner (4 containers). That\u2019s all.<\/p>\n<p>Installation:<\/p>\n<pre><code class=\"bash\">curl -fsSL https:\/\/raw.githubusercontent.com\/biosshot\/metric\/v0.1.5\/deploy\/install.sh | sh<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>One command. No cloning the repository, no building, no 20-minute interactive installer. The script generates passwords itself, pulls the ready-made image, and brings the stack up.<\/p>\n<p>And now \u2014 my favorite part \u2014 performance. Metric doesn\u2019t say \u201cwe\u2019re fast\u201d. Metric <strong>commits raw benchmark results to the repository<\/strong> (<code>performance\/baselines\/<\/code>), so you can verify them and nitpick. The reference machine is an ordinary laptop Ryzen 5 5600H (6 cores) with 16 GB of RAM:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">Workload<\/p>\n<\/th>\n<th>\n<p align=\"left\">Result<\/p>\n<\/th>\n<th>\n<p align=\"left\">Latency<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Durable error ingest, 5,000\/s target<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>4,983 events\/s<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">p95 = 27.7 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Saturation probe, 20,000\/s target<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>7,307 durable events\/s<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">p95 = 296.8 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">HTTP pipeline (parse\/auth\/scrubbing)<\/p>\n<\/td>\n<td>\n<p align=\"left\">2,500 requests\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">p99 = 0.62 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Span writes to MongoDB<\/p>\n<\/td>\n<td>\n<p align=\"left\">32,639 spans\/s<\/p>\n<\/td>\n<td>\n<p align=\"left\">&#8212;<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>Four thousand nine hundred eighty-three <strong>durably written<\/strong> events per second \u2014 that is, with a confirmed write to the database, with zero loss of acknowledged events \u2014 on hardware that wouldn\u2019t even pass the self-hosted Sentry installer check. Read that sentence again. A laptop that Sentry would turn away at the door handles a stream equivalent to <strong>hundreds of millions of events per day<\/strong> (Metric\u2019s design envelope is exactly 100 million events a day; the model is laid out in ADR-0037).<\/p>\n<p>And \u2014 the key difference from all the alternatives above \u2014 Metric <strong>doesn\u2019t cut functionality<\/strong>:<\/p>\n<ul>\n<li>\n<p>errors, issue grouping, search, timelines, trends;<\/p>\n<\/li>\n<li>\n<p>structured logs, transactions, spans, trace view;<\/p>\n<\/li>\n<li>\n<p>application metrics (counters, gauges, distributions), dashboards, saved searches;<\/p>\n<\/li>\n<li>\n<p>releases, deploys, release health, sessions;<\/p>\n<\/li>\n<li>\n<p>cron monitoring and active HTTP uptime monitors;<\/p>\n<\/li>\n<li>\n<p>Session Replay, user feedback with screenshots, attachments;<\/p>\n<\/li>\n<li>\n<p>alerts to email, Telegram, and signed webhooks with retry history;<\/p>\n<\/li>\n<li>\n<p>source maps and debug files via sentry-cli, symbolication via Symbolicator;<\/p>\n<\/li>\n<li>\n<p>scrubbing\/pseudonymization of personal data <strong>before writing to storage<\/strong> \u2014 by default, not as a checkbox in the settings;<\/p>\n<\/li>\n<li>\n<p>a single query language across all entities, export to JSON\/CSV;<\/p>\n<\/li>\n<\/ul>\n<p>This is not \u201ca subset of Sentry\u201d. It\u2019s Sentry\u2019s full working cycle \u2014 ingestion, grouping, investigation, alerting, releases, monitoring \u2014 in a single binary that fits inside a 320 MB memory limit on the minimal profile.<\/p>\n<p>A separate love goes to honesty in the small things. Every profile has hard ceilings on memory, queues, and retention: the system doesn\u2019t assume infinite hardware, it <em>guarantees bounded behavior<\/em>. MongoDB gets a WiredTiger cache of exactly 256 MB, not \u201cwhatever it manages to grab\u201d. Container logs rotate after two 5 MB files. The Metric developers have clearly had their fill of infrastructure surprises \u2014 and you can really feel it.<\/p>\n<h3>Act five, in which the author takes off the tinfoil hat<\/h3>\n<p>So that the comments don\u2019t tear me apart \u2014 I\u2019ll say it myself, honestly and up front:<\/p>\n<ul>\n<li>\n<p>Metric is release <strong>0.1.5<\/strong>, an early one. No profiling, no SSO\/SCIM\/MFA, and some platforms (Apple\/Cocoa, Flutter, Android, PHP, Ruby, React Native) are not yet covered by release tests. No sharding and no HA \u2014 it\u2019s a deliberately single-node system. The authors don\u2019t hide this in fine print: there\u2019s a whole <a href=\"https:\/\/biosshot.github.io\/metric\/known-limits\" rel=\"noopener nofollow\">known limits<\/a> page.<\/p>\n<\/li>\n<li>\n<p>Sentry\u2019s 65 services are not all alive at once: there are profiles and one-shot jobs in there. But 20+ permanently running containers is still 5\u201310 times more than Metric, for the task most teams actually have.<\/p>\n<\/li>\n<li>\n<p>The conspiracy theory from act two is a theory. Perhaps Sentry simply grew historically: a Python monolith sprouted queues, then ClickHouse arrived for analytics, then Relay for ingestion, then Snuba, then\u2026 But you know what? <strong>The result is indistinguishable from a conspiracy.<\/strong> And the one paying for that result is you.<\/p>\n<\/li>\n<\/ul>\n<h3>Finale<\/h3>\n<p>The moral of this article is not \u201cSentry is bad\u201d. The moral is this:<\/p>\n<p><strong>The industry has accepted as normal that ingesting telemetry costs 16\u201332 GB of RAM and a zoo of six dozen services. That\u2019s not a norm. It\u2019s a habit \u2014 and it\u2019s a habit someone profits from handsomely.<\/strong><\/p>\n<p>GlitchTip and BugSink proved you can catch errors on a few hundred megabytes. Metric proved the next step: on one gigabyte and two containers you can have the <strong>entire<\/strong> Sentry workflow \u2014 logs, traces, metrics, releases, monitors, replays, alerts \u2014 with full SDK compatibility and performance it isn\u2019t ashamed to benchmark in a public repository.<\/p>\n<p>If your Sentry SaaS bill is bigger than the cost of a 4 GB VPS \u2014 you know what to do. If you\u2019ve been operating self-hosted Sentry for a year and consider that normal \u2014 I\u2019ll see you in the comments; bring your numbers and let\u2019s compare.<\/p>\n<p>Metric: <a href=\"https:\/\/github.com\/biosshot\/metric\" rel=\"noopener nofollow\">github.com\/biosshot\/metric<\/a> \u00b7 MIT License \u00b7 one-command install \u00b7 runs on a server Sentry would refuse to install on.<\/p>\n<p><em>A GitHub star isn\u2019t 32 GB of RAM, of course, but it makes the developers unreasonably happy.<\/em><\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1076638\/\">https:\/\/habr.com\/ru\/articles\/1076638\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sentry vs. MetricDisclaimer: this article is written with bias, bile, and tables. The author believes infrastructure should work for you, not the other way around. If you\u2019re a Sentry fan \u2014 read to the end; you\u2019ll have plenty to bring up in the comments. Act one, in which we buy a ticket to the zooEveryone who has ever stood up a self-hosted Sentry remembers this moment. You clone getsentry\/self-hosted, run .\/install.sh, and go make coffee. You come back, and Docker is still pulling images. You open docker-compose.yml, and the fun begins: time to meet the family.Here is the official Compose file for release 26.7.2. It contains 65 (sixty-five) service definitions. Sixty-five, Carl! That\u2019s not a typo and not an exaggeration for dramatic effect \u2014 it\u2019s a file from the official repository; open it and count for yourself. Yes, some of them are profiles and one-shot jobs. But even the \u201cproduction\u201d cast includes: Sentry web, workers (several kinds!), Relay, Snuba (API + consumers), Kafka, ZooKeeper, ClickHouse, PostgreSQL, Redis, Memcached, Symbolicator, Vroom, cron, and so on down the list until your eyes get tired.Sixty-five services. To catch stack traces. Words fail me.Let\u2019s put this in perspective. A stack trace is text. A JSON document of a few kilobytes. Humanity learned to accept JSON over HTTP around 2005. And to store that JSON and show it in a web UI, Sentry asks you to run a Kafka message broker (with ZooKeeper included, naturally), the ClickHouse columnar analytical database, a separate PostgreSQL relational database, two different caches, the Relay proxy layer, Python queue workers, a standalone Snuba service that translates internal API queries into ClickHouse\u2019s dialect, and the Vroom profiler \u2014 and that\u2019s still not the whole list.Now, the hardware. The official system requirements:4 CPU cores;16 GB RAM \u2014 and that\u2019s the minimum; below it the installer flat-out refuses to run;16 GB of swap (yes, swap the size of your RAM \u2014 a quiet moment of self-irony);20 GB of disk \u2014 to start with; after that, ClickHouse will decide for itself.Plus a gentle footnote: \u201c32 GB recommended\u201d. Battle-scarred operators on the getsentry\/self-hosted issue tracker nod along: under real load, a comfortable life starts at 24\u201332 GB, because ClickHouse alone can bite off 8\u201312 GB without blinking.Thirty-two gigabytes of RAM. To watch your side project crash.For comparison: a machine like that could run half the microservice infrastructure of an average startup! But no \u2014 it will be keeping 65 containers warm, so that you can learn about TypeError: undefined is not a function in a timely fashion.Act two, in which the author puts on a tinfoil hatNow, the million-dollar question (literally \u2014 Sentry\u2019s revenue is estimated in the hundreds of millions): is this an accident?Look at this beautiful mechanism:Sentry is an excellent product. No argument there. The UX is gold-standard, the SDKs are lovely, the ecosystem is huge. You genuinely want to use it.The SaaS version costs money. Prices grow with event volume, and at some point the bill starts resembling the rent on a small property.\u201cBut we\u2019re open source!\u201d Sentry says. \u201cTake the self-hosted version!\u201d And it hands you a 65-container leviathan that demands a dedicated engineer, 16+ GB of RAM, and nerves of steel.After a month of operating the leviathan, the average team reaches the only rational conclusion: \u201cIt\u2019s easier to just pay for SaaS.\u201dA heretical thought occurs: what if this homemade monster is part of the sales funnel? The perfect inoculation against self-hosting as a concept. \u201cSee how hard it is? Now look at our Team plan \u2014 just $26 a month, zero pain.\u201dLet\u2019s spice it up. Sentry hasn\u2019t been open source in the strict sense for a while: its license is the Functional Source License (FSL 1.1) \u2014 \u201csource available\u201d, with a ban on competing with Sentry\u2019s cloud. So you may look at the code, but building your own sensible service on top of it is legally risky. Convenient, right? A shop window made of bulletproof glass.And the cherry on top: the official self-hosted repository honestly states that this build is intended for low-volume deployments and proofs of concept. So the company itself admits: a full-scale production deployment was never the plan. Then why, one might ask, does the \u201csmall\u201d scenario need 65 containers? A good question. A damn good question.Is that enough to declare a conspiracy? That\u2019s for you to decide in the comments. But admit it: if a SaaS company wanted to make self-hosting deliberately inconvenient, it could hardly have done better.Act three, in which we look at the alternatives and almost rejoiceDon\u2019t just criticize \u2014 propose! Saying \u201cSentry is bad\u201d is easy. The question is what to offer instead. The good news: alternatives exist, and they beautifully prove this article\u2019s main thesis \u2014 ingesting errors doesn\u2019t require the infrastructure of a small nation-state. The bad news: each one comes with its own compromise.Sentry self-hostedMetricGlitchTipBugSinkHawkLanguage\/stackPython + the whole zooRust + MongoDBDjango + PostgreSQL (+Redis\/Valkey optional)Django + PostgreSQL\/SQLiteNode.js microservices + MongoDB + RedisContainers out of the box65 definitions, ~20+ always on2 (Min\/Low) \u2013 4 (Medium\/High)2\u201341\u20132dozens (workers, collectors, API, garage, notifier\u2026)Minimum machine4 CPU \/ 16 GB RAM \/ 16 GB swap1 vCPU \/ 1 GB RAM~512 MB RAMfrom 64\u2013256 MBnoticeably more than a couple of gigabytesSentry SDK compatibilitynativefull: only the DSN changespartial ingest APIpartial ingest API (errors only)none, custom SDKsErrors and groupingyesyesyesyesyesLogs (structured)yesyesnononoTraces \/ transactions \/ spansyesyespartialnonoReleases and release healthyesyespartialnonoCron monitoringyesyesnononoUptime monitorsyesyesyesnonoApplication metrics and dashboardsyesyesnononoSession Replayyesyes (enabled per project)nononoUser FeedbackyesyespartialnonoSource maps \/ debug filesyesyespartialyes (source maps)noAlerts (email\/Telegram\/webhook)yesyesemail\/webhookyesyesLet\u2019s go through the worthy ones.GlitchTip is a wonderful project. Django, PostgreSQL, starts on half a gigabyte, handles errors, uptime, and partially transactions. But it is precisely a \u201csimplified Sentry\u201d: no structured logs, no Session Replay, no cron monitoring, no application metrics with dashboards. The GlitchTip team itself says it honestly: we implement a subset of the Sentry API. If you only need errors \u2014 an excellent choice. If you\u2019re used to all of Sentry \u2014 you\u2019ll keep walking into walls.BugSink is an even more radical minimalism: it catches errors, shows them beautifully, lives in a single container, and is even proud that it ignores everything except errors \u2014 no performance, no replays. A respectable philosophy, but it answers the question \u201cwhat replaces my log folder\u201d, not \u201cwhat replaces Sentry\u201d.Hawk comes from the CodeX team. A nice interface, an active team. But: custom SDKs (so migrating from Sentry means rewriting your integration), and an architecture in the spirit of \u201cmicroservices for microservices\u2019 sake\u201d: an API, separate collector workers per language, MongoDB, Redis, and a long Compose file. In spirit, it\u2019s the leviathan\u2019s younger brother rather than its antidote.See the pattern? Every alternative either cuts functionality (GlitchTip, BugSink), or breaks compatibility (Hawk), or does both. Changing your DSN and losing half of the Sentry you\u2019re used to \u2014 not much of a deal.Act four, in which Metric walks onto the stageAnd now \u2014 the reason all of this was started.Metric is a Sentry-compatible monitoring system rewritten from scratch in Rust, with one obsessive idea: functionality doesn\u2019t have to cost 65 containers.Migration looks like this:Sentry.init({ dsn: &#171;http:\/\/&lt;key&gt;@your-metric-host:4001\/&lt;project_id&gt;&#187; });That\u2019s it. That\u2019s the whole migration. The official Sentry SDKs keep working as if nothing happened \u2014 because Metric speaks Sentry\u2019s native protocol: envelopes, stores, releases, sessions, check-ins, replay recordings. And compatibility is backed not by words but by release tests: real processes of @sentry\/browser and @sentry\/node 10.66.0, sentry-sdk (Python) 2.32.0, sentry-java 8.50.1, Sentry for .NET 6.7.0, sentry-go 0.48.0, sentry for Rust 0.48.5, and even sentry-cli 3.6.2\/2.58.6 for uploading source maps and debug files \u2014 all of this is run against Metric before every release, and a platform is declared supported only after its test goes green.Now, the hardware. Metric\u2019s minimal profile:1 vCPU, 1 GB RAM, 15 GB SSD.Not a typo. One gigabyte. That\u2019s cheaper than a coffee subscription at any VPS host on the planet. The \u201cMedium\u201d profile (4 vCPU \/ 8 GB) is already a full-blown server with Symbolicator and symbolication of native and JS stack traces. Stack composition: one Rust binary + MongoDB (2 containers); the higher profiles add Symbolicator with a cache cleaner (4 containers). That\u2019s all.Installation:curl -fsSL https:\/\/raw.githubusercontent.com\/biosshot\/metric\/v0.1.5\/deploy\/install.sh | shOne command. No cloning the repository, no building, no 20-minute interactive installer. The script generates passwords itself, pulls the ready-made image, and brings the stack up.And now \u2014 my favorite part \u2014 performance. Metric doesn\u2019t say \u201cwe\u2019re fast\u201d. Metric commits raw benchmark results to the repository (performance\/baselines\/), so you can verify them and nitpick. The reference machine is an ordinary laptop Ryzen 5 5600H (6 cores) with 16 GB of RAM:WorkloadResultLatencyDurable error ingest, 5,000\/s target4,983 events\/sp95 = 27.7 msSaturation probe, 20,000\/s target7,307 durable events\/sp95 = 296.8 msHTTP pipeline (parse\/auth\/scrubbing)2,500 requests\/sp99 = 0.62 msSpan writes to MongoDB32,639 spans\/s-Four thousand nine hundred eighty-three durably written events per second \u2014 that is, with a confirmed write to the database, with zero loss of acknowledged events \u2014 on hardware that wouldn\u2019t even pass the self-hosted Sentry installer check. Read that sentence again. A laptop that Sentry would turn away at the &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-492914","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/492914","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=492914"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/492914\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=492914"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=492914"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=492914"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}