To write an incident response plan, decide what counts as an incident, sort incidents into severity levels, name who owns each decision, write a one-page runbook for each attack you are most likely to face, confirm the logs you will need actually exist, pre-approve containment actions, and test the whole thing with a tabletop exercise before you need it.
Think of it like a fire evacuation plan. Nobody reads a binder while the alarm is going off. What saves people is that the exits were chosen in advance, someone was already named to check each floor, and everyone walked the route once during a drill. An incident response plan works the same way. Its job is to move the decisions out of the worst hour of your year and into a calm afternoon, so that when something breaks, people are executing rather than debating.
Most templates online stop at the document. This guide goes one level lower, to the place plans usually fail: the link between the plan and the alerts that start it. We run managed Wazuh for customers, so the examples use real Wazuh rule IDs, a real containment config, and a traced incident from first alert to closed ticket.
What is an incident response plan, and what goes in it?
An incident response plan is a short written agreement about who does what when a security event turns out to be real. It covers scope, severity, roles, contacts, scenario runbooks, evidence handling, communications, and how the plan gets tested and updated. A plan that only lists phases, without names and thresholds, is a policy, not a plan.
The reference most teams start from is NIST. Its incident response guide, SP 800-61 Revision 3, was published in April 2025 and replaced the 2012 Revision 2. The older edition is the one most templates still copy, with its four-phase cycle: preparation; detection and analysis; containment, eradication, and recovery; and post-incident activity. Revision 3 recasts the guidance as a Community Profile of the NIST Cybersecurity Framework (CSF) 2.0, which treats incident response as part of everyday risk management instead of a separate emergency drill. In plain terms: the plan should connect to how you already run and monitor systems, not sit in a folder.
| Section | What it answers | The test it must pass |
|---|---|---|
| Scope and definitions | Which systems and data are covered, and what an "incident" is | Two people classify the same event the same way |
| Severity matrix | How bad is it, and how fast do we move | Every level has a response time and a named owner |
| Roles and contacts | Who leads, who decides, who talks to whom | Reachable when company email and chat are down |
| Scenario runbooks | The first concrete steps for your likely attacks | A new hire could start step one alone |
| Evidence and logging | What records exist and how long they are kept | Retention is longer than a realistic detection delay |
| Containment authority | Who may block, disable, or isolate without a meeting | Written down before the incident, not during |
| Communications | Customers, regulators, insurers, staff | Legal deadlines are listed with their clock start |
| Testing and review | When the plan is exercised and updated | Dated proof of the last exercise |
How do you write an incident response plan, step by step?
Write it in the order below, because each step depends on the one before it. You cannot set response times until you have severity levels, and you cannot write runbooks until you know who is allowed to act.
Step 1: Define an incident and set severity levels
Start with one sentence that defines an incident for your organization, for example: "any event that threatens the confidentiality, integrity, or availability of customer data or production systems." Then create three or four severity levels, each with a response time and an owner.
Here is where most plans go vague. They say "monitor the SIEM" and stop. A SIEM, or security information and event management system, is the tool that collects logs from your servers and raises alerts (our plain-English SIEM explainer covers it). Wazuh scores every alert on a scale from 0 to 15, as described in the Wazuh rule classification table: level 10 covers repeated failed logins, level 11 integrity warnings such as a modified system binary, and level 15 a severe attack. Tie your severity levels to those numbers so the plan starts from the same signal your monitoring produces.
| Severity | Typical trigger | Response time | Who is involved |
|---|---|---|---|
| SEV-1 Critical | Confirmed compromise of production or customer data; alert level 12 to 15 that a human confirms | Page now, start within 15 minutes | Incident lead, system owner, management, legal |
| SEV-2 High | Likely compromise of one host or account, such as a login that succeeds right after a brute-force burst | Within 1 hour | Incident lead, system owner |
| SEV-3 Medium | Attack attempts with no sign of success; level 10 brute force that was blocked | Same business day | On-call engineer |
| SEV-4 Low | Isolated alerts below level 10 with no follow-on activity | Weekly review | Whoever tunes detections |
Note the SEV-2 row. A successful SSH login is only level 3 in Wazuh, a routine "authorized event." It becomes the most important line in your logs when it arrives from the same address that just failed forty times. Severity is about context, and your matrix should say so.
Step 2: Name the roles and build a contact sheet that works offline
Every incident needs one incident lead who makes decisions, even in a team of three. Name a primary and a backup for that role, plus an owner for each critical system and one person who handles outside communication.
Put phone numbers on the sheet, not just chat handles. If an attacker is inside your email tenant, the incident cannot be coordinated on that email. Keep a printed copy and a copy outside your main cloud account.
Step 3: Write one-page runbooks for your likely incidents
Pick the four or five incidents you are actually likely to face, not the full catalog. For a small company running Linux servers, that is usually a compromised account, ransomware, a compromised web application, a lost or stolen laptop, and accidental data exposure. Each runbook gets a trigger, the first five actions, and the condition for escalating.
Here is a skeleton you can paste into your wiki and fill in:
# Incident Response Plan (v1, owner: NAME, last tested: DATE) ## 1. Scope Systems: ... Data: ... Out of scope: ... ## 2. Definition An incident is ... ## 3. Severity SEV-1 | trigger | 15 min | lead + owner + mgmt + legal SEV-2 | trigger | 1 h | lead + owner SEV-3 | trigger | same day | on-call SEV-4 | trigger | weekly | detection owner ## 4. Roles and contacts (phone first) Incident lead: NAME, PHONE Backup: NAME, PHONE Comms: NAME, PHONE Legal/insurer: NAME, PHONE ## 5. Runbooks 5.1 Compromised account 5.2 Ransomware 5.3 Web app compromise 5.4 Lost device 5.5 Data exposure ## 6. Pre-approved containment Block source IP: on-call, no approval needed Disable user account: on-call, tell lead within 15 min Isolate production host: lead only ## 7. Evidence Log sources, retention period, where copies go, who holds them ## 8. Notification deadlines Regulator, clock start, owner ## 9. Testing Next tabletop: DATE Review after every SEV-1 or SEV-2
Step 4: Make sure the evidence will exist
A plan is only as good as the records behind it. Before you finalize anything, check three things: which systems actually send logs, how long those logs are kept, and whether a copy lives somewhere an attacker on the affected host cannot delete.
Retention is the quiet failure. Intruders often go unnoticed for weeks, so if your alerts are kept for 30 days and the first sign of trouble is a month old, the trail you need is already gone. On a Wazuh server, alerts are written to /var/ossec/logs/alerts/alerts.json and rotated into dated archives under the same directory. Check what you really have:
sudo ls /var/ossec/logs/alerts/ sudo du -sh /var/ossec/logs/alerts/
Also check how long the Wazuh indexer, the searchable store behind the dashboard, keeps alert indices. That is set by an index lifecycle policy you configure, so write the real number into the plan rather than an assumption.
Step 5: Pre-approve containment actions
Decide now which actions the on-call person may take without asking anyone. Blocking an attacking IP address is low risk and should be automatic. Disabling an account is medium risk. Pulling a production server offline is a business decision, so it belongs to the incident lead.
Wazuh can carry out the low-risk tier for you through active response, which runs a script on the affected host when a rule fires. The firewall-drop command ships in the default server configuration. This block, added to /var/ossec/etc/ossec.conf on the Wazuh server, blocks any address that trips the two SSH brute-force rules for ten minutes (option reference: Wazuh active response settings):
<ossec_config> <active-response> <disabled>no</disabled> <command>firewall-drop</command> <location>local</location> <rules_id>5712,5763</rules_id> <timeout>600</timeout> </active-response> </ossec_config>
Rule 5712 fires on repeated logins with a username that does not exist, and rule 5763 on repeated failed passwords, each after eight matching failures from one address within two minutes, per the rule definitions. Add your office and VPN addresses as <white_list> entries in the <global> section of the same file so you never block yourself, then restart the manager with sudo systemctl restart wazuh-manager. For when automation helps and when it hurts, see using Wazuh for endpoint response.
Step 6: Write down communications and legal deadlines
List every party you may have to tell, and the deadline for each. If you handle personal data of people in the EU, Article 33 of the GDPR requires you to notify the supervisory authority of a personal data breach within 72 hours of becoming aware of it, where feasible. Your cyber insurer, contracts with customers, and sector rules may set their own clocks. Write who owns each notification, and keep a holding statement drafted so nobody writes one from scratch at 3 a.m.
Step 7: Test it with a tabletop exercise
A tabletop exercise is a meeting where you walk a made-up incident through the plan, out loud, step by step. It takes about an hour, and it finds the gaps cheaply: the backup lead who left the company, the runbook that assumes access nobody has. Run one before you call the plan finished, then at least once a year and after every SEV-1 or SEV-2.
A worked example: tracing one alert through the plan
Here is the exact scenario we use as a first tabletop, because it exercises severity, containment, and evidence in under an hour. It is an illustrative alert built on real Wazuh rule IDs.
1. The alert. At 03:14 the Wazuh server raises rule 5763 against a web server called web-01. A trimmed copy of the alert looks like this:
{ "timestamp": "2026-10-27T03:14:09.512+0000", "rule": { "level": 10, "id": "5763", "description": "sshd: brute force trying to get access to the system. Authentication failed.", "mitre": { "id": ["T1110"], "technique": ["Brute Force"] } }, "agent": { "id": "003", "name": "web-01" }, "data": { "srcip": "203.0.113.45", "dstuser": "deploy" } }
The MITRE ATT&CK tag T1110 names the technique, password guessing (our MITRE ATT&CK explainer covers how those tags work). On its own this is SEV-3: an attempt, already blocked by the active response from Step 5.
2. The question that changes severity. The runbook's first action is "check whether any login from this address succeeded." It uses jq, a JSON filter (apt install jq or dnf install jq if missing); for older incidents, pipe the dated archives from Step 4 through zcat into the same filter. On the Wazuh server:
sudo jq -c 'select(.rule.id=="5715" and .data.srcip=="203.0.113.45") | {timestamp, host: .agent.name, user: .data.dstuser}' /var/ossec/logs/alerts/alerts.json{"timestamp":"2026-10-27T03:13:51.204+0000","host":"web-01","user":"deploy"}Rule 5715 is a successful SSH login. One line came back, eighteen seconds before the brute-force alert. The attacker got in as deploy before the block landed. Per the matrix, this is now SEV-2: page the incident lead.
3. Contain. Disabling an account is pre-approved for on-call, so the engineer locks it on web-01 and confirms the block is in place:
sudo usermod --lock --expiredate 1 deploy sudo pkill -KILL -u deploy sudo iptables -S | grep 203.0.113.45
The Wazuh server also records rule 651, "Host Blocked by firewall-drop Active Response," which proves the automatic step ran and when.
4. Preserve evidence. Before anyone cleans up, copy the records off the host and fingerprint them, so you can later show they were not altered:
sudo mkdir -p /root/ir-20261027 sudo cp /var/log/auth.log /home/deploy/.bash_history /home/deploy/.ssh/authorized_keys /root/ir-20261027/ sudo sh -c 'cd /root/ir-20261027 && sha256sum * > SHA256SUMS'
On RHEL-family hosts such as AlmaLinux, the SSH log is /var/log/secure instead of /var/log/auth.log. Then move that folder to storage outside the server. Our test usually finds the gap right here: there is no agreed place for evidence, so the runbook gets a line naming one.
5. Decide and close. The lead reviews the shell history and finds a new key in authorized_keys. That is persistence, so the host is rebuilt from a known-good image rather than cleaned, the password policy for service accounts changes, and the ticket closes with a short review: what fired, what we did, and the one change to the plan.
Where incident response plans break
Plans rarely fail because a section is missing. They fail at the seams between the document and the real systems. These are the failures we see most:
-
The plan lives on the system that is down. A plan stored only in the company wiki is unreadable during a ransomware event that took the wiki with it.
-
Severity follows the alert number blindly. As the example showed, the dangerous event was a level 3 success, not the level 10 failure. A matrix keyed only to alert levels sends the real incident to the weekly review.
-
Automatic blocking catches the wrong address. If your servers sit behind a load balancer or a content delivery network, the address Wazuh sees may be the proxy, and blocking it takes down your own traffic. Test active response against your real network path before you trust it.
-
Logs exist on the box the attacker controls. An intruder with root can delete local logs. The copy that matters is the one already shipped to a separate server.
-
Nobody retests after the team changes. The named backup lead leaves, and the plan silently loses its second pair of hands. Put "last tested" on the first line so a stale plan is obvious.
Noisy alerts also train people to ignore pages, so test your rules with wazuh-logtest before a plan depends on them. If you would rather not run the detection side yourself, our incident response page explains how we approach it.
How long should an incident response plan be?
For a small or mid-sized company, ten to fifteen pages is plenty: a few pages for scope, severity, roles, and contacts, then one page per runbook. If it runs past thirty pages, people will not read it under pressure. Short and tested beats long and untouched every time.