SSD Nodes Learn 🎉 VPS from $4.99/mo
Guides Matt ConnorBy Matt Connor

Self-hosted error tracking: Sentry alternatives

Sentry self-hosted wants 16 GB RAM. GlitchTip runs in 512 MB. Compare RAM, disk growth, container count and upgrade pain before you pick one.

What self-hosted error tracking costs before it stores one event

Self-hosted error tracking has one number that decides the whole choice, and it is the RAM floor. Sentry's own self-hosted documentation asks for 4 CPU cores, 16 GB of RAM plus 16 GB of swap, and 20 GB of free disk, before your application sends a single event. GlitchTip documents 512 MB. All of the options here accept events from the same Sentry SDKs, so this is not a decision about how you instrument your code. It is a decision about how large a server you are willing to pay for and keep alive.

Published resource figures, side by side

These are the numbers each project publishes about itself, as of August 2026. They are not the same kind of measurement, so read the note on each row before you compare them.

ChartPublished RAM figures per error tracking stack, August 2026
The data behind this chart
[
  {
    "tool": "Sentry self-hosted",
    "published_ram_gb": 16,
    "notes": "documented minimum, plus 16 GB of swap"
  },
  {
    "tool": "Bugsink",
    "published_ram_gb": 4,
    "notes": "the vendor's own published benchmark box, 2 vCPU"
  },
  {
    "tool": "GlitchTip",
    "published_ram_gb": 0.5,
    "notes": "documented recommendation, 256 MB stated as the minimum"
  }
]

Sentry's 16 GB is a documented minimum, and the same page recommends 32 GB. GlitchTip's 0.5 GB is a recommendation, and the project states 256 MB as a working minimum, or 128 MB plus swap with careful configuration. Bugsink's 4 GB is neither of those: it is the box the vendor used for its own throughput benchmark. A published figure is a starting point, not a promise about your event volume.

Sentry self-hosted: the whole product, and the whole bill

The official stack is getsentry/self-hosted, a Docker Compose project that runs the same components Sentry runs in production. Its own documentation describes it as "feature-complete and packaged up for low-volume deployments and proofs-of-concept". That sentence is the honest summary. You get every feature, and you get every moving part that makes those features work.

Install from a tagged release rather than from master:

VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/getsentry/self-hosted/releases/latest)
VERSION=${VERSION##*/}
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted
git checkout ${VERSION}
./install.sh

Then start it:

docker compose up --wait

Sentry listens on http://127.0.0.1:9000 by default. Docker Engine 19.03.6 or later and Docker Compose 2.32.2 or later are required, and an older Compose fails on the file syntax rather than on anything Sentry does.

Look at what you actually started:

docker compose ps
free -h

docker compose ps lists every service in the stack, and the list is long: Postgres, ClickHouse, Kafka, Redis, Relay, Snuba, Symbolicator, and several worker and cron processes. Count them once, because that number is your maintenance load. Each entry is a process that can crash, fill a disk, or fail a migration.

If a service sits in Restarting state, look at memory before anything else:

dmesg -T | grep -i 'out of memory'

A line like Out of memory: Killed process 3412 (java) means the kernel's OOM killer (out of memory killer) took a container because the box ran out of RAM, so that service never becomes healthy and the stack never finishes starting. This is the ordinary outcome of running the full stack under its documented minimum. The documentation also flags disk speed: iowait above 10% means the machine cannot keep up with the ingest pipeline. Read it from the wa column in top, or from iostat -x 5 if you have sysstat installed.

Upgrades are the part people underestimate

Sentry self-hosted ships monthly under CalVer, a calendar based version scheme, with a primary release on the 15th of each month. You cannot jump from an old version straight to the latest. The project defines hard stop versions, and you must check out each one in order to pick up its database migrations. As of August 2026 the published hard stops are 9.1.2, 21.5.0, 21.6.3, 23.6.2, 23.11.0, 24.8.0, 25.5.1, 26.5.0 and 26.7.0. The documentation also lists releases to skip because of migration problems, including 23.7.0, 25.9.0, 25.12.0 and the 26.3.0 to 26.4.0 range.

An upgrade is a checkout plus a rerun of the installer:

git fetch
git checkout 26.7.0
./install.sh
docker compose up --wait

Snapshot the server before you start, because a migration over a large ClickHouse dataset can run for hours and a failure partway leaves the database between two schemas. The plain cause of most failed self-hosted Sentry upgrades: the box sat on one version for a year, so the jump crosses several hard stops at once and one of the skipped migrations was the one that mattered.

One more thing to know before you commit. Sentry self-hosted is under the Functional Source License (FSL), which Sentry itself introduced. It is fair source rather than OSI approved open source: you may run it for yourself, and you may not sell it as a competing service. Each release converts to Apache 2.0 two years after it ships.

GlitchTip: the 512 MB answer

GlitchTip is MIT licensed and receives events from Sentry's open source SDKs, so an instrumented application moves across by changing one value: the DSN (data source name, the URL your SDK posts events to). It needs PostgreSQL 14 or later. Valkey or Redis 7 or later is optional, and it makes larger instances faster.

Install is Docker plus one compose file:

sudo apt install docker.io
curl -O https://glitchtip.com/assets/compose.sample.yml
mv compose.sample.yml compose.yml

Edit the environment section before you start anything. The values you must set are the secret, the domain and the mail path:

SECRET_KEY: <output of openssl rand -base64 50>
GLITCHTIP_DOMAIN: https://errors.example.com
DEFAULT_FROM_EMAIL: errors@example.com
EMAIL_URL: smtp://user:password@smtp.example.com:587

The sample already wires DATABASE_URL to its own postgres service, so leave that line alone unless you are pointing at a database you run elsewhere. GLITCHTIP_DOMAIN must include the scheme. Without https:// on the front, the links in alert emails are built wrong and land on a URL that does not answer.

Start it and watch the first boot:

docker compose up -d
docker compose logs -f web

The image tags in the sample as of August 2026 are postgres:18, valkey/valkey:9 and glitchtip/glitchtip:6. Keep them pinned. A compose file that says latest will upgrade your database engine on the next docker compose pull, and a Postgres major version jump under a running instance is how a working error tracker stops starting.

To reach the 256 MB to 512 MB range, the sample file's own comments tell you what to switch off, starting with Valkey and the optional log and uptime features. Running without Valkey means GlitchTip uses its database for cache and queue work instead, which is slower and still correct. All in one mode runs the worker inside the web process, so you maintain one application container rather than two.

Put a proxy in front of it. GlitchTip's documentation asks for a proxy or load balancer that buffers requests and handles chunked Transfer-Encoding, and it gives nginx as the worked example. Without buffering, a slow client holds an application worker open for the whole upload, so a few slow senders can occupy every worker you have and healthy clients start timing out.

Upgrades are the easy part:

docker compose pull
docker compose stop
docker compose up -d

Database migrations run automatically at start. Take a dump first anyway, because an automatic migration is still a migration.

Bugsink: one container, and a licence you must read

Bugsink is the lightest of the three. It speaks the Sentry SDK protocol, and it runs with no message queue and no external service beyond a database. SQLite is the default, with MySQL and PostgreSQL supported when you outgrow it.

A throwaway instance, to see the interface before you commit:

docker pull bugsink/bugsink:latest
docker run \
  -e SECRET_KEY=PUT_AN_ACTUAL_RANDOM_SECRET_HERE_OF_AT_LEAST_50_CHARS \
  -e CREATE_SUPERUSER=admin@example.org:admin \
  -e PORT=8000 \
  -p 8000:8000 \
  bugsink/bugsink

Open http://localhost:8000/ and sign in with the address and password you passed in CREATE_SUPERUSER. That container keeps nothing when it stops. For a real instance take the project's compose sample, which pairs bugsink/bugsink:2 with postgres:17-alpine and sets DATABASE_URL, BASE_URL and BEHIND_HTTPS_PROXY. Generate the secret properly:

openssl rand -base64 50

BASE_URL has to match the URL your users and SDKs really use, including the scheme. Leave it at http://localhost:8000 on a box you reach at https://errors.example.com and every link in a notification email points at a host that does not resolve for the person reading it. Set BEHIND_HTTPS_PROXY to true when nginx or Caddy terminates TLS (transport layer security) in front of it, because otherwise Bugsink builds http:// URLs behind your https:// proxy and browsers block the mixed content.

The vendor publishes its own throughput figures: 18 events per second at 50 KB each, which works out at 1.5 million events per day, on a 2 vCPU and 4 GB VPS. Treat that as the shape of the tool rather than a guarantee for your workload. It does tell you the ceiling sits far above what one small application produces.

Now the licence, and this is the part to read before it lands in your stack. Bugsink is released under the PolyForm Shield License 1.0.0. That is source available, not open source: you may run it and modify it, and you may not use it to build something that competes with Bugsink. For an internal error tracker the restriction never comes up. If your company sells developer tooling, have someone read the licence text first.

Error tracking and LLM observability are still two tools

Search for one tool that does error tracking and large language model (LLM) observability together and you will find products claiming both. The data shapes are different, which is why the merge keeps not happening. An error tracker receives an exception with a stack trace, computes a fingerprint from it, and collapses thousands of occurrences into one issue with a counter. An LLM tracing tool receives a span holding a prompt, a response, a token count and a latency, and it has to keep every one of them, because two calls with identical inputs are still separate events worth reading.

So run both. Send exceptions to the error tracker, and send model calls somewhere built for them: self-hosted Langfuse for agent tracing covers that side, and self-hosted AI observability approaches the same job from a different angle. Your application already produces both kinds of failure. A model call that returns confident nonsense throws no exception at all, so an error tracker will never show it to you.

Disk growth is the failure that finds you later

Every error tracker is a write heavy database with an unbounded input. Your application decides how much it writes, and one new bug in a hot code path can produce a million events overnight.

GlitchTip publishes a figure worth planning against: an instance handling one million events per month may need 30 GB of disk. That covers one month of ingest at that rate, and your retention window decides how many months you are storing at once.

Bugsink comes at it from the other end. Instead of a fixed quota it applies a retention algorithm over event count and event age, and it exposes the caps directly: MAX_RETENTION_EVENT_COUNT for the whole installation, MAX_RETENTION_PER_PROJECT_EVENT_COUNT per project, and MAX_EVENT_AGE_DAYS as an absolute cut. Setting an installation wide event budget is the honest way to size a disk, because that budget is the disk.

Watch the real numbers on the box:

df -h /
docker system df -v
du -sh /var/lib/docker/volumes/*

docker system df -v prints per volume sizes, so you can see which service is the one growing. A volume gaining several gigabytes a week with no change in traffic usually means retention was never configured, so nothing is ever deleted and the only limit is the partition.

Memory is the same problem wearing different clothes. A stack with no limits will take everything the kernel offers, and when the machine runs out the OOM killer picks the largest process, which may well be your web server rather than the tracker that caused it. Give every service a ceiling: memory limits in Docker Compose shows the syntax and what a container does when it reaches its cap. A container killed at its own limit is a contained failure. A container killed by the kernel takes a neighbour down with it.

Which stack fits which VPS

  • 1 GB, or 2 GB with room to spare: GlitchTip in all in one mode with Valkey switched off, or Bugsink on SQLite. Both are comfortable here for a handful of applications.
  • 4 GB: Bugsink with PostgreSQL, or GlitchTip with Valkey on and a separate worker service. This is the size where you stop tuning and just run it.
  • 8 GB: still short of the official Sentry stack. Spend it on a longer retention window and a larger disk for whichever light option you chose.
  • 16 GB minimum, 32 GB recommended: the official Sentry self-hosted stack, and only when you need a Sentry feature the lighter projects do not implement. Check the specific feature against each project's documentation first, because the compatible projects cover the common ones.

Whatever you run, the error tracker cannot report its own death. Put a check on it from a different machine: Uptime Kuma watching from another box will tell you the tracker is down, which is precisely the moment your application starts throwing errors that nobody is recording.

When a hosted plan is the cheaper answer

Self-hosting an error tracker pays off when data residency rules require it, or when your event volume is high enough that per event pricing hurts. Outside those cases, do the arithmetic honestly. Sentry's documented minimum is a 16 GB server with 4 cores and fast disk, and a VPS that size is not a cheap VPS. Then add the operational work: crossing every hard stop in order, and taking a snapshot before each migration, a few times a year.

GlitchTip and Bugsink change that arithmetic completely, because 512 MB to 4 GB is an inexpensive box and the upgrade is a docker compose pull. That is why most people who ask this question end up on one of the compatible projects rather than on the official stack. They wanted error tracking, not a distributed data pipeline to babysit.

If you are still working out what belongs on the server at all, the wider list of what is worth self-hosting puts error tracking next to the other services competing for the same RAM.

FAQ

Can I self-host Sentry on a 2 GB VPS?

No. Sentry's self-hosted documentation states a minimum of 4 CPU cores, 16 GB of RAM plus 16 GB of swap, and 20 GB of free disk. The stack runs Postgres, ClickHouse, Kafka, Redis and several worker processes at the same time, so on a small box the kernel kills containers before the install finishes. Confirm it with dmesg -T | grep -i 'out of memory', which prints a line naming the killed process. For a 2 GB VPS use GlitchTip, which documents 512 MB, or Bugsink, which runs as a single container on SQLite.

Do I have to change my application code to switch from Sentry to GlitchTip or Bugsink?

No. Both accept events from Sentry's open source SDKs, so you keep the SDK you already installed and change one value: the DSN, the URL the SDK posts events to. Move it into an environment variable if it is still hardcoded, point it at the new host, then raise a test exception and watch it arrive. If nothing appears, check that the project identifier in the DSN matches a project that exists on the new server, and that your firewall lets the application reach that host and port.

How much disk does self-hosted error tracking need?

That depends on your event volume and your retention window rather than on the tool. GlitchTip publishes 30 GB for an instance handling one million events per month. Bugsink lets you set the budget directly with MAX_RETENTION_EVENT_COUNT and MAX_EVENT_AGE_DAYS, so you choose the ceiling and the disk requirement follows from it. Configure retention on the first day. A tracker with no retention policy grows until df -h reads 100%, and at that point ingest stops and you lose the errors you most wanted to see.

Why does upgrading self-hosted Sentry keep failing?

Because the upgrade skipped a hard stop. Sentry self-hosted defines specific versions carrying database migrations you must pass through, and as of August 2026 those are 9.1.2, 21.5.0, 21.6.3, 23.6.2, 23.11.0, 24.8.0, 25.5.1, 26.5.0 and 26.7.0. Going straight from an old release to the newest skips those migrations, so the schema and the code disagree and the upgrade stops partway. Check out each hard stop in order and run ./install.sh at every one, snapshot the server before you begin, and read the documented list of releases to avoid, which includes 23.7.0, 25.9.0 and 25.12.0.

#error-tracking#sentry#glitchtip#observability#self-hosting