Sentry self-hosted RAM: 16 GB on paper, 32 GB in practice.
The getsentry/self-hosted README lists 16 GB as the minimum. That number is accurate in the same way that a car’s “city MPG” is accurate: technically measured, practically optimistic. Operators who run Sentry self-hosted in production report a different floor. This guide walks through the service-by-service breakdown, what the issue tracker actually says, what fails first when memory is tight, and what hardware budget operators end up at.
20 seconds. Sentry self-hosted documents a 16 GB RAM minimum. Operators on the getsentry/self-hosted issue tracker report 24–32 GB to maintain stability under real ingest. ClickHouse alone claims 8–12 GB on a loaded deployment. Kafka plus ZooKeeper baseline around 2–4 GB before any events arrive.
60 seconds. The 16 GB figure reflects an idle, container-limit-tuned state on a best-case workload. Twenty-odd containers each carry a memory floor. ClickHouse is the dominant consumer. Kafka and ZooKeeper, running on the JVM, are the first to show instability when the host gets tight. Snuba and Symbolicator each add several hundred megabytes to over a gigabyte. The web, worker, and cron processes add another gigabyte or two in aggregate.
When the host runs out of headroom, Kafka consumers stall first. Events stop processing while the web UI stays up, which makes the failure mode misleading. The OOM killer eventually reaches one of the JVM processes, the container restarts, and the queue digs itself out — until the next pressure spike.
The 16 GB number, and where it comes from
The getsentry/self-hosted README has stated a 4 GB minimum for years; it was revised upward to 16 GB as the stack grew. The number is real in the sense that the install script will complete and the stack will start on a 16 GB host. It is not the number you want if you plan to ingest events.
The 16 GB figure appears to reflect a specific configuration: container memory limits tuned below service defaults, a single-node Kafka setup with reduced JVM heap, a lightly loaded ClickHouse that has not yet materialized any significant data, and no simultaneous user query load. That combination clears 16 GB. A stock deploy with default container limits, under any meaningful ingest load, exceeds it.
The project maintainers have been transparent about the minimum being a floor rather than a comfortable operating point. Issue getsentry/self-hosted#1521 is a recurring community thread on hardware requirements that spans multiple years of reports, each moving the practical number upward.
The gap between the documented minimum and the practical one is the subject of this guide.
Where the RAM actually goes
Sentry self-hosted is not one process. The current Docker Compose stack runs roughly twenty containers. Each has a memory floor, and those floors add up. The breakdown below uses observed ranges from operator reports and the issue tracker; exact numbers vary with ingest rate, retention settings, and JVM tuning.
| Service | Typical idle (GB) | Under real load (GB) | Notes |
|---|---|---|---|
| ClickHouse | 2–4 | 8–12 | Largest single consumer. Materializes data aggressively on query. |
| Kafka | 1–2 | 2–3 | JVM-based. Heap grows with partition count and log retention. |
| ZooKeeper | 0.3–0.5 | 0.5–1 | JVM-based. Required for Kafka coordination in the current stack. |
| Postgres | 0.2–0.5 | 0.5–2 | Grows with shared_buffers and query load. Keeps metadata and user data. |
| Redis | 0.1–0.3 | 0.3–0.8 | Cache, rate limiting, celery task queue. Grows with queue depth. |
| Snuba (multiple workers) | 0.5–1 | 1–3 | Multiple workers: consumer, replacer, api. Each runs Python. |
| Symbolicator | 0.3–0.5 | 1–2 | Rust service. Spikes heavily during symbol download and native crash processing. |
| Vroom | 0.1–0.2 | 0.3–0.5 | Profiling backend. Relatively small at low profile volume. |
| Relay | 0.1–0.3 | 0.3–0.8 | Rust service. Buffers events before forwarding to Kafka. |
| web + worker + cron | 0.5–1 | 1–2 | Django processes. Worker handles async tasks; web handles HTTP. |
| Supporting services | 0.2–0.4 | 0.4–0.8 | ingest-consumer, post-process-forwarder, and a few more. |
The table rows are not independent. ClickHouse memory grows when Snuba queries it. Kafka heap grows when Snuba falls behind on consumption. Snuba falls behind when the ingest-consumer can’t write to ClickHouse fast enough. The services are coupled in ways that make the memory footprint non-linear with ingest rate.
Adding the idle column: roughly 5–9 GB at a quiet idle. Adding the load column: 15–26 GB under a real deployment. The 16 GB minimum is somewhere in between those states, attainable with careful JVM tuning and low ingest, not the comfortable operating floor.
What fails first when memory is tight
The failure sequence is consistent enough across operator reports that it is worth naming directly.
Kafka and ZooKeeper stall
Both run on the JVM. JVM processes respond to memory pressure by increasing GC pause duration before they fail outright. Under a constrained host, Kafka brokers start spending a measurable fraction of their time in GC rather than serving produce and consume requests. Consumers fall behind. The event queue in Redis or Relay backs up.
The web UI stays up. The issue list loads. Events stop appearing. This is the failure mode that confuses operators the most: everything looks healthy from the outside while ingest has silently stopped.
Issue getsentry/self-hosted#1719 tracks Kafka and ZooKeeper baseline memory on a fresh install, and the comments document the failure pattern in detail. Issue getsentry/self-hosted#3467 covers the OOM behavior with default container limits.
ClickHouse crashes on query
ClickHouse manages its own memory allocation and will attempt to use as much as the host allows. When another container reaches its memory limit and the OOM killer fires, ClickHouse can be mid-query and lose state. The recovery is automatic but slow: ClickHouse restarts, warms its buffer pools, and the Snuba consumers that read from it wait or retry.
A ClickHouse restart during query load typically adds 30–90 seconds of unavailability for the parts of the UI that depend on it. That includes the Issues list, Discover, and performance dashboards.
Symbolicator runs out of headroom during symbolication
Symbolicator downloads debug information files from external sources (or from Sentry’s internal object storage) and holds them in memory during symbolication. A minidump from an application with a large binary or multiple shared libraries can spike Symbolicator’s resident memory by several hundred megabytes in a few seconds.
On a host with no free memory budget, that spike triggers the OOM killer. The symbolication job fails. The event is stored without a stack trace. The user sees a blank stack or a “failed to symbolicate” message.
Redis queue grows until eviction
Redis holds the Celery task queue. When workers fall behind because other services are under pressure, tasks queue in Redis. If Redis hits its memory limit, it starts evicting. Evicted tasks are lost. The result is silent drop of background processing: source map processing, alert evaluation, and post-processing enrichment.
Issue getsentry/self-hosted#3566 documents memory exhaustion under non-trivial ingest on documented-minimum hardware. The thread includes operator measurements of per-container usage that closely match the table above.
The 24–32 GB number, and what the issue tracker says
The practical floor that experienced operators settle on is 24–32 GB. This is not a rumor; it is a pattern visible across multiple independent threads on the getsentry/self-hosted issue tracker.
A selection of the data points:
- Issue #3566: operator on a 16 GB host reports Kafka and ClickHouse together consuming 14 GB at idle after a week of real ingest, leaving no headroom for burst. Moved to 32 GB.
- Issue #3467: multiple operators reporting OOM kills on 16 GB hosts with default container limits. Maintainer comment suggests raising memory limits per container and planning for 24 GB minimum.
- Issue #1521: the long-running hardware thread. Replies from 2022 through 2025 document the steady upward drift of reported minimum requirements. Early replies suggest 8 GB; recent replies suggest 24–32 GB.
- Issue #1719: Kafka and ZooKeeper baseline analysis. JVM heap configured at 512 MB each by default in older releases; current defaults are higher.
- Issue #2262: ClickHouse and Kafka disk usage growth over time. The thread also surfaces the memory growth pattern as ClickHouse materializes more data.
The consistent shape: operators who run the stack for more than a few days with real ingest outgrow 16 GB. Those who tune aggressively can hold 20 GB. Those who want stability without babysitting settle at 32 GB.
Sentry does a good job on the hosted product. The self-hosted distribution reflects the same architecture, which carries real resource requirements. This is not a knock on the project; it is the honest cost of what the architecture does.
The hidden costs: swap, OOM-kill loops, Kafka backpressure
Three failure patterns are worth naming separately because they are not obvious from the memory table alone.
Swap
Most VPS and cloud instances ship with swap disabled or with a small swap partition. Running the Sentry stack with swap enabled on a 16 GB host sounds like it buys headroom. It does not, in any useful sense.
The JVM does not behave well when pages are swapped out. GC pauses become unpredictable. Kafka and ZooKeeper, both JVM-based, can take minutes to recover from a single swapping event. ClickHouse, which manages its own memory, will see similar degradation when its buffer cache gets paged.
Swap on a constrained Sentry host is a slow-motion failure mode rather than a safety net. The stack does not degrade gracefully; it stutters until something restarts.
OOM-kill loops
When the Linux OOM killer fires on a JVM process, the JVM exits without a clean shutdown. The container restart policy brings it back. The restarted service runs GC on startup, which spikes memory briefly. If the host is still tight, the OOM killer fires again. The result is a restart loop that keeps the service marginally available without it ever fully recovering.
This loop is particularly problematic for Kafka because partition leadership re-election and consumer group rebalancing happen on every restart. Each restart costs seconds to minutes of ingest unavailability. A host that is 2 GB short of what the stack needs can produce continuous partial unavailability rather than a single clean failure.
Kafka backpressure into Relay
Relay buffers events before writing to Kafka. When Kafka consumers are slow because of memory pressure elsewhere, the Kafka topic lag grows. Relay holds events in memory while the lag persists. On a host that is already tight, Relay’s buffer growth adds to the pressure. The cycle feeds itself: Kafka is slow because the host is tight; the host is tighter because Relay is buffering; Relay buffers more because Kafka is slow.
The exit from this loop is either more memory on the host or a reduction in ingest rate. Rate limiting at the DSN level is the recommended mitigation, but it requires knowing the right rate cap in advance.
A reproducible measurement plan
If you want to measure your own deployment rather than rely on reported numbers, the tooling is straightforward.
# Check per-container RSS while the stack is running
docker stats --no-stream --format \
"table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}"
Run this at three points: immediately after stack startup, after an hour of real ingest, and after a day of real ingest. The growth from startup to day-one is typically 30–60%. ClickHouse grows the most; Kafka and Snuba grow proportionally to ingest volume.
# Check host-level available memory (not container limits)
free -h
The number that matters is “available”, not “free”. Available includes page cache that the kernel will reclaim under pressure. If available drops below 2 GB on a 16 GB host during ingest, you are in the failure-prone zone.
# Watch OOM events in the kernel log
dmesg -T | grep -i "oom\|killed process" | tail -20
Any output here during normal operation is a signal that the host is too small for the workload. The process names in the output will tell you which service got killed first.
The recommended measurement cadence: run these three commands once per day for the first week after deploy. The trend tells you where the practical floor is for your ingest volume.
What this means for sizing decisions
The hardware budget for a stable Sentry self-hosted deployment, based on operator reports, breaks down like this:
- Proof of concept, no real ingest: 16 GB is sufficient to get the stack running and explore the UI.
- Light production, under 100 events per second: 20–24 GB with JVM heap tuning. Expect occasional OOM events during burst ingest.
- Stable production, 100–500 events per second: 32 GB with default container settings. This is the floor that operator reports consistently identify as stable without babysitting.
- High volume, above 500 events per second: 32 GB plus dedicated Kafka and ClickHouse nodes. Single-host Sentry self-host above this range is not a supported configuration.
The monthly cost of a 32 GB host from a cloud provider runs $80–200 depending on region and provider. Add Postgres backup storage, object storage for source maps and debug files, and the ops time to maintain the stack, and the total cost of ownership for Sentry self-hosted in production lands meaningfully above the hardware sticker price.
None of that disqualifies Sentry self-hosted for teams that need its product surface. Session replay, deep profiling, enterprise IdP integration, and HA across multiple nodes are capabilities that justify the infrastructure cost for the right workload. The point is that the cost is real, and the 16 GB minimum understates it.
urgentry on the same axis
urgentry sits at the other end of this spectrum by design.
It runs as a single Go binary. SQLite is the default database; Postgres is optional for deployments that need it. Under steady ingest at 400 events per second, the measured resident memory is roughly 52 MB. Peaks during SQLite WAL compaction reach around 68 MB. A 1 GB host is sufficient; a 2–4 GB box gives comfortable headroom for queries and source map processing.
The tradeoff is product surface. urgentry covers 218 of 218 documented Sentry API operations. Errors, OTLP traces, OTLP logs, JavaScript source maps, ProGuard, native crash ingest, releases, alerts, the issue lifecycle, and a Discover-style query language all work. Session replay is present with a partial playback experience. Deep profiling product surfaces are metadata-only today. HA across multiple regions is not the architecture.
For teams where the Sentry self-hosted RAM budget is the binding constraint — a small SaaS, an internal tool, a side project, an early-stage company where a $200/month observability server is not the right allocation — urgentry is the alternative that fits the same SDK surface on a 1–4 GB box. The DSN swap is one configuration line per service. Nothing else changes.
For teams that genuinely need session replay as a centerpiece debugging workflow, or deep profiling with flamegraphs, or multi-region HA, Sentry self-hosted is the correct answer and the 32 GB budget is the honest cost. Pick the one that fits your workload, not the one with the smaller RAM number on the README.
Frequently asked questions
What are the real RAM requirements for Sentry self-hosted?
The README states 16 GB. Operators on the getsentry/self-hosted issue tracker consistently report needing 24–32 GB for a stable production deployment under any meaningful ingest load. The 16 GB number reflects a lightly loaded idle state with tuned container limits, not a comfortable production margin.
Which service uses the most RAM in Sentry self-hosted?
ClickHouse. On a real workload it claims 8–12 GB on its own. Kafka plus ZooKeeper together account for another 2–4 GB at baseline, before any events arrive. Both are JVM-based and grow as partition count and log retention increase.
What fails first when Sentry self-hosted runs out of memory?
Kafka and ZooKeeper are the most sensitive to memory pressure and the first to produce visible symptoms. When JVM heap is throttled by OOM conditions, Kafka consumers stall, Snuba falls behind, and event processing queues back up. The web UI often stays up while events stop ingesting — which makes the failure mode confusing to diagnose.
Can you run Sentry self-hosted on 8 GB of RAM?
The install script will complete and the stack will start. Under any real ingest load, you will hit OOM conditions on one or more containers. The failures are not immediate; they emerge over hours as buffers fill and JVM GC pressure builds. Most operators who try this report the same shape of failure.
Is there a lower-RAM alternative that keeps my Sentry SDK code?
urgentry is a Go single-binary Sentry-compatible tracker covering 218 of 218 documented Sentry API operations. It runs at roughly 52 MB resident memory under 400 events per second with SQLite as the database. A 1–4 GB box is sufficient. The migration is one DSN configuration line per service.
Sources
- getsentry/self-hosted — the official self-hosted distribution, including the current README minimum hardware specification and Docker Compose stack.
- getsentry/self-hosted#3566 — operator-reported memory exhaustion under non-trivial ingest on 16 GB hardware.
- getsentry/self-hosted#3467 — OOM behavior with default container limits and maintainer guidance on memory allocation.
- getsentry/self-hosted#1719 — Kafka and ZooKeeper JVM baseline memory on a fresh install.
- getsentry/self-hosted#1521 — the long-running minimum hardware thread, with operator reports from 2022 through 2025.
- getsentry/self-hosted#2262 — ClickHouse and Kafka disk and memory growth over time.
52 MB instead of 32 GB.
urgentry runs as one Go binary against SQLite. The DSN swap is one configuration line per service. Source maps, ProGuard, OTLP traces, and native crash ingest all work on the same call paths your SDK already uses.