Self-hosted error telemetry (GlitchTip)
Self-hosted error telemetry (GlitchTip)
This pattern composes Compute, Network, and Block Storage into a self-hosted error-tracking service you run on infrastructure you control.
What this template does#
Provisions a single instance running GlitchTip, an open-source error-tracking system that speaks the Sentry protocol (a self-hosted alternative to Sentry or LogRocket for error events). The official Sentry SDKs report to it unchanged once pointed at a project DSN:
- Compute instance that runs GlitchTip's web, worker, and migrate containers in Docker alongside bundled PostgreSQL and Redis (4 vCPU and 4 GiB RAM)
- Private network, subnet, router, port, and security group; a floating IP for public access
- A block volume mounted at
/var/lib/docker, so the event database and uploaded files (sourcemaps, debug symbols) live on a volume you can grow rather than on the boot disk - cloud-init installs Docker Engine and brings up PostgreSQL, Redis, runs the database migration, and starts the web and worker services
GlitchTip's secret key and the database password are generated on first boot and written to /opt/glitchtip/.env; no credential ships with this template.
Parameters#
| Parameter | Description | Default |
|---|---|---|
key_name | SSH keypair name (must already exist) | No default |
flavor_name | Instance size (GlitchTip's containers plus PostgreSQL and Redis run on 4 vCPU / 4 GiB) | s1a.medium |
image_name | Operating system image | Ubuntu-24.04 |
app_name | Display name prefix for resources | glitchtip |
volume_size | Block volume size in GiB, mounted at /var/lib/docker | 20 |
external_network | External network for floating IP allocation | PublicStatic |
private_cidr | CIDR for the private subnet | 10.49.0.0/24 |
app_allowed_cidr | CIDR allowed to reach GlitchTip on port 8000 | 10.49.0.0/24 |
Finish setup after apply#
cloud-init runs the database migration and starts GlitchTip's web and worker services against the bundled PostgreSQL and Redis, with the generated secrets in /opt/glitchtip/.env. Open http://YOUR_FLOATING_IP:8000 (reachable from app_allowed_cidr, the private network by default) and register the first account; it becomes the instance superuser.
After creating your account, lock down public self-signup:
sudo sed -i 's/ENABLE_USER_REGISTRATION=true/ENABLE_USER_REGISTRATION=false/' /opt/glitchtip/.env
cd /opt/glitchtip && sudo docker compose up -dFor production, point a domain at the floating IP, put a reverse proxy in front for HTTPS on 443, and update GLITCHTIP_DOMAIN in /opt/glitchtip/.env to the HTTPS address.
Connect a Sentry SDK#
GlitchTip speaks the Sentry event-ingestion protocol. Create a project from the GlitchTip UI to get a DSN, then initialize the Sentry SDK in your application as you normally would, pointed at that DSN:
import sentry_sdk
sentry_sdk.init(dsn="http://<key>@YOUR_FLOATING_IP:8000/<project_id>")No code change beyond the DSN is needed; GlitchTip accepts events from the official Sentry SDKs across languages.
Access and security#
GlitchTip listens on port 8000 over plain HTTP by default. The security group restricts 8000 to app_allowed_cidr, which defaults to the private network only. Put a reverse proxy with TLS on 443 in front for routine access and so SDKs report events over HTTPS.
When to use this pattern#
Run error tracking and event grouping for your applications on a host you operate, with a DSN your existing Sentry SDK integrations point at unchanged. The bundled PostgreSQL and Redis suit a single team's event volume; to run them separately, point DATABASE_URL and VALKEY_URL in /opt/glitchtip/.env at a self-managed PostgreSQL instance and a Redis instance.
Estimated cost#
Monthly cost estimate
Pricing calculator ↗Sized as a custom package on shared vCPU.
Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.
What each resource is for
GlitchTip error-telemetry host
s1a.medium · 4 shared vCPU, 4 GiB RAM, 0.5 Gbps
Runs GlitchTip's web, worker, and migrate containers in Docker (Sentry-protocol-compatible error tracking) alongside its bundled PostgreSQL and Redis, with the event database and upload storage on an attached volume.
GlitchTip plus its bundled PostgreSQL and Redis runs on 4 vCPU and 4 GiB RAM. Size up for high event-ingestion volume.
Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.
Included in baseline
s1a.medium
4 shared vCPU, 4 GiB RAM, 0.5 Gbps
Compute + RAM rate basis
4 vCPU + 4 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.
Block storage (50 GiB)
50 GiB at $0.08/GiB/mo
Public IP (included)
1 included with the custom package
Package promotional discount
Flat −$5.00/mo on the custom package (same promotion as named plans).
Included at no charge
These line items are zero on Quake AI. Many other providers meter them separately.
Data transfer (inbound and outbound)
Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.
AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.
Learn morePrivate networking
Private networks, subnets, Neutron routers, and security groups are included with the plan.
VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.
Control-plane API requests
OpenStack API calls for provisioning and management are included.
Some managed services on other clouds meter API calls or charge for premium control-plane features.
Pricing data last validated: . For current rates, check quake.ai/pricing.
Template source#
Show source (7 files)Hide source
data "openstack_images_image_v2" "os" {
name = var.image_name
most_recent = true
}
data "openstack_networking_network_v2" "external" {
name = var.external_network
}
resource "openstack_networking_network_v2" "private" {
name = "${var.app_name}-net"
admin_state_up = true
}
resource "openstack_networking_subnet_v2" "private" {
name = "${var.app_name}-subnet"
network_id = openstack_networking_network_v2.private.id
cidr = var.private_cidr
ip_version = 4
dns_nameservers = ["1.1.1.1", "8.8.8.8"]
}
resource "openstack_networking_router_v2" "main" {
name = "${var.app_name}-router"
external_network_id = data.openstack_networking_network_v2.external.id
}
resource "openstack_networking_router_interface_v2" "private" {
router_id = openstack_networking_router_v2.main.id
subnet_id = openstack_networking_subnet_v2.private.id
}
resource "openstack_networking_secgroup_v2" "glitchtip" {
name = "${var.app_name}-sg"
description = "SSH and HTTP/HTTPS for a reverse proxy; app port 8000 restricted"
}
resource "openstack_networking_secgroup_rule_v2" "ssh" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.glitchtip.id
}
# 80 and 443 carry GlitchTip when it is served over a domain with automatic
# TLS through a reverse proxy (Caddy or Nginx). They are not used until you put
# a proxy in front; see the reference page.
resource "openstack_networking_secgroup_rule_v2" "http" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.glitchtip.id
}
resource "openstack_networking_secgroup_rule_v2" "https" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.glitchtip.id
}
# Raw app HTTP on 8000 is restricted to app_allowed_cidr (the private network
# by default). Use it for setup over an SSH tunnel or a scoped workstation IP;
# put a reverse proxy on 443 in front for routine access and for SDKs that
# report events over HTTPS.
resource "openstack_networking_secgroup_rule_v2" "app" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 8000
port_range_max = 8000
remote_ip_prefix = var.app_allowed_cidr
security_group_id = openstack_networking_secgroup_v2.glitchtip.id
}
resource "openstack_networking_port_v2" "glitchtip" {
name = "${var.app_name}-port"
network_id = openstack_networking_network_v2.private.id
security_group_ids = [openstack_networking_secgroup_v2.glitchtip.id]
fixed_ip {
subnet_id = openstack_networking_subnet_v2.private.id
}
depends_on = [openstack_networking_router_interface_v2.private]
}
resource "openstack_blockstorage_volume_v3" "data" {
name = "${var.app_name}-data"
size = var.volume_size
}
resource "openstack_compute_instance_v2" "glitchtip" {
name = var.app_name
flavor_name = var.flavor_name
key_pair = var.key_name
user_data = templatefile("${path.module}/cloud-init/glitchtip.yaml.tftpl", {
app_name = var.app_name
})
block_device {
uuid = data.openstack_images_image_v2.os.id
source_type = "image"
destination_type = "volume"
volume_size = 30
boot_index = 0
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.glitchtip.id
}
}
resource "openstack_compute_volume_attach_v2" "data" {
instance_id = openstack_compute_instance_v2.glitchtip.id
volume_id = openstack_blockstorage_volume_v3.data.id
}
resource "openstack_networking_floatingip_v2" "glitchtip" {
pool = var.external_network
}
resource "openstack_networking_floatingip_associate_v2" "glitchtip" {
floating_ip = openstack_networking_floatingip_v2.glitchtip.address
port_id = openstack_networking_port_v2.glitchtip.id
}
variable "key_name" {
description = "SSH keypair name (must already exist in your project)"
type = string
}
variable "flavor_name" {
description = "Instance size. GlitchTip's web, worker, and migrate containers plus bundled PostgreSQL and Redis run comfortably on 4 vCPU and 4 GiB RAM (s1a.medium). Size up for high event-ingestion volume."
type = string
default = "s1a.medium"
}
variable "image_name" {
description = "Operating system image. Ubuntu 24.04 is the recommended base."
type = string
default = "Ubuntu-24.04"
}
variable "app_name" {
description = "Display name prefix for compute and network resources"
type = string
default = "glitchtip"
}
variable "volume_size" {
description = "Block volume size in GiB, mounted at /var/lib/docker so the error-telemetry data (the PostgreSQL database holding events and the upload storage) lives on a volume you can grow rather than on the boot disk."
type = number
default = 20
}
variable "external_network" {
description = "Shared external network for router gateway and floating IPs; defaults to PublicStatic (persisted FIP / production pattern). Override with PublicEphemeral for ephemeral demos."
type = string
default = "PublicStatic"
}
variable "private_cidr" {
description = "CIDR for the private tenant network the instance lives in"
type = string
default = "10.49.0.0/24"
}
variable "app_allowed_cidr" {
description = "CIDR allowed to reach GlitchTip on port 8000. Defaults to the private network only, so the app is not exposed to the public internet on its raw port. Serve it over a domain with HTTPS on 443 behind a reverse proxy for routine access; SDKs report events to the DSN over whichever scheme you configure. To reach port 8000 directly from your workstation during setup, set this to YOUR_IP/32."
type = string
default = "10.49.0.0/24"
}
output "instance_id" {
description = "ID of the compute instance running GlitchTip"
value = openstack_compute_instance_v2.glitchtip.id
}
output "floating_ip" {
description = "Public floating IP address of the GlitchTip host"
value = openstack_networking_floatingip_v2.glitchtip.address
}
output "private_ip" {
description = "Private IP address of the instance"
value = openstack_compute_instance_v2.glitchtip.access_ip_v4
}
output "app_url" {
description = "GlitchTip app URL on port 8000. Reachable from app_allowed_cidr (the private network by default). Set GLITCHTIP_DOMAIN to this address (or your real domain once you put a reverse proxy in front) so GlitchTip generates correct absolute links and DSNs."
value = "http://${openstack_networking_floatingip_v2.glitchtip.address}:8000"
}
terraform {
required_version = ">= 1.6.0"
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "~> 2.0"
}
}
}
provider "openstack" {}
# Required: SSH keypair must already exist in your project
key_name = "YOUR_KEY_NAME"
# Recommended: restrict the app port (8000) to your workstation IP for setup.
# Leave unset to keep 8000 reachable only from the private network and tunnel
# over SSH. Serve GlitchTip over a domain with HTTPS on 443 behind a reverse
# proxy for routine access.
# app_allowed_cidr = "203.0.113.10/32"
# flavor_name = "s1a.medium"
# image_name = "Ubuntu-24.04"
# app_name = "glitchtip"
# volume_size = 20
# external_network = "PublicStatic"
# private_cidr = "10.49.0.0/24"
#cloud-config
package_update: true
packages:
- ca-certificates
- curl
write_files:
- path: /opt/glitchtip/docker-compose.yml
permissions: "0644"
content: |
# GlitchTip error telemetry for ${app_name}. GlitchTip speaks the Sentry
# protocol, so the official Sentry SDKs report to it unchanged once
# pointed at a project DSN. The app listens on port 8000. No credential
# ships with this template: SECRET_KEY and the database password are
# generated on first boot. The image uses a major version tag (v6),
# which GlitchTip's own docs recommend so minor updates apply on
# `docker compose pull` without a breaking major-version jump.
services:
migrate:
image: glitchtip/glitchtip:v6
restart: "no"
command: ["./manage.py", "migrate"]
env_file:
- /opt/glitchtip/.env
depends_on:
- postgres
- redis
web:
image: glitchtip/glitchtip:v6
restart: unless-stopped
ports:
- "8000:8000"
env_file:
- /opt/glitchtip/.env
depends_on:
- migrate
volumes:
- glitchtip_uploads:/code/uploads
worker:
image: glitchtip/glitchtip:v6
restart: unless-stopped
command: ["./manage.py", "runworker", "--scheduler"]
env_file:
- /opt/glitchtip/.env
depends_on:
- migrate
volumes:
- glitchtip_uploads:/code/uploads
postgres:
image: postgres:16-alpine
restart: unless-stopped
env_file:
- /opt/glitchtip/.env
volumes:
- glitchtip_pg:/var/lib/postgresql/data
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- glitchtip_redis:/data
volumes:
glitchtip_uploads:
glitchtip_pg:
glitchtip_redis:
runcmd:
- |
set -e
# The data volume attaches as /dev/sdb on this platform (not /dev/vdb).
# Mount it at /var/lib/docker before Docker is installed so the
# PostgreSQL data and uploaded files (sourcemaps, debug symbols) live on
# the resizable volume rather than the boot disk.
DEV=/dev/sdb
for i in $(seq 1 30); do [ -b "$DEV" ] && break; sleep 5; done
if ! blkid "$DEV" >/dev/null 2>&1; then mkfs.ext4 -F -L glitchtipdata "$DEV"; fi
mkdir -p /var/lib/docker
mount "$DEV" /var/lib/docker
grep -q "$DEV" /etc/fstab || echo "$DEV /var/lib/docker ext4 defaults,nofail 0 2" >> /etc/fstab
# Install Docker Engine plus the compose plugin from Docker's convenience
# script.
curl -fsSL https://get.docker.com | sh
# Generate GlitchTip's secret key and the bundled database password on
# first boot. These never leave this instance.
SECRET_KEY=$(openssl rand -hex 32)
PGPASS=$(openssl rand -hex 24)
FLOATING_IP=$(curl -fsSL -m 5 https://ifconfig.me || echo "")
umask 077
{
echo "SECRET_KEY=$SECRET_KEY"
echo "DATABASE_URL=postgres://glitchtip:$PGPASS@postgres:5432/glitchtip"
echo "POSTGRES_USER=glitchtip"
echo "POSTGRES_PASSWORD=$PGPASS"
echo "POSTGRES_DB=glitchtip"
echo "VALKEY_URL=redis://redis:6379/0"
echo "GLITCHTIP_DOMAIN=http://$FLOATING_IP:8000"
echo "[email protected]"
echo "EMAIL_URL=consolemail://"
echo "ENABLE_USER_REGISTRATION=true"
echo "# Set GLITCHTIP_DOMAIN to your real https:// domain once a reverse"
echo "# proxy is in front, and switch ENABLE_USER_REGISTRATION to false"
echo "# once your account is created:"
echo "# GLITCHTIP_DOMAIN=https://errors.example.com"
echo "# ENABLE_USER_REGISTRATION=false"
echo "# Configure outbound email for invite and alert notifications,"
echo "# for example: EMAIL_URL=smtp://user:[email protected]:587"
} > /opt/glitchtip/.env
chmod 600 /opt/glitchtip/.env
cd /opt/glitchtip
docker compose up -d
# Self-hosted error telemetry (GlitchTip)
Single compute instance running [GlitchTip](https://glitchtip.com), an open-source error-tracking system that speaks the Sentry protocol (a self-hosted alternative to Sentry or LogRocket for error events), on infrastructure you control. After apply, you register the first account, lock down public self-signup, create a project, and point a Sentry SDK at its DSN.
**Network class:** production — `external_network` defaults to `PublicStatic` for persisted floating IPs and multi-tier stacks; override with `PublicEphemeral` for ephemeral demos.
The instance provisions a private network, a floating IP, and a block volume mounted at `/var/lib/docker` so the event database and uploaded files live on a resizable volume. cloud-init installs Docker Engine, brings up the bundled PostgreSQL and Redis, runs the database migration, and starts GlitchTip's web and worker services.
## Where this fits
GlitchTip is the error-tracking layer in the observability stack: it groups exceptions, captures stack traces, and alerts on regressions, the same role Sentry plays for teams that have not self-hosted. It is the error-telemetry equivalent of Sentry or LogRocket in the [self-hosted vibecode stack](/resources/solutions/self-hosted-vibecode-stack).
## Prerequisites
- OpenTofu >= 1.6.0 or Terraform >= 1.6.0
- Quake AI account with OpenStack credentials
- An existing SSH keypair in your project (the value of `key_name` must match that keypair)
## Resource baseline
GlitchTip's web, worker, and migrate containers plus bundled PostgreSQL and Redis run on 4 vCPU and 4 GiB RAM. The default `s1a.medium` flavor leaves headroom for the three application containers. Size up for high event-ingestion volume.
## Usage
1. Clone or copy this template directory
2. Copy `terraform.tfvars.example` to `terraform.tfvars` and fill in your values
3. Source your OpenStack credentials: `source openrc.sh`
4. Initialize: `tofu init`
5. Preview: `tofu plan`
6. Apply: `tofu apply`
After apply, cloud-init installs Docker, generates the secret key and database password into `/opt/glitchtip/.env`, runs the database migration, and starts PostgreSQL, Redis, the web service, and the worker. Open the app and register the first account; it becomes the instance superuser.
No credential ships with this template: `SECRET_KEY` and the database password are generated on first boot.
## Access and security
GlitchTip listens on port 8000 over plain HTTP. The security group restricts 8000 to `app_allowed_cidr`, which defaults to the private network only. Put a reverse proxy with TLS on 443 in front for routine access and so SDKs report events over HTTPS. `ENABLE_USER_REGISTRATION` defaults to `true` so you can create the first account; disable it immediately after.
## Datastores
This template bundles PostgreSQL and Redis as containers on the same instance, which suits a single team's event volume. To run them separately, point `DATABASE_URL` and `VALKEY_URL` in `/opt/glitchtip/.env` at a [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres) instance and a [Redis](/resources/iac-templates/redis-cache) instance, and remove the bundled services from the compose file.
## Variables
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key_name` | string | yes | n/a | SSH keypair name (must already exist in your project) |
| `flavor_name` | string | no | `s1a.medium` | Instance size (GlitchTip's containers plus PostgreSQL and Redis run on 4 vCPU / 4 GiB) |
| `image_name` | string | no | `Ubuntu-24.04` | Operating system image |
| `app_name` | string | no | `glitchtip` | Display name prefix for resources |
| `volume_size` | number | no | `20` | Block volume size in GiB, mounted at `/var/lib/docker` |
| `external_network` | string | no | `PublicStatic` | Persisted FIP / production default; override with `PublicEphemeral` for demos |
| `private_cidr` | string | no | `10.49.0.0/24` | CIDR for the private subnet |
| `app_allowed_cidr` | string | no | `10.49.0.0/24` | CIDR allowed to reach GlitchTip on port 8000 |
## Outputs
| Name | Description |
| --- | --- |
| `floating_ip` | Public floating IP assigned to the instance |
| `private_ip` | Private IP address of the instance |
| `app_url` | GlitchTip app URL on port 8000 |
| `instance_id` | Compute instance ID |
## Scope
This is a single-VM GlitchTip host that you operate, not a managed error-tracking cloud. It is CPU-only and runs in one region. You operate the instance, Docker, GlitchTip, PostgreSQL, Redis, and the data volume yourself: back them up, patch them, and watch resource use as event volume grows. For higher volume, move PostgreSQL and Redis onto their own instances and size the app host up.
## Documentation
See also: [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres), [Redis](/resources/iac-templates/redis-cache), [Umami analytics](/resources/iac-templates/analytics-umami)
Resources, parameters, and variables
key_namerequiredflavor_name="s1a.medium"image_name="Ubuntu-24.04"app_name="glitchtip"volume_size=20external_network="PublicStatic"private_cidr="10.49.0.0/24"app_allowed_cidr="10.49.0.0/24"
Customize this pattern#
- Customize a template's image and flavor
- Add a block volume to a template
- Parameterize a template with a tfvars file
See also#
- Self-managed PostgreSQL
- Redis
- Umami analytics: the product-analytics counterpart in the observability layer
- Monitoring stack: infrastructure metrics alongside this application-level error telemetry
- Self-hosted vibecode stack
Usage Guidelines
The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.
For the full policy, see Usage Guidelines.
See Also
Terraform and OpenTofu on Quake AI
Prerequisite
Networks
Prerequisite
Authoring IaC templates for Quake AI
Shares: Volumes, Security Groups
Deploy an API gateway with the api-gateway template
Shares: Volumes, Security Groups
Deploy a regional edge cache with the edge-cache template
Shares: Volumes, Security Groups