This repository contains a demo project created as part of my DevOps studies in the TechWorld with Nana – DevOps Bootcamp.
Demo Project: Data Backup & Restore
Technologies used: Python, Boto3, AWS
Project Description:
- Write a Python script that automates creating backups for EC2 Volumes
- Write a Python script that cleans up old EC2 Volume snapshots
- Write a Python script that restores EC2 Volumes
- Python 3.12+ and uv for dependency management
- An AWS account with permissions to manage EC2 instances, EBS volumes and snapshots
- Boto3 — the AWS SDK for Python (EC2 client reference)
- schedule — for running the backups on a recurring basis
Install Python dependencies:
uv syncConfigure AWS credentials at ~/.aws/credentials:
[default]aws_access_key_id = AKIA...
aws_secret_access_key = ...And the default region at ~/.aws/config:
[default]region = eu-central-1All scripts in this project target
eu-central-1. If you use a different region, update theregion_nameargument (and theAvailabilityZonein the restore script) accordingly.
Launch 2 instances:
- Number of instances:
2 - Instance type:
t2.micro - Storage:
1 volume — 8 GiB
Tag the instances — name one dev and the other prod:
Confirm each instance has its volume attached:
Boto3 docs:
importboto3ec2_client=boto3.client('ec2', region_name='eu-central-1')
volumes=ec2_client.describe_volumes()
forvolumeinvolumes['Volumes']:
new_snapshot=ec2_client.create_snapshot(
VolumeId=volume['VolumeId']
)
print(new_snapshot)Run
python3 volume-backups.pyCheck snapshots in AWS Dashboard
importboto3importscheduleimporttimeec2_client=boto3.client('ec2', region_name='eu-central-1')
defcreate_volume_snapshots():
volumes=ec2_client.describe_volumes()
forvolumeinvolumes['Volumes']:
new_snapshot=ec2_client.create_snapshot(
VolumeId=volume['VolumeId']
)
print(new_snapshot)
schedule.every().day.do(create_volume_snapshots)
whileTrue:
schedule.run_pending()
time.sleep(1)For a quick demo — shorten the interval to a few seconds:
schedule.every(20).seconds.do(create_volume_snapshots)First, tag the production volume. Navigate to the prod instance → Storage → select its attached volume:
Set the volume name to prod:
Then filter describe_volumes by that tag so only the prod volume is backed up (Filters reference):
volumes=ec2_client.describe_volumes(
Filters=[
{
'Name': 'tag:Name',
'Values': ['prod']
}
]
)python3 volume-backups.pyCheck the source volume id — it should match the prod volume:
Doc: describe_snapshots
First, list the snapshots you own, sorted newest-first (reverse=True):
importboto3fromoperatorimportitemgetterec2_client=boto3.client('ec2', region_name='eu-central-1')
snapshots=ec2_client.describe_snapshots(
OwnerIds=['self']
)
sorted_by_date=sorted(snapshots['Snapshots'], key=itemgetter('StartTime'), reverse=True)
forsnapinsnapshots['Snapshots']:
print(snap['StartTime'])
print('############')
forsnapinsorted_by_date:
print(snap['StartTime'])With the list sorted newest-first, slicing [2:] skips the two most recent and deletes everything older:
Boto3 doc: delete_snapshot
importboto3fromoperatorimportitemgetterec2_client=boto3.client('ec2', region_name='eu-central-1')
snapshots=ec2_client.describe_snapshots(
OwnerIds=['self']
)
sorted_by_date=sorted(snapshots['Snapshots'], key=itemgetter('StartTime'), reverse=True)
forsnapinsorted_by_date[2:]:
response=ec2_client.delete_snapshot(
SnapshotId=snap['SnapshotId']
)
print(response)Run:
python3 cleanup-snapshots.pyCheck the AWS dashboard — only the 2 most recent snapshots remain:
importboto3fromoperatorimportitemgetterec2_client=boto3.client('ec2', region_name='eu-central-1')
volumes=ec2_client.describe_volumes(
Filters=[
{
'Name': 'tag:Name',
'Values': ['prod']
}
]
)
forvolumeinvolumes['Volumes']:
snapshots=ec2_client.describe_snapshots(
OwnerIds=['self'],
Filters=[
{
'Name': 'volume-id',
'Values': [volume['VolumeId']]
}
]
)
sorted_by_date=sorted(snapshots['Snapshots'], key=itemgetter('StartTime'), reverse=True)
forsnapinsorted_by_date[2:]:
response=ec2_client.delete_snapshot(
SnapshotId=snap['SnapshotId']
)
print(response)Grab the prod instance id from the AWS dashboard:
Boto3 docs:
boto3.resourcedescribe_volumes— filtered byattachment.instance-iddescribe_snapshotscreate_volume- Volume resource — used to poll
vol.state - Instance resource and its
attach_volumeaction
Get the device id:
A new volume can't reuse a device name already in use, so change the device from /dev/xvda to /dev/xvdb.
Replace
instance_idwith your ownprodinstance id, and make sureAvailabilityZonematches the instance's AZ — a volume can only attach to an instance in the same Availability Zone. The script pollsvol.stateuntil the new volume isavailable, then attaches it.
importboto3importtimefromoperatorimportitemgetterec2_client=boto3.client('ec2', region_name='eu-central-1')
ec2_resource=boto3.resource('ec2', region_name='eu-central-1')
instance_id='i-0b62c30dfaaf087c5'device_id='/dev/xvdb'volumes=ec2_client.describe_volumes(
Filters=[
{
'Name': 'attachment.instance-id',
'Values': [instance_id]
}
]
)
instance_volume=volumes['Volumes'][0]
print(instance_volume)
snapshots=ec2_client.describe_snapshots(
OwnerIds=['self'],
Filters=[
{
'Name': 'volume-id',
'Values': [instance_volume['VolumeId']]
}
]
)
latest_snapshot=sorted(snapshots['Snapshots'], key=itemgetter('StartTime'), reverse=True)[0]
print(latest_snapshot['StartTime'])
new_volume=ec2_client.create_volume(
SnapshotId=latest_snapshot['SnapshotId'],
AvailabilityZone='eu-central-1a',
TagSpecifications=[
{
'ResourceType': 'volume',
'Tags': [
{
'Key': 'Name',
'Value': 'prod'
}
]
}
]
)
whileTrue:
vol=ec2_resource.Volume(new_volume['VolumeId'])
print(vol.state)
ifvol.state=='available':
ec2_resource.Instance(instance_id).attach_volume(
VolumeId=new_volume['VolumeId'],
Device=device_id
)
breaktime.sleep(1)Run:
python3 restore-volume.pyCheck the restored volume on AWS — it's attached to the prod instance as a second device:
Tear down everything created from the AWS console once you're done:
- Instances
- Snapshots
- Volumes

















