Unprotected EC2 instances: the basics
What does "unprotected" actually mean for an EC2 instance?
An EC2 instance is "protected" when AWS Backup (or an equivalent system) is taking regular, retained snapshots of the machine. AWS Backup does this by creating an AMI of the instance plus EBS snapshots of every attached volume, captured atomically as a single recovery point. From that recovery point you can launch a new instance that is, for all practical purposes, the same machine: same OS, same packages, same data, same network and IAM configuration.
An "unprotected" instance has no backup plan covering it. Snapshots aren't being taken, or they're being taken on someone's old cron job with no retention policy, or they exist only as one-off AMIs from a deploy six months ago. When the volume corrupts, the AZ goes down, ransomware encrypts the disk, or someone runs terraform destroy against the wrong workspace, there's nothing to restore from.
Continuity check COV-001 ("Unprotected EC2 Instances") cross-references every running EC2 instance against the list of resources covered by an AWS Backup plan. Anything running for more than a configurable threshold without a recent recovery point fails the check. Severity is HIGH because the cost of finding out you have no backups is always paid at the worst possible moment.
In this lesson you'll learn what an EC2 instance backup actually contains, when an instance-level backup is the right call versus an EBS-only snapshot, how to detect coverage gaps across your fleet, and how to bring an unprotected instance under a backup plan using tag-based selection. You'll see the exact CLI calls for the gap-detection query, the tagging step, and a verification check that the next backup window catches the change.
The AMI restore that took 47 minutes
A 2024 internal report from a mid-size SaaS company described restoring a production EC2 instance from an AWS Backup recovery point during a real outage. The AMI registration took 4 minutes; copying snapshots into the new instance's volumes took 38 minutes; the instance booted, mounted, and joined the load balancer in another 5. Total: 47 minutes of customer-visible downtime against an SLO of 30. The fix wasn't more backups; it was pairing AMI-level recovery with EBS-only snapshots for the data volume, so the next time they could attach the data volume to a warm spare in under 90 seconds.
Closing the coverage gap in action
Marco is the SRE on call when COV-001 fires for the production account: 14 running EC2 instances with no AWS Backup coverage, 3 of them flagged HIGH because they're tagged Environment=prod. The flagged set includes the application server hosting a long-lived rules engine, which is exactly the kind of pet that needs to be recoverable.
He doesn't tag and walk away. First he wants to know which instances are actually unprotected versus which ones are covered by a backup plan he forgot about. AWS Backup's list-protected-resources endpoint is the source of truth for that: anything not in there isn't being backed up, regardless of what the tags say.
He starts by cross-referencing describe-instances against list-protected-resources, scoped to EC2.
First, build the coverage gap query: every running instance minus everything AWS Backup currently considers protected.
The set-difference between running instances and protected resources is the coverage gap.
Tag the flagged production instance for inclusion. The backup plan's selection rule picks up anything with BackupRequired=true at the next plan run.
Tag-based selection means coverage scales with the fleet, not with engineering hours.
What an EC2 backup actually capturesdeep dive
When AWS Backup runs against an EC2 instance, it does two things atomically: it registers a new AMI representing the instance (capturing instance type, network configuration, IAM role attachments, user data, and the root device mapping) and it triggers an EBS snapshot for every attached volume. The AMI and the snapshots are tied together as a single recovery point in the vault. Restore that recovery point and AWS launches a brand-new instance, attaches restored volumes, and brings it up with the original instance metadata intact.
Instance-level backups are crash-consistent by default: AWS pauses I/O briefly and snapshots the EBS volumes as if the machine had hard-stopped at that moment. Filesystems handle this fine on restore (they replay their journals), but applications with in-memory state (most relational databases, anything caching writes) do not. AWS Backup's native path to application-consistent EC2 backups is Windows VSS: with the SSM Agent installed, it invokes the AWSEC2-CreateVssSnapshot Run Command to coordinate the Windows VSS writers (flush buffers, freeze I/O, snapshot, thaw) without any custom scripts. For Linux databases there is no native AWS Backup pre/post-script hook; that capability (SSM pre/post-script templates for mysql FLUSH TABLES WITH READ LOCK, pg_start_backup, and the like) belongs to Amazon Data Lifecycle Manager, a separate service.
The downside is restore time. An AMI-based instance restore re-registers the AMI (a few minutes), launches a new EC2 instance from it, and waits for the volumes to be ready. End-to-end is typically 10-30 minutes depending on volume size and AZ. If all you need is the data on one volume (not the whole machine) restoring just the EBS snapshot and attaching it to an existing instance is dramatically faster, often under two minutes. Real recovery plans use both: AMIs for whole-machine DR, EBS-only snapshots for routine "oops we deleted a directory" recoveries.
# Inspect what's actually inside an EC2 recovery point.
aws backup describe-recovery-point \
--backup-vault-name prod-daily-30d \
--recovery-point-arn arn:aws:ec2:eu-west-1::image/ami-0abcd1234ef567890 \
--query '{Created:CreationDate,Size:BackupSizeInBytes,Status:Status,Lifecycle:Lifecycle}'
# List every EBS snapshot tied to the same recovery point.
aws ec2 describe-snapshots \
--filters Name=tag:aws:backup:source-resource,Values=i-0a1b2c3d4e5f60111 \
--query 'Snapshots[].{Id:SnapshotId,Vol:VolumeId,Size:VolumeSize,Started:StartTime}' \
--output table What is the impact of leaving an instance unprotected?
The direct impact is binary: when a recovery event happens, either you have a recent recovery point or you don't. Volume corruption from a kernel bug, an AZ-wide power event, a ransomware payload, or an operator running terraform destroy against the wrong account: all of these have the same recovery path if there's no backup, which is to rebuild from scratch and reconcile data from whatever logs and replicas survived. For a stateful app server with local config and on-disk state, that's hours to days of engineering time and customer-visible downtime.
The second-order impact is decision pressure during the incident. Without a backup, the incident commander is choosing between options like "restore from a four-month-old AMI we made for a deploy," "replay traffic from the load balancer's access logs and hope nothing was written async," or "announce data loss to customers." None of these are choices anyone wants to make at 3am. A current recovery point reduces the entire decision tree to "restore and verify."
On the regulatory side, SOC 2 CC9.1, ISO 27001 A.12.3, and HIPAA's contingency planning requirements all expect demonstrable, tested backups for systems holding regulated data. An EC2 instance flagged as unprotected by your own continuity check becomes audit evidence the moment a regulator walks in: it's not just a missing backup, it's documented awareness of a gap.
The cost side is real but usually small. AMI + EBS snapshots are storage-only: typically $0.05/GB-month for snapshot storage, deduplicated across recovery points so the marginal cost of each additional snapshot is just the changed blocks. For a 100GB instance with daily backups and 30-day retention, expect roughly $5-15/month per instance. For cattle (stateless ASG nodes) that's $5-15/month wasted; for pets (long-lived stateful instances) it's the cheapest insurance policy on the bill.
How do you bring an instance under protection?
Closing a coverage gap is a four-step loop: figure out what's exposed, decide what actually needs protecting, bring it under a backup plan, and make sure new instances don't slip through.
1. Inventory the coverage gap
Cross-reference describe-instances against backup list-protected-resources. The set difference is your gap. Before you tag anything, split the gap into pets and cattle: stateless ASG/EKS nodes don't need instance-level backups because they're already disposable: back up the data layer (RDS, S3, EFS) instead. Long-lived app servers with local state, jump hosts, anything bespoke: those are pets and they need a recovery point.
2. Tag for inclusion, not for exclusion
Define one or two backup plans (e.g. daily-7d and daily-30d) with tag-based resource selection (BackupRequired=true). Tagging an instance for inclusion makes coverage scale with the fleet: every new pet gets the tag in its launch template or Terraform module, and AWS Backup picks it up automatically at the next plan run. Avoid the opposite pattern (back up everything, exclude with tags); it's expensive and quietly breaks when someone forgets to exclude a stateless node group.
3. Make database backups application-consistent
Crash-consistent is fine for filesystems; it's not fine for databases. For Windows database workloads, AWS Backup gets you application consistency natively: install the SSM Agent and it runs the AWSEC2-CreateVssSnapshot Run Command to quiesce the Windows VSS writers around the snapshot, with no scripts to maintain. For Linux databases AWS Backup has no native pre/post-script hook; use Amazon Data Lifecycle Manager's pre/post-script SSM templates (which flush writes via mysql FLUSH TABLES WITH READ LOCK, pg_start_backup, or equivalent and release afterward), or back up the database engine directly (RDS, or a dump shipped to S3). Without application consistency you have a backup that may or may not restore cleanly, and you only find out which one during recovery.
4. Prevent recurrence with AWS Config and IaC defaults
Enable the AWS Config managed rule ec2-resources-protected-by-backup-plan to alert on any running EC2 instance without recent backup coverage. For prevention, bake BackupRequired=true into your Terraform/CloudFormation modules for any long-lived instance pattern, and lint pull requests to flag new EC2 resources without a backup tag. The goal is that an unprotected pet shouldn't be possible to create in the first place.
# Bulk-tag every running instance in a target account that isn't already protected.
UNPROTECTED=$(comm -23 \
<(aws ec2 describe-instances --filters Name=instance-state-name,Values=running \
--query 'Reservations[].Instances[].InstanceId' --output text | tr '\t' '\n' | sort) \
<(aws backup list-protected-resources \
--query "Results[?ResourceType=='EC2'].ResourceArn" --output text | tr '\t' '\n' | awk -F/ '{print $NF}' | sort))
echo "$UNPROTECTED" | xargs -n1 -I {} \
aws ec2 create-tags --resources {} \
--tags Key=BackupRequired,Value=true Key=BackupTier,Value=daily-30d
# Verify coverage at the next plan run.
aws backup list-backup-jobs \
--by-state COMPLETED --by-created-after $(date -u -d '24 hours ago' +%FT%TZ) \
--query 'BackupJobs[?ResourceType==`EC2`].ResourceArn' Quick quiz
Question 1 of 5You've identified 14 unprotected EC2 instances. Three are long-lived app servers (pets), 11 are stateless ASG nodes (cattle) backed by an EKS cluster. What's the right protection strategy?
You scored
0 / 5
Keep learning
Dig deeper into backup strategy, application-consistent snapshots, and continuous coverage detection.
- AWS Backup documentation Service docs covering backup plans, vaults, resource selection, and restore flows across EC2, EBS, RDS, EFS, and more.
- AWS Backup: application-consistent backups for EC2 How AWS Backup uses Windows VSS to take application-consistent EC2 snapshots that restore cleanly.
- AWS Config managed rule: ec2-resources-protected-by-backup-plan Continuous detection for any EC2 instance running without recent backup coverage.
- AWS Well-Architected Reliability Pillar (Backup & Recovery) How backup strategy fits into a broader continuity and disaster-recovery design.
You've completed Protect EC2 instances with AWS Backup. You can now distinguish a true coverage gap from a noisy finding, decide which instances are pets that genuinely need recovery points, bring them under a backup plan with tag-based selection, and keep new unprotected instances from sneaking in. The next time COV-001 fires at 2am, you'll have a four-step loop ready to run.
Back to the library