Grafana OnCall is an on-call management tool that runs as a Grafana plugin: it receives alerts from Grafana Alerting, Alertmanager or any webhook, groups them, and pages the right person according to schedules and escalation chains. In this tutorial you will deploy the open source edition of OnCall with Docker Compose on Ubuntu 24.04, connect it to a Grafana instance, create an on-call schedule and an escalation chain, and fire a test alert through a webhook integration.

Prerequisites

To follow this tutorial you need:

  • A server running Ubuntu 24.04 LTS with at least 2 vCPUs and 4 GB of RAM, for example a CubePath VPS.
  • A non-root user with sudo privileges.
  • Docker Engine and the Docker Compose plugin installed from Docker's official repository.
  • SSH access from your workstation, used to reach the web interfaces through a tunnel.

Step 1 - Preparing the deployment directory

OnCall's "hobby" deployment is a single Compose file that starts the OnCall engine, a Celery worker, Redis, a database migration job and, optionally, a Grafana container with the OnCall plugin preinstalled. Create a directory for it:

sudo mkdir -p /opt/oncall
sudo chown "$USER": /opt/oncall
cd /opt/oncall

Download the Compose file from the (archived) OnCall repository:

curl -fsSL https://raw.githubusercontent.com/grafana/oncall/dev/docker-compose.yml -o docker-compose.yml

Open it and read through the services before running anything:

less docker-compose.yml

You will see the engine, celery, redis, oncall_db_migration and grafana services. The grafana service belongs to the with_grafana Compose profile, so it only starts when that profile is enabled.

Step 2 - Configuring the environment file

OnCall reads its settings from a .env file next to the Compose file. SECRET_KEY signs sessions and tokens and must be at least 32 characters long, so generate a random one:

openssl rand -hex 32

Create the environment file:

nano .env

Add the following, replacing the secret key with the value you just generated and your_strong_password with a password for the Grafana admin account:

DOMAIN=http://localhost:8080
COMPOSE_PROFILES=with_grafana
SECRET_KEY=paste_the_generated_hex_string_here
GRAFANA_USER=admin
GRAFANA_PASSWORD=your_strong_password

DOMAIN is the URL where the OnCall engine is reachable; it is used to build the integration URLs shown in the UI. Because you will reach everything through an SSH tunnel in this tutorial, http://localhost:8080 is correct. If you later expose OnCall behind a reverse proxy, change it to the public HTTPS URL.

Restrict the file, since it contains secrets:

chmod 600 .env

Step 3 - Starting the OnCall stack

Pull the images and start the services in the background:

docker compose pull
docker compose up -d

The migration container runs first and exits when the database schema is ready; the engine and worker wait for it. Check the state after a minute:

docker compose ps
NAME                IMAGE                    SERVICE    STATUS
oncall-celery-1     grafana/oncall           celery     Up 50 seconds
oncall-engine-1     grafana/oncall           engine     Up 50 seconds
oncall-grafana-1    grafana/grafana          grafana    Up 55 seconds
oncall-redis-1      redis                    redis      Up 58 seconds (healthy)

The oncall_db_migration container is not listed because it has already finished. If the engine keeps restarting, read its logs:

docker compose logs --tail=50 engine

A missing or too short SECRET_KEY is the most common cause of a failing engine.

Step 4 - Opening Grafana through an SSH tunnel

The Compose file publishes Grafana on port 3000 and the engine on port 8080. Docker adds its own iptables rules for published ports, so UFW does not block them. Rather than exposing an unmaintained application to the internet, reach it through an SSH tunnel. Run this on your workstation, replacing your_user and your_server_ip:

ssh -L 3000:localhost:3000 -L 8080:localhost:8080 your_user@your_server_ip

Keep that session open and browse to http://localhost:3000. Log in with the GRAFANA_USER and GRAFANA_PASSWORD values from .env.

Step 5 - Connecting the OnCall plugin to the engine

The Grafana container already has the grafana-oncall-app plugin installed, but the plugin needs to know where the OnCall backend lives. In Grafana, go to Administration > Plugins and data > Plugins, search for OnCall, open it and select the Configuration tab.

Set OnCall backend URL (called OnCall API URL in older plugin versions) to:

http://engine:8080

This is the engine's address on the internal Compose network, as seen from the Grafana container, not from your browser. Click Connect. When the connection succeeds, OnCall appears under Alerts & IRM in the left menu.

If you run your own Grafana instead of the bundled one, remove with_grafana from COMPOSE_PROFILES, install the plugin with grafana cli plugins install grafana-oncall-app, restart Grafana, and use a backend URL that your Grafana server can reach, such as http://your_server_ip:8080.

Step 6 - Creating an on-call schedule

A schedule answers the question "who is on call right now". Before creating one, make sure each responder has a Grafana user; OnCall syncs users from Grafana automatically.

  1. Go to Alerts & IRM > OnCall > Schedules and click + New schedule.
  2. Choose Set up on-call rotation schedule (a schedule edited in the web UI). The other types import an iCal calendar or are managed through the API or Terraform.
  3. Give it a name such as Primary on-call, set the time zone and save it.
  4. Click + Add rotation, pick the users who take turns, choose the shift length (for example, one week, starting Monday 09:00) and save.

The schedule view now shows the rotation layers and a Final schedule row, which is what OnCall actually uses after applying overrides. To cover vacations, click + Add override on the schedule and assign a colleague for the affected time range; overrides take priority over rotations.

Step 7 - Building an escalation chain

An escalation chain defines what happens after an alert group is created and nobody reacts. Go to Alerts & IRM > OnCall > Escalation chains, click + New escalation chain and name it, for example Critical.

Add steps in this order:

StepTypeSetting
1Notify users from on-call schedulePrimary on-call
2Wait5 minutes
3Notify users from on-call schedule (important)Primary on-call
4Wait10 minutes
5Notify usersYour team lead

The chain stops as soon as someone acknowledges or resolves the alert group. "Important" notifications use each user's important notification policy, which they configure in their own OnCall profile under Users > View my profile. Without the Cloud Connection, the usable channels on OSS are mobile app push (where still supported), email if SMTP is configured, and the chat integrations you set up yourself.

Step 8 - Creating a webhook integration and routing alerts

Integrations are the entry points for alerts. Each one gets its own URL. Go to Alerts & IRM > OnCall > Integrations, click + New integration, choose Webhook, name it Test webhook and create it.

The integration page shows its URL, similar to:

http://localhost:8080/integrations/v1/webhook/AbCdEf123456/

Under Routes, the default route catches everything. Set its escalation chain to Critical. To send only some alerts there, add a route above it with a Jinja2 condition that evaluates the incoming JSON payload, for example:

{{ payload.severity == "critical" }}

Routes are evaluated top to bottom and the first match wins; the default route handles anything left over.

Now send a test alert from the server. Replace the URL with your integration URL:

curl -X POST http://localhost:8080/integrations/v1/webhook/AbCdEf123456/ \
  -H "Content-Type: application/json" \
  -d '{"title": "Disk almost full on web-01", "severity": "critical", "message": "Root filesystem is 95% used"}'

The engine accepts any JSON body on a webhook integration and answers with HTTP 200. Open Alerts & IRM > OnCall > Alert groups. A new alert group appears in the Firing state, and the user on shift in Primary on-call receives the first notification. Click Acknowledge and then Resolve to close it and stop the escalation.

Step 9 - Sending Grafana Alerting notifications to OnCall

In most setups the alerts come from Grafana Alerting. Create an integration of type Grafana Alerting in OnCall the same way as in Step 8. OnCall then creates a matching contact point in Grafana automatically.

Go to Alerts & IRM > Alerting > Notification policies and point the default policy, or a nested policy that matches labels such as severity=critical, at the OnCall contact point. From then on, firing Grafana alert rules create alert groups in OnCall and follow the routes and escalation chains you defined.

To check the path end to end, open Alerting > Contact points, edit the OnCall contact point and click Test. A test alert group appears in OnCall within a few seconds.

Troubleshooting

The plugin reports that it cannot connect to the backend. The backend URL is resolved from the Grafana server, not from your browser. With the bundled Grafana it must be http://engine:8080. Confirm the engine is running with docker compose ps and check docker compose logs engine for errors.

Alert groups are created but nobody is notified. Notifications are delivered by the Celery worker. Check it with docker compose logs --tail=100 celery, and confirm that the route uses an escalation chain and that the schedule has someone on shift at that moment.

Integration URLs show the wrong host. They are built from DOMAIN in .env. Fix the value and recreate the containers with docker compose up -d --force-recreate.

Conclusion

You deployed Grafana OnCall OSS with Docker Compose, connected the plugin to its engine, and built the full paging path: an integration receives an alert, a route selects an escalation chain, and the chain notifies whoever the schedule says is on call. Given the project's archived status, keep the deployment private and plan a migration path. Useful next steps are putting Grafana behind Nginx with HTTPS, wiring Prometheus Alertmanager to an OnCall integration, and managing schedules and escalation chains as code with the Grafana Terraform provider.