Recipes / Recipe 5
Automatic nightly backups of a Google Sheet
A dated copy of your spreadsheet every night in a folder you choose, keeping the last 30 and trashing older ones.
You'll need: A Google Drive folder for the backups.
Steps
- Create a folder in Drive called e.g. Sheet backups and copy its ID from the URL.
- In the spreadsheet you want to protect, open Extensions → Apps Script and paste the script.
- Set
folderId, how many copies tokeep, and thehourto run. - Run
backupSpreadsheetonce to test (approve permissions), and check the folder. - Run
installDailyBackuponce. From now on it runs every night.
/**
* Nightly backup: copies this spreadsheet into a Drive folder and keeps only the newest N copies.
* Run installDailyBackup once; after that it runs on its own.
*/
const BACKUP_CONFIG = {
folderId: 'PASTE_BACKUP_FOLDER_ID', // a folder you own, ideally not shared widely
keep: 30, // how many backups to keep
hour: 2 // hour of day (script time zone) to run
};
function installDailyBackup() {
ScriptApp.getProjectTriggers()
.filter((t) => t.getHandlerFunction() === 'backupSpreadsheet')
.forEach((t) => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger('backupSpreadsheet').timeBased().everyDays(1).atHour(BACKUP_CONFIG.hour).create();
}
/** Pure function: given [{id, created}] returns the ids to delete, keeping the newest `keep`. */
function pickBackupsToDelete(files, keep) {
return files
.slice()
.sort((a, b) => b.created - a.created)
.slice(keep)
.map((f) => f.id);
}
function backupSpreadsheet() {
const cfg = BACKUP_CONFIG;
const ss = SpreadsheetApp.getActiveSpreadsheet();
const folder = DriveApp.getFolderById(cfg.folderId);
const prefix = 'Backup - ' + ss.getName() + ' - ';
const stamp = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd HHmm');
DriveApp.getFileById(ss.getId()).makeCopy(prefix + stamp, folder);
const existing = [];
const files = folder.getFiles();
while (files.hasNext()) {
const f = files.next();
if (f.getName().indexOf(prefix) === 0) existing.push({ id: f.getId(), created: f.getDateCreated().getTime() });
}
pickBackupsToDelete(existing, cfg.keep).forEach((id) => DriveApp.getFileById(id).setTrashed(true));
}
Good to know
- Copies land in the script owner's Drive and count against their storage.
- Old backups are moved to the Trash (recoverable for 30 days), not permanently deleted.
- Version history (File → Version history) already exists, but separate copies survive accidental deletion of the file itself and are easy to hand to an accountant.
- Change the time zone under Project Settings if backups run at an odd hour.
Do it yourself
Recipe Club: $12/month
Prefer to set things up yourself? Join the Recipe Club and get access to a private GitHub repository of ready-made, tested automations for Sheets, Apps Script, Python and Node. Each one comes with setup notes and sample data.
- A new recipe added every week
- Everything in the free library, plus the longer, more complete versions
- Use them in your own business as much as you like
- Cancel anytime from your Stripe receipt or by email
Recipe Club opens soon: join the waitlist
You'll need a free GitHub account. After checkout, reply with your GitHub username and we'll send the repo invite, usually within one business day.
Or buy a one-time recipe pack ($19–$29)
- Clean Data Pack $19
Dedupe, cleaning, merging and normalizing scripts and formulas for messy exports, with a sample sheet.
Coming soon - Forms & Inbox Pack $19
Form auto-replies, request routing, a lead log and follow-up nudges, ready to paste into Apps Script.
Coming soon - Money Ops Pack $29
Invoice reminders, a monthly revenue dashboard with QUERY, a receipts log and nightly backups.
Coming soon
Written with AI assistance. Script logic is unit-tested; formulas follow documented Google Sheets behavior. Always try new automations on a copy of your sheet first.