How long to keep backups: writing a backup retention policy
Keep backups long enough to reach back past any problem you might not notice for a while, and delete them once they are no longer needed. For most servers, grandfather-father-son (GFS) rotation does both: 7 daily, 4 weekly and 12 monthly backups reach back a year with 23 copies instead of 365. Write the rules down as a retention policy, then make your backup tool and your storage enforce them.
What a retention policy decides
A retention policy answers four questions for each kind of backup:
- How far back can we go? Further than the time it could take to notice a problem. An attacker who waits, a slowly corrupted table or a folder deleted by mistake can go unseen for weeks.
- How many restore points in between? A bad deploy needs the copy from just before it. A slow corruption needs the last good copy, wherever that is.
- What does it cost? Every copy is billed per GB per month, and some storage bills a minimum number of days per object.
- When must data be deleted? Old backups hold old personal data, old credentials, and data customers asked you to remove.
Write the answers down. A policy in a file can be reviewed, tested and shown to a customer. A policy that lives only in a cron line cannot.
Grandfather-father-son rotation
GFS keeps three generations: frequent recent backups (sons), fewer older ones (fathers) and a few long-lived ones (grandfathers). Borg's documentation names this scheme for its own retention options. A common starting point:
| Generation | Which backup | Keep | Reaches back |
|---|---|---|---|
| Daily (son) | Every night's backup | 7 | 1 week |
| Weekly (father) | Sunday's backup | 4 | About 1 month |
| Monthly (grandfather) | The backup from the 1st | 12 | 1 year |
| Yearly (optional) | The backup from 1 January | As required | Years |
That is 23 copies for a year of history. Compared with keeping every nightly backup, you can no longer restore an arbitrary day from five months ago, but you keep a restore point in every month. Adjust each row to fit: a busy database may also want hourly copies for the last two days.
Worked example: what three policies cost
A server produces a 20 GB compressed full backup every night. Prices use Backblaze B2's pay-as-you-go rate as of October 2026, $6.95 per TB per month with no minimum storage duration, ignoring the free first 10 GB:
| Policy | Copies kept | Storage | Per month | Reaches back |
|---|---|---|---|---|
| 30 daily | 30 | 600 GB | About $4.17 | 30 days |
| GFS 7 / 4 / 12 | 23 | 460 GB | About $3.20 | 1 year |
| 365 daily | 365 | 7,300 GB | About $50.74 | 1 year |
GFS costs less than a month of dailies and reaches back twelve times as far. Keeping every nightly copy for a year costs about 16 times as much, for restore points you will rarely use.
Deduplicating tools change the math. restic and Borg store each unique chunk once, so 23 snapshots of slowly changing data take far less than 23 full copies. Retention still matters there: it decides how long changed and deleted data stays in the repository.
Minimum storage durations
Some storage bills each object for a minimum number of days, even if you delete it sooner. Deleting a 7-day daily from these saves nothing:
| Storage | Minimum billed duration (as of October 2026) |
|---|---|
| Amazon S3 Standard | None |
| S3 Standard-IA and One Zone-IA | 30 days |
| S3 Glacier Instant Retrieval and Glacier Flexible Retrieval | 90 days |
| S3 Glacier Deep Archive | 180 days |
| Cloudflare R2 Standard / Infrequent Access | None / 30 days |
| Backblaze B2 | None |
| Wasabi, Pay as You Go | 90 days |
On Wasabi, 20 GB a night, each billed for at least 90 days, comes to about 1.8 TB of billed storage whether you keep 7 dailies or 90, so keep 90. Put short-lived dailies in storage with no minimum, and move only copies kept for months to colder classes. Details per provider: Amazon S3, Cloudflare R2 and Wasabi.
How your tools express GFS
restic takes the policy as flags on forget, and --prune removes the unreferenced data:
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --pruneBorg takes the same idea on prune. Disk space is only freed when you run borg compact afterwards:
borg prune -v --list --keep-daily=7 --keep-weekly=4 --keep-monthly=12 /path/to/repoThe two count differently. restic's docs say its rules overlap: the 7 dailies already include one end-of-week snapshot, so --keep-weekly 4 adds 3 more. Borg applies its rules in order, and a backup kept by one rule does not count toward the next, so --keep-weekly=4 keeps 4 weeks beyond the dailies. Try both with --dry-run first. Setup, scheduling and restores are in the restic and Borg guides.
For plain archives in a bucket, lifecycle rules do the deleting, but they only know an object's age. Express GFS as three prefixes with three ages. Upload every night to daily/, then copy that night's file to weekly/ on Sundays and to monthly/ on the 1st:
#!/usr/bin/env bash
# Upload tonight's archive to daily/, then copy it to weekly/ on Sundays
# and to monthly/ on the 1st. Lifecycle rules delete each prefix by age.
set -euo pipefail
NAME="web-01-$(date +%F).tar.gz"
DEST="s3://acme-server-backups/web-01"
aws s3 cp "/var/backups/$NAME" "$DEST/daily/$NAME"
if [ "$(date +%u)" = "7" ]; then # %u: 1 is Monday, 7 is Sunday
aws s3 cp "$DEST/daily/$NAME" "$DEST/weekly/$NAME"
fi
if [ "$(date +%d)" = "01" ]; then # %d: day of the month, zero-padded
aws s3 cp "$DEST/daily/$NAME" "$DEST/monthly/$NAME"
fiAn aws s3 cp from one S3 path to another copies inside the storage service, so the file is only uploaded once. Then set one rule per prefix, with admin credentials rather than the server's:
{
"Rules": [
{
"ID": "daily-7-days",
"Filter": { "Prefix": "web-01/daily/" },
"Status": "Enabled",
"Expiration": { "Days": 7 }
},
{
"ID": "weekly-28-days",
"Filter": { "Prefix": "web-01/weekly/" },
"Status": "Enabled",
"Expiration": { "Days": 28 }
},
{
"ID": "monthly-365-days",
"Filter": { "Prefix": "web-01/monthly/" },
"Status": "Enabled",
"Expiration": { "Days": 365 }
}
]
}aws s3api put-bucket-lifecycle-configuration --bucket acme-server-backups --lifecycle-configuration file://lifecycle.jsonThat keeps about 7, 4 and 12 copies. The command replaces the bucket's whole lifecycle configuration, so the file must hold every rule you want. In a versioned bucket, Expiration only adds a delete marker; add NoncurrentVersionExpiration to each rule so old versions go too, as in the S3 bucket guide. Backblaze B2 and Cloudflare R2 have their own lifecycle settings built on the same idea.
Legal and regulatory retention
Some records must be kept for a set number of years, and some must not be kept longer than needed. The rules depend on your country, your industry and your contracts, so ask counsel what applies to you. This section is not legal advice.
If you process personal data of people in the EU, the GDPR's storage limitation principle, in Article 5(1)(e), says personal data must not be kept in a form that identifies people for longer than is necessary for the purposes it is processed for, with narrow exceptions for archiving, research and statistics. Backups that contain personal data are part of that question. Two practical points:
- A period you can state and show, such as 'database backups are deleted after 12 months', is far easier to explain than 'we keep everything'.
- Decide what happens when someone asks to be erased and their data is still in a backup. Whatever you decide, a restore should re-apply the deletions made since that backup was taken.
Where a law requires you to keep records for years, keep that archive separate from your disaster recovery backups, with its own rule, rather than stretching every backup to the longest period.
Deletion is part of the policy
Retention has two edges. The minimum protects you. The maximum limits cost and exposure, because every old backup is one more copy of your data and your old secrets that could leak. Enforce both:
- Let the storage delete, with lifecycle rules, rather than the server. The server's key then needs no delete permission, which also protects the backups if the server is compromised. See protecting backups from ransomware.
- If a script prunes, have it prune only after a new backup succeeds, so a broken job cannot age out the last good copy. The find guide has a guarded prune script.
- If you use Object Lock, keep the lock shorter than the expiry, so the lifecycle rule can remove each version when its time comes.
- Check that deletion happens. Bucket size should level off after one cycle of your longest rule. If it keeps growing, old versions or unfinished uploads are piling up.
A sample retention policy
Adapt the periods, then keep the policy next to your disaster recovery plan:
# Backup retention policy
Version: ____ Owner: ____________ Approved by: ____________ Date: ________
Review: every 12 months, and when systems, storage or legal requirements change
## Purpose
Keep enough backups to recover from failures, mistakes and attacks that may
go unnoticed for up to 30 days, and delete backups we no longer need.
## Scope
All production servers and databases in the system inventory.
## Schedule
| Backup | Frequency | Keep | Where |
|-----------------------------|-------------|-------------------------------|---------------------------|
| Database dumps | Every 6 h | 2 days | Off-provider bucket |
| Database dumps (nightly) | Nightly | 7 daily, 4 weekly, 12 monthly | Off-provider bucket |
| Files and /etc | Nightly | 7 daily, 4 weekly, 12 monthly | Off-provider bucket |
| Provider snapshots | Daily | 7 | Server's provider |
| Snapshots before upgrades | On demand | 14 days | Server's provider |
| Legal archive (if required) | Per counsel | Per counsel | Separate bucket and rules |
## Rules
1. Deletion is done by storage lifecycle rules or a separate admin job,
never with credentials stored on the server being backed up.
2. Pruning never runs unless a new backup has succeeded.
3. Object Lock, where used, is shorter than the expiry period.
4. Short-lived copies use storage with no minimum storage duration.
5. Backups are encrypted; keys are stored outside the backed-up systems.
6. A restore re-applies erasure requests received since the backup.
## Verification
- Monthly: the oldest and newest copy of each backup match this schedule,
and bucket size is stable.
- Quarterly: restore a monthly copy, not only last night's.
## Exceptions
Legal holds and incident investigations pause deletion for the affected
backups. Record who asked, the scope and the date to review it.Frequently asked questions
- How long should I keep server backups?
- Longer than it could take you to notice a problem, which can be weeks. Seven daily, four weekly and twelve monthly copies is a common default that reaches back a year with 23 copies.
- What is the grandfather-father-son backup scheme?
- A rotation that keeps recent backups often and older ones sparsely: for example daily copies for a week, weekly copies for a month and monthly copies for a year.
- Does deleting backups early save money?
- Not on storage with a minimum duration. Wasabi's Pay as You Go plan bills at least 90 days per object, and S3 Standard-IA bills at least 30, as of October 2026. On storage with no minimum, such as S3 Standard or Backblaze B2, it does.
- Do I have to delete backups under the GDPR?
- The GDPR's storage limitation principle says personal data should be kept no longer than necessary, and backups can contain personal data. How that applies to you is a question for counsel; a documented, enforced retention period makes the answer much easier.
How this was checked
Commands, limits and prices were checked against these official pages, on October 3, 2026:
- restic documentation: Removing backup snapshots (forget policies)
- Borg documentation: borg prune
- Amazon S3 User Guide: Understanding and managing Amazon S3 storage classes
- Amazon S3 User Guide: Examples of S3 Lifecycle configurations
- AWS CLI reference: put-bucket-lifecycle-configuration
- AWS CLI reference: s3 cp (copying from S3 to S3)
- Cloudflare R2 pricing
- Backblaze B2 Cloud Storage pricing
- Wasabi: Pricing FAQs (minimum storage duration)
- EUR-Lex: Regulation (EU) 2016/679 (GDPR), Article 5(1)(e)