Blog
A blameless postmortem template with summary, impact, timeline, root cause, contributing factors and owned action items, plus a filled example of a short outage.
An incident postmortem template is a fixed document structure your team fills in after every significant outage. It covers a summary, impact, timeline, root cause, contributing factors and action items with named owners. Written blamelessly, it turns a bad afternoon into concrete fixes instead of a search for whoever pushed the button. Below you will find a copy-paste template, an explanation of every section and a filled example for an 18-minute API outage.
This article is about the document you write after the incident is resolved. The messages you post to customers while it is still running are covered separately in how to write an incident status update.
A good template exists to answer four questions: what happened, how bad it was, why it was possible and what will change. Every section maps to one of them. If a section does not help answer one of these questions, drop it. Long templates get skipped, and short incidents then go undocumented.
The minimum set that works for small teams:
Blameless does not mean nobody made a mistake. It means you treat the mistake as a symptom of the system that allowed it. If one engineer could take production down with a single config change, the question is why the pipeline accepted that change, not why that engineer made it.
In practice this changes the wording. Compare:
The second version produces an action item. The first produces a more careful Marco and the same outage next quarter with someone else. A postmortem is only finished when every contributing factor points to a change in a system, process or tool, each owned by a named person with a date.
Keep it in your repository or wiki as Markdown so it can be reviewed like code.
# Postmortem: [short descriptive title]
Incident date: [YYYY-MM-DD]
Author(s): [names]
Status: draft | in review | final
Severity: [SEV1 | SEV2 | SEV3]
## Summary
[2-4 sentences: what broke, for how long, who was affected, what fixed it.]
## Impact
- Duration: [HH:MM] to [HH:MM] UTC ([n] minutes)
- Affected: [services, endpoints, regions, customer segments]
- User-visible effect: [errors, latency, delayed data, failed logins]
- Measured by: [monitors, error rate, logs, support tickets]
- SLO / SLA effect: [error budget used, credits owed if any]
## Timeline (UTC)
- HH:MM [event] (source: [alert, deploy log, chat, status page])
## Root cause
[The technical mechanism, specific enough that another engineer
could reproduce it.]
## Contributing factors
- [Condition that made the failure possible, larger, or slower to detect]
## Detection and response
- Time to detect: [n] min
- Time to mitigate: [n] min
- What worked:
- What slowed us down:
## Action items
- [ ] [Action] | type: prevent / detect / mitigate | owner: [name] | due: [date] | ticket: [link]
## Open questions
- [Anything still unexplained]Write it last, even though it comes first. Two to four sentences, readable by someone outside engineering: what customers experienced, the duration, the cause in plain terms and what restored service. If your support lead cannot paste it into a customer reply, it is too technical.
Use numbers you can actually back up, and name the source for each one. Duration should run from the first user-visible failure, not from when the alert fired. Those two moments differ, and the gap belongs in the detection section. If you track SLOs, state how much error budget the incident consumed; the definitions of SLO, error budget and MTTR are in the monitoring glossary if your team needs a shared vocabulary.
Only facts with timestamps, all in one time zone, ideally UTC. Pull them from systems rather than memory: alert history, deploy logs, chat messages and incident updates. Include the dead ends, such as the five minutes spent checking the database before anyone looked at the deploy. Those minutes are often where the best action items hide.
Describe the mechanism, not a label. Human error and misconfiguration are labels. The application started with the client library default pool size of 5 because the expected variable was missing is a mechanism. A useful test: could another engineer reproduce the failure from your description alone?
Real outages rarely have one cause. List everything that made the incident possible, made it worse or delayed recovery: a missing test, a health check that did not test the dependency, an alert threshold set too loose, a runbook that was out of date. Asking why repeatedly works, but stop when the answer leaves your control or turns into a person's name.
Each item needs exactly one owner, a due date and a ticket in your normal backlog. Tag each as prevent (stops recurrence), detect (finds it faster) or mitigate (limits the damage). A healthy postmortem usually has at least one of each. Avoid items like be more careful or improve monitoring; if you cannot tell when it is done, rewrite it.
The following is a fictional but realistic example for a small SaaS team.
# Postmortem: API errors after config refactor
Incident date: 2024-05-14
Author(s): Lena (on-call), Marco (backend)
Status: final
Severity: SEV2
## Summary
For 18 minutes, a large share of API requests failed with 502 and 504
errors. A deploy renamed the database pool setting, production still had
only the old variable set, and the app fell back to a pool of 5
connections. Rolling back the deploy restored service.
## Impact
- Duration: 14:06 to 14:24 UTC (18 minutes)
- Affected: public REST API and dashboard, all regions
- User-visible effect: 502/504 errors, slow page loads in the dashboard
- Measured by: proxy logs, API monitor, 11 support tickets
- SLO effect: part of the monthly availability error budget consumed
## Timeline (UTC)
- 14:03 Deploy v2.41.0 starts (source: CI log)
- 14:06 First 504s in proxy logs (source: proxy logs)
- 14:08 API monitor fails from 2 of 3 regions twice in a row
- 14:09 Alert to on-call via Slack and SMS
- 14:11 Lena acknowledges, sees error rate rising on dashboard
- 14:12 Health endpoint still returns 200, causes 3 min of doubt
- 14:13 Status page incident opened: Investigating
- 14:15 Error onset correlated with deploy time
- 14:17 Rollback to v2.40.2 started
- 14:21 Rollback complete, error rate dropping
- 14:24 Error rate at baseline, monitors green
- 14:40 Status page incident resolved after observation period
## Root cause
The config refactor in v2.41.0 replaced DB_POOL_SIZE with
DATABASE_POOL_MAX. Production only had DB_POOL_SIZE set. The client
library silently used its default of 5 connections. Under afternoon
load, requests waited for a connection until the proxy timed out.
## Contributing factors
- No startup validation for required config values
- Old variable still present, so nothing looked missing
- /health does not touch the database, so it stayed green
- Deploy went to 100% of instances at once
## Detection and response
- Time to detect: 3 min (first error to alert)
- Time to mitigate: 15 min (first error to rollback complete)
- What worked: multi-region API check, fast rollback script
- What slowed us down: green health check, no deploy marker on dashboards
## Action items
- [ ] Fail startup if DATABASE_POOL_MAX is unset | prevent | owner: Marco | due: 2024-05-21
- [ ] Remove DB_POOL_SIZE from all environments | prevent | owner: Marco | due: 2024-05-21
- [ ] Add DB check to /health/ready | detect | owner: Priya | due: 2024-05-24
- [ ] Alert on pool wait time above 100 ms for 2 min | detect | owner: Lena | due: 2024-05-24
- [ ] Canary deploys: 10% of instances for 10 min | mitigate | owner: Marco | due: 2024-06-07
- [ ] Show deploy markers on error-rate dashboard | detect | owner: Lena | due: 2024-05-28Notice what the example does not contain: no speculation about who should have noticed the variable, and no action item that depends on someone remembering something. The 14:12 entry about the green health check led directly to two detection items.
If you publish a customer-facing version, shorten it to summary, impact, cause in plain language and the preventive measures. Internal details such as pool sizes or instance counts can stay internal.
The timeline is the section that costs the most time, because the facts are scattered across tools. Two habits make it much faster: record the incident start automatically, and post updates into a system that keeps timestamps.
Any incident tool that logs start time, updates and resolution will help. In m0nitor, for example, incident management records when a disruption started, what was affected and every update until resolution, and alert workflows can open an incident automatically when a monitor fails. That gives you the detection and status page entries of the timeline without reconstructing them from chat. Deploy logs and application metrics still have to come from your own systems.
A root cause analysis is one part of a postmortem. The postmortem also documents impact, timeline, detection, response and action items, so it explains not just why the failure happened but how the team handled it and what will change.
Write one for every incident that meets a predefined trigger, typically customer-visible outages above a few minutes, data loss, security events and serious near misses. Draft it within a few working days so logs and memories are still available.
The internal version should stay complete and candid. Many teams publish a shorter customer-facing version for major incidents with a plain summary, the impact, the cause and the preventive steps, which builds trust without exposing internal details.
As long as needed to answer what happened, how bad it was, why it was possible and what will change. For a short outage like the example above, one to two pages is typical. If nobody reads it to the end, it is too long.