Deploying Qarote

Qarote ships as a self-contained application. Pick the deployment method that fits your environment — all three give you the same dashboard.

| Method | Best for | | --------------------------------- | ----------------------------------------------------------------------- | | Binary | Minimal footprint, existing PostgreSQL, VMs or bare metal | | Docker Compose | Containerised setup, everything bundled including Postgres | | Dokku | Git-push deploys, automatic HTTPS, Heroku-style PaaS on your own server |

All deployments start with the free tier (core monitoring). Premium features — workspaces, alerting, integrations — unlock when you activate a license key in Settings → License. No restart needed.

Prerequisites

  • Binary: PostgreSQL 17+ with the TimescaleDB extension and TimescaleDB Toolkit already running (no Docker or Node.js needed)
  • Docker Compose: Docker and Docker Compose installed (a TimescaleDB + Toolkit PostgreSQL is bundled)
  • Dokku: Dokku installed on your server (its Postgres must ship TimescaleDB + Toolkit — see below)
  • Minimum 2 GB RAM, 10 GB disk space

⚠️ PostgreSQL must ship TimescaleDB and the TimescaleDB Toolkit. Qarote stores time-series data in TimescaleDB hypertables and derives throughput rates at read time with the Toolkit's counter_agg/rate(). Both are server-side extensions that must be present in your Postgres image/package — Qarote activates them (CREATE EXTENSION) but cannot install them, so a missing extension fails the migration on first boot. Use timescale/timescaledb-ha (the Docker Compose setup bundles it) or install the timescaledb-2-postgresql-17 and timescaledb-toolkit-postgresql-17 packages. timescaledb-toolkit has no Homebrew formula — on macOS use a Docker/Linux Postgres.

Binary

The binary embeds both the API and the frontend. Drop it on any Linux or macOS host that already has PostgreSQL — nothing else required.

1. Set up PostgreSQL

If PostgreSQL isn't already running:

# macOS
brew install postgresql@17

# Ubuntu / Debian / WSL2
sudo apt install postgresql

Create a dedicated database user:

sudo -u postgres psql -c "CREATE USER qarote WITH PASSWORD 'your-secure-password';"
sudo -u postgres psql -c "CREATE DATABASE qarote OWNER qarote;"

# Recommended: set idle connection timeouts
sudo -u postgres psql -c "ALTER SYSTEM SET idle_session_timeout = '30min';"
sudo -u postgres psql -c "ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';"
sudo -u postgres psql -c "SELECT pg_reload_conf();"

Your connection URL will be: postgresql://qarote:your-secure-password@localhost:5432/qarote

2. Download the binary

# Auto-detects your OS and architecture
PLATFORM="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/' | sed 's/aarch64/arm64/')"
curl -L "https://github.com/getqarote/Qarote/releases/latest/download/qarote-${PLATFORM}.tar.gz" | tar xz --strip-components=1

Or download a specific build from GitHub Releases:

| Platform | File | | ------------------- | ---------------------------- | | Linux x64 | qarote-linux-x64.tar.gz | | Linux ARM64 | qarote-linux-arm64.tar.gz | | macOS Apple Silicon | qarote-darwin-arm64.tar.gz | | macOS Intel | qarote-darwin-x64.tar.gz |

Windows

Run the binary inside WSL2. Native Windows is not supported.

3. Run the setup wizard

./qarote setup

The wizard will:

  • Ask for your PostgreSQL URL and verify the connection
  • Create an admin account (written directly to the database — no sign-up flow needed)
  • Let you disable public registration (recommended for private deployments)
  • Generate JWT_SECRET and ENCRYPTION_KEY
  • Write a .env file in the current directory

Tip

Say Yes to creating an admin account and No to public registration. You can invite team members later from inside the app.

4. Start Qarote

./qarote

The server starts on http://localhost:3000. Database migrations run automatically on first boot. The migrations/ directory must stay alongside the binary.

To run the background workers, start each in a separate terminal or as a systemd service:

./qarote worker               # alert evaluation (required for alerts)
./qarote notification-worker  # transactional emails (required if email is enabled)

With a Developer or Enterprise license, three additional workers unlock premium features:

./qarote digest-worker    # weekly digest emails
./qarote metrics-worker   # queue metrics collection (run exactly 1 — multiple replicas write duplicate snapshot rows)
./qarote firehose-worker  # message tracing (run exactly 1 — multiple replicas create duplicate consumers)

Running as a system service (Linux)

To keep Qarote running across reboots, create a systemd unit for each process. This assumes the binary lives at /opt/qarote/qarote and the .env at /opt/qarote/.env.

/etc/systemd/system/qarote.service

[Unit]
Description=Qarote RabbitMQ Dashboard
After=network.target postgresql.service

[Service]
Type=simple
User=qarote
WorkingDirectory=/opt/qarote
EnvironmentFile=/opt/qarote/.env
ExecStart=/opt/qarote/qarote
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Create one unit per worker with the same template, changing Description and ExecStart:

| Unit file | ExecStart | | ------------------------------------ | ---------------------------------------- | | qarote-alert-worker.service | /opt/qarote/qarote worker | | qarote-notification-worker.service | /opt/qarote/qarote notification-worker |

Then reload and enable:

sudo systemctl daemon-reload
sudo systemctl enable --now qarote qarote-alert-worker qarote-notification-worker

Manual setup (without the wizard)

If you prefer flags over the interactive wizard:

# Requires openssl to generate secrets (installed by default on macOS and most Linux distros)
./qarote \
  --database-url postgresql://qarote:password@localhost/qarote \
  --jwt-secret $(openssl rand -hex 64) \
  --encryption-key $(openssl rand -hex 64)

CLI reference

| Flag | Description | | -------------------------------- | ------------------------------------------- | | ./qarote setup | Interactive setup wizard (generates .env) | | ./qarote worker | Start the alert worker process | | ./qarote notification-worker | Start the notification worker process | | ./qarote digest-worker | Start the digest worker (EE) | | ./qarote metrics-worker | Start the metrics worker (EE) | | ./qarote firehose-worker | Start the firehose worker (EE) | | --database-url <url> | PostgreSQL connection URL | | --jwt-secret <secret> | JWT signing secret (min 32 chars) | | --encryption-key <key> | Encryption key (min 32 chars) | | -p, --port <port> | Server port (default: 3000) | | -h, --host <host> | Server host (default: localhost) | | --enable-email <bool> | Enable email features (default: false) | | --smtp-host <host> | SMTP server hostname | | --smtp-port <port> | SMTP server port (default: 587) | | --smtp-user <user> | SMTP username | | --smtp-pass <pass> | SMTP password | | --smtp-service <name> | SMTP service for OAuth2 (e.g. gmail) | | --smtp-oauth-client-id <id> | OAuth2 client ID | | --smtp-oauth-client-secret <s> | OAuth2 client secret | | --smtp-oauth-refresh-token <t> | OAuth2 refresh token |

Updating (binary)

# Back up the current binary before replacing it
cp qarote qarote.backup

# Stop the running instance
kill $(pgrep -f './qarote') 2>/dev/null || true

# Download latest release
PLATFORM="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/' | sed 's/aarch64/arm64/')"
curl -L "https://github.com/getqarote/Qarote/releases/latest/download/qarote-${PLATFORM}.tar.gz" | tar xz --strip-components=1

# Restart — migrations run automatically
./qarote

Your .env file is preserved. To roll back, restore the backup: mv qarote.backup qarote && ./qarote


Docker Compose

The Docker Compose setup bundles PostgreSQL, the backend, the alert worker, the notification worker, and the frontend. One command starts everything.

1. Clone the repository

git clone https://github.com/getqarote/Qarote.git qarote
cd qarote

2. Generate configuration

./setup.sh

This creates a .env file with secure random secrets. Alternatively, copy .env.selfhosted.example and fill in the values manually.

Key variables:

# Security — generate with: openssl rand -hex 64
JWT_SECRET=...
ENCRYPTION_KEY=...

# Ports
POSTGRES_PORT=5432
BACKEND_PORT=3000
FRONTEND_PORT=8080

# Frontend API URL (baked at build time — must match BACKEND_PORT)
# Changing this after the initial build requires rebuilding the frontend container:
#   docker compose -f docker-compose.selfhosted.yml build frontend && docker compose ... up -d
VITE_API_URL=http://localhost:3000

3. Start services

docker compose -f docker-compose.selfhosted.yml up -d

This starts five containers:

| Container | Purpose | | ---------------------------- | ----------------------------------- | | qarote_postgres | PostgreSQL 17 database | | qarote_backend | API server on port 3000 | | qarote_alert_worker | Alert evaluation worker | | qarote_notification_worker | Transactional emails (auth, Stripe) | | qarote_frontend | React frontend on port 8080 |

EE workers (licensed features)

With a Developer or Enterprise license, uncomment digest-worker, metrics-worker, and firehose-worker in docker-compose.selfhosted.yml to enable weekly digests, queue metrics, and message tracing. Keep metrics-worker and firehose-worker at exactly 1 replica — Docker Compose already enforces this; do not add deploy.replicas for these services.

4. Run database migrations

docker exec qarote_backend pnpm run db:migrate

5. Open the dashboard

Navigate to http://localhost:8080. Use the sign-up page to create your first account — this becomes the admin account. After that, you can disable public registration and invite team members via the Settings → Members page.

Ports

Frontend is served on FRONTEND_PORT (default 8080). The backend API is on BACKEND_PORT (default 3000). Both are configurable in .env.

Updating (Docker Compose)

git pull origin main
docker compose -f docker-compose.selfhosted.yml up -d --build
docker exec qarote_backend pnpm run db:migrate

Viewing logs

docker compose -f docker-compose.selfhosted.yml logs -f

Production: TLS with a reverse proxy

The Docker Compose stack listens on plain HTTP. For production, put a TLS-terminating reverse proxy in front of the stack. Caddy is the simplest option — it fetches and renews Let's Encrypt certificates automatically.

/etc/caddy/Caddyfile

your-domain.com {
  reverse_proxy localhost:8080
}

api.your-domain.com {
  reverse_proxy localhost:3000
}

After setting up the proxy, update VITE_API_URL in .env to your API domain and rebuild the frontend container:

docker compose -f docker-compose.selfhosted.yml build frontend
docker compose -f docker-compose.selfhosted.yml up -d

Tip

Bind the Docker Compose ports to localhost only so the services aren't directly reachable from the internet. In docker-compose.selfhosted.yml, change "${FRONTEND_PORT}:80" to "127.0.0.1:${FRONTEND_PORT}:80" (and similarly for the backend port).


Dokku

Dokku gives you git-push deploys, automatic HTTPS via Let's Encrypt, and managed PostgreSQL — on a server you control.

Why Dokku?

Dokku is a lightweight self-hosted PaaS. Push your code with git push dokku main and it rebuilds and redeploys automatically. The Postgres plugin handles backups and linking. Let's Encrypt via the dokku-letsencrypt plugin handles SSL.

1. Install Dokku

Follow the Dokku installation guide to set up Dokku on your server.

2. Create the app and database

# On your server (or via SSH)
ssh dokku@your-server apps:create qarote

sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
dokku postgres:create qarote-db
dokku postgres:link qarote-db qarote

DATABASE_URL is set automatically when you link the database.

3. Set environment variables

dokku config:set qarote \
  JWT_SECRET=$(openssl rand -hex 64) \
  ENCRYPTION_KEY=$(openssl rand -hex 64) \
  TZ=UTC \
  ENABLE_EMAIL=false

4. Deploy

From your local machine:

git remote add dokku dokku@your-server:qarote
git push dokku main

Dokku builds the app from the Procfile, runs the release phase (migrations), and starts the web, alert-worker, and notification-worker processes.

5. Run database migrations

dokku run qarote pnpm run db:migrate
dokku domains:set qarote your-domain.com
dokku letsencrypt:enable qarote

Scale the workers

dokku ps:scale qarote alert-worker=1 notification-worker=1
  • alert-worker — alert evaluation. Required for alerts to fire.
  • notification-worker — transactional emails (password reset, invitations, Stripe receipts). Required if ENABLE_EMAIL=true.

With a Developer or Enterprise license, scale these additional workers to enable premium features:

dokku ps:scale qarote digest-worker=1 metrics-worker=1 firehose-worker=1
  • digest-worker — weekly digest emails.
  • metrics-worker — queue metrics collection. Run as exactly 1 replica — multiple replicas write duplicate snapshot rows.
  • firehose-worker — message tracing. Run as exactly 1 replica — multiple replicas create duplicate consumers.

Updating (Dokku)

# Push latest — triggers rebuild and redeploy automatically
git push dokku main

# Apply any new migrations
dokku run qarote pnpm run db:migrate

SMTP configuration

Email features (password reset, invitations) are disabled by default. To enable them, set ENABLE_EMAIL=true and provide SMTP credentials.

SMTP can be configured three ways, in priority order:

  1. Admin UI — Settings → Email Settings (takes effect immediately, no restart)
  2. Setup wizard./qarote setup (binary) or ./setup.sh followed by editing .env (Docker Compose)
  3. Environment variables / dokku config:set

Common providers

Gmail (app password)

ENABLE_EMAIL=true
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password  # Not your Google password — generate at myaccount.google.com/apppasswords

SendGrid

ENABLE_EMAIL=true
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=your-sendgrid-api-key

Mailgun

ENABLE_EMAIL=true
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER=postmaster@your-domain.mailgun.org
SMTP_PASS=your-mailgun-smtp-password

Amazon SES

ENABLE_EMAIL=true
SMTP_HOST=email-smtp.us-east-1.amazonaws.com  # replace region as needed
SMTP_PORT=587
SMTP_USER=your-ses-smtp-username
SMTP_PASS=your-ses-smtp-password

Use OAuth2 for production

Gmail and Office 365 support OAuth2 authentication, which is more secure than app passwords and doesn't require storing credentials directly. See the Nodemailer OAuth2 guide for setup instructions.

Test your SMTP configuration

# Binary
./qarote test:smtp
./qarote test:smtp -- --send admin@yourcompany.com

# Docker Compose
docker exec qarote_backend pnpm run test:smtp
docker exec qarote_backend pnpm run test:smtp -- --send admin@yourcompany.com

# Dokku
dokku run qarote pnpm run test:smtp -- --send admin@yourcompany.com

SSO configuration

Qarote supports Single Sign-On via OIDC and SAML 2.0. SSO can be configured through the Admin UI (Settings → SSO Settings), the setup wizard (./qarote setup), or environment variables.

Key variables

| Variable | Description | Default | | ------------------------ | --------------------------------------------------------------------------- | ----------------------- | | SSO_ENABLED | Enable SSO | false | | SSO_TYPE | Protocol: oidc or saml | oidc | | SSO_OIDC_DISCOVERY_URL | OIDC discovery endpoint — must end with /.well-known/openid-configuration | — | | SSO_OIDC_CLIENT_ID | OIDC client ID | — | | SSO_OIDC_CLIENT_SECRET | OIDC client secret | — | | SSO_SAML_METADATA_URL | SAML IdP metadata URL | — | | API_URL | Backend URL — used for the SSO callback | http://localhost:3000 | | FRONTEND_URL | Frontend URL — used for the post-login redirect | http://localhost:8080 | | SSO_BUTTON_LABEL | Login button text | Sign in with SSO |

Register these URLs in your identity provider (Keycloak, Okta, Azure AD, Auth0, Google Workspace, etc.):

  • OIDC callback URL: {API_URL}/sso/callback
  • SAML ACS URL: {API_URL}/sso/acs

Binary

Pass SSO flags at startup, or run ./qarote setup to configure interactively:

./qarote \
  --sso-enabled true \
  --sso-oidc-discovery-url https://your-idp.com/.well-known/openid-configuration \
  --sso-oidc-client-id qarote \
  --sso-oidc-client-secret your-client-secret \
  --api-url https://api.your-domain.com \
  --frontend-url https://your-domain.com

Docker Compose

Add SSO variables to .env, then uncomment the SSO block in docker-compose.selfhosted.yml and restart the backend:

SSO_ENABLED=true
SSO_TYPE=oidc
SSO_OIDC_DISCOVERY_URL=https://your-idp.com/.well-known/openid-configuration
SSO_OIDC_CLIENT_ID=qarote
SSO_OIDC_CLIENT_SECRET=your-client-secret
API_URL=https://api.your-domain.com
FRONTEND_URL=https://your-domain.com

Dokku

dokku config:set qarote \
  SSO_ENABLED=true \
  SSO_TYPE=oidc \
  SSO_OIDC_DISCOVERY_URL=https://your-idp.com/.well-known/openid-configuration \
  SSO_OIDC_CLIENT_ID=qarote \
  SSO_OIDC_CLIENT_SECRET=your-client-secret \
  API_URL=https://api.your-domain.com \
  FRONTEND_URL=https://your-domain.com

Troubleshooting

"Exec format error" (binary only)

You downloaded the wrong architecture binary. Check your platform:

uname -m   # x86_64 → use -x64 | aarch64 → use -arm64

Re-download the correct variant — your .env is preserved.

Tip

Multipass VMs on Apple Silicon are ARM64. Use linux-arm64, not linux-x64.

Database connection refused

  • Verify PostgreSQL is running: systemctl status postgresql or docker compose ps postgres
  • Check the DATABASE_URL format: postgresql://user:password@host:port/database
  • Make sure migrations have been run
  • For Docker Compose, wait for the health check to pass before the backend starts

Message rate charts are blank

Your RabbitMQ rates_mode is set to basic (the default), which doesn't retain sample history.

Fix it in rabbitmq.conf:

management.rates_mode = detailed

Or apply at runtime without restarting:

rabbitmqctl eval 'application:set_env(rabbitmq_management, rates_mode, detailed).'

Services not starting (Docker Compose)

docker compose -f docker-compose.selfhosted.yml logs

Common causes: missing .env values, port conflicts on 3000/5432/8080, or insufficient disk space.

License or premium features not activating

  • Go to Settings → License and re-enter your key
  • Check that the key hasn't expired
  • Check backend logs for validation errors
  • Contact support@qarote.io if the issue persists

License activation

Premium features are activated through the UI — no env vars or restarts needed.

  1. Purchase a license at the Customer Portal
  2. Copy your license key (a signed JWT string)
  3. In Qarote, go to Settings → License
  4. Paste the key and click Activate

Features unlock immediately. Validation is cryptographic and offline — no network call to Qarote's servers.