{% extends "base.html" %} {% block title %}{{ page.title }} · status page{% endblock %} {% block head %} {% endblock %} {% block content %} {% set ov_cls = {'operational': 'ok', 'degraded': 'warn', 'down': 'err', 'maintenance': 'info', 'pending': ''}[overall] %}
Status pages / {{ page.title }}

{{ page.title }}

{{ overall_text }} · {{ base_host }}/s/{{ page.slug }}{% if page.custom_domain %} · {{ page.custom_domain }}{% endif %} {% if not page.is_public %}hidden{% elif page.password_hash %}password protected{% else %}public{% endif %}
View public page {% if can_edit %}
{% endif %}
Components
{{ page.monitor_ids|length }}
{{ groups|length }} group{% if groups|length != 1 %}s{% endif %}
Subscribers
{{ subs|length }}
{{ sub_counts.slack }} Slack · {{ sub_counts.discord }} Discord · {{ sub_counts.webhook }} webhook
Open incidents
{{ incidents|selectattr('resolved_at', 'none')|list|length }}
{{ incidents|length }} in the last 30 days
Visibility
{% if not page.is_public %}Hidden{% elif page.password_hash %}Private{% else %}Public{% endif %}
{% if page.hide_branding and plan.hide_branding %}branding hidden{% else %}Vigil branding shown{% endif %}
Settings Components Custom CSS Subscribers {{ subs|length }} Embed Incidents{% set open_n = incidents|selectattr('resolved_at', 'none')|list|length %}{% if open_n %} {{ open_n }}{% endif %}

Page settings

{{ base_host }}/s/{{ page.slug }}
Shown at 30px tall next to the title. Use a PNG or SVG with a transparent background.
Add a DNS CNAME record for status pointing to {{ base_host }}{% if server_ip %} (or an A record to {{ server_ip }}){% endif %}. HTTPS is issued automatically within a minute of the first visit.{% if not plan.custom_domain %} Upgrade to use your own domain.{% endif %}
Private pages ask visitors to log in. The private link below lets clients or auditors view without an account; badges, feeds and the widget need that link too.
People outside your workspace unlock the page once per browser (30 days).
{% if private_link and page.members_only %}
Badges, feeds, status.json and the widget also work with this key. Regenerate it below if it leaks.
{% endif %}
Hide "Powered by Vigil" {% if not plan.hide_branding %}Pro{% endif %}Remove our footer credit from the public page.
Show response timesDisplay each component's 24h average response time.
{% if page.password_hash and can_edit %}
{% endif %}

Live preview

{{ overall_text }}
{% if page.monitor_ids %}
{% for mid in page.monitor_ids %}{% if mid in monitor_names %}
{{ monitor_names[mid] }}
{{ state_label[states[mid]] }}
{% endif %}{% endfor %}
{% else %}

No components yet. Add some under Components.

{% endif %}
status badge
Maintenance windows from the Maintenance page show up automatically under "Scheduled maintenance", and components under an active window display as Maintenance instead of Outage.

Components & groups

Group related monitors (e.g. "APIs", "Website", "Background jobs"). Groups appear in this order; a monitor can only be in one group. Unchecked monitors are not shown.

{% if monitors %}
{% for g in groups %}{% set gi = loop.index0 %}
{% for m in monitors %}{% endfor %}
{% endfor %}

Ungrouped components

Shown after the groups, without a heading.
{% for m in monitors %}{% endfor %}
{% else %}

Create some monitors first, then pick which ones to show here.

{% endif %}

Custom CSS

Injected after the default stylesheet. @import, expression(), javascript: and non-https url() values are stripped.

Useful selectors

:root
Theme variables: --bg, --panel, --text, --muted, --brand, --ok, --warn, --err, --line
body.status-page
Page background; also carries the overall state class (.operational, .down…)
nav.top .logo
Title and logo
.hero-s
Overall status banner
.grp .gh
Group headers
.mrow, .days
Component rows and 90-day bars
.inc
Incident cards
footer
Footer links
/* light theme example */
:root{--bg:#f6f7fb;--panel:#fff;--panel2:#f1f3f9;--text:#111827;--muted:#5b6478;--line:#e4e7ef;--line2:#d5dae6;--bg2:#fff}

Subscribers

{{ sub_counts.slack }} Slack{{ sub_counts.discord }} Discord{{ sub_counts.webhook }} webhook

Visitors subscribe from the public page; each gets a message when an incident is opened, updated or resolved. Webhooks receive JSON {event, page, incident}.

{% if subs %}
{% for s in subs %}{% endfor %}
TypeTargetComponentsSince
{{ s.kind }} {{ s.target[:64] }}{% if s.target|length > 64 %}…{% endif %} {% if s.monitor_ids %}{% for mid in s.monitor_ids %}{{ monitor_names.get(mid, '?') }}{% if not loop.last %}, {% endif %}{% endfor %}{% else %}All{% endif %} {{ s.created_at|timeago }} {% if can_edit %}
{% endif %}
{% else %}

No subscribers yet. Share the subscribe form on your public page.

{% endif %}
{% set key_q = ('?key=' ~ page.access_token) if (page.password_hash and page.access_token) else '' %}

Status badge

Overall status, refreshed every minute. Drop it in a README or footer.

status badge
<a href="{{ base_url }}/s/{{ page.slug }}{{ key_q }}"><img src="{{ base_url }}/s/{{ page.slug }}/badge.svg{{ key_q }}" alt="status"></a>
[![status]({{ base_url }}/s/{{ page.slug }}/badge.svg{{ key_q }})]({{ base_url }}/s/{{ page.slug }}{{ key_q }})

Per-component badges

{% if page.monitor_ids %}{% for mid in page.monitor_ids %}{% if mid in monitor_names %}
{{ monitor_names[mid] }}{{ base_url }}/s/{{ page.slug }}/badge/{{ mid }}.svg{{ key_q }}
{% endif %}{% endfor %}{% else %}

Add components first.

{% endif %}

Website widget

A small floating pill in the bottom-right corner of your site showing live status and linking to this page. Vanilla JS, ~1 KB, no dependencies.

<script src="{{ base_url }}/s/{{ page.slug }}/widget.js{{ key_q }}" async></script>

Feeds & API

Atom feed
{{ base_url }}/s/{{ page.slug }}/feed.xml{{ key_q }}
Status JSON
{{ base_url }}/s/{{ page.slug }}/status.json{{ key_q }}
JSON returns {overall, updated_at, components:[{name, state}]} with CORS enabled, so you can read it from any website.{% if page.password_hash %} Private pages need the ?key= parameter on every URL above.{% endif %}

Post an incident or maintenance

{% if page.monitor_ids %}
{% for mid in page.monitor_ids %}{% if mid in monitor_names %}{% endif %}{% endfor %}
Marks those components as affected on the page. Leave all unchecked for a page-wide notice.
{% endif %}
Subscribers are notified immediately.

Incidents

{% if incidents %}{% for i in incidents %}
{{ i.title }} {{ 'resolved' if i.resolved_at else i.status }}{% if not i.manual %} auto{% endif %}{% if i.kind == 'maintenance' %} maintenance{% endif %} {% set aff = ([i.monitor_id] if i.monitor_id else []) + (i.monitor_ids or []) %}{% if aff %}
{% for mid in aff %}{{ monitor_names.get(mid, '?') }}{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}
{{ i.started_at|timeago }}{% if i.scheduled_end and not i.resolved_at %} · until {{ i.scheduled_end|dt }}{% endif %}
{% if i.updates %}
    {% for u in i.updates|reverse %}
  • {{ u.status }}{% if u.message %} — {{ u.message }}{% endif %} {{ (u.at or '')[:16]|replace('T', ' ') }}
  • {% endfor %}
{% elif i.cause %}
{{ i.cause[:200] }}
{% endif %} {% if can_edit %} {% if not i.resolved_at and i.manual %}
{% elif i.resolved_at %}
{% if i.postmortem %}Edit postmortem{% else %}Add postmortem{% endif %}
{% endif %} {% endif %} {% if i.postmortem %}
Postmortem
{{ i.postmortem }}
{% endif %}
{% endfor %}{% else %}

Nothing yet. Automatic incidents from monitors listed on this page appear here and on the public page without any action.

{% endif %}
{% endblock %} {% block scripts %} {% endblock %}