Back to Blog

How to Build a Free, Serverless Dead Man's Switch Using Google Apps Script

As tech enthusiasts, we spend an unreasonable amount of time optimizing our lives. We write scripts to automate software installations, tweak our development environments to perfection, and build intricate widgets to pull live data. Yet, despite having total control over our digital workspaces, we completely ignore our digital legacy.

What happens to all of this if we unexpectedly log off for good?

A "Dead Man's Switch" is an automated system designed to answer that exact question. It is a mechanism that waits for your signal. If you do not interact with it for a predetermined period, it assumes you are incapacitated (or worse) and automatically sends out your pre-written emails and files to the people you trust.

Why Do We Even Need a Dead Man's Switch?

In the analog days, people left a physical will in a safe. Today, our lives are overwhelmingly digital, and the traditional methods have not kept up. Here is why setting up a Dead Man's Switch is no longer just for spies in movies, but a necessity for anyone living a digital life:

  • The Digital Vault Lockout: Think about your password managers, cryptocurrency wallets, API keys, and two-factor authentication (2FA) apps. If you disappear, your family is hit with a cryptographic brick wall. A switch can securely deliver the master passwords or recovery phrases they need to access or shut down your accounts.

  • Asset Preservation: We own domain names, run personal blogs, and manage hosting servers. Without renewal or intervention, these assets expire, get wiped, or get snatched up by bots.

  • Psychological Closure and Practical Guidance: Beyond the technical nightmare, losing someone is devastating. Leaving your loved ones to play detective with your digital footprint adds immense psychological burden and stress. A well-timed, automated message providing clear legal instructions, financial details, or simply a final heartfelt goodbye can offer profound peace of mind and closure during a chaotic time.

My Motivation: The Pragmatic Choice of Google

I knew I needed a system like this, but I was not happy with the existing solutions. There are paid commercial services, but trusting a random startup with my most sensitive final instructions felt inherently wrong. On the other hand, writing a custom Python script and hosting it on a VPS or a local Raspberry Pi felt too fragile. Servers crash, SD cards corrupt, cron jobs fail, and hosting bills get forgotten. A system designed for the ultimate downtime should not rely on infrastructure that requires constant maintenance.

This leads to an obvious question: If I am skeptical of random startups, is it not a massive contradiction to put blind faith in a tech giant like Google?

The truth is, my choice is not based on blind trust; it is entirely pragmatic. First, I am already deeply integrated into the Google ecosystem. My communications flow through Gmail, and my documents live in Google Drive. Introducing a completely new third-party service means expanding my attack surface and handing my data to yet another entity. By using Google Apps Script, the data never leaves the boundaries of my personal Google account until the trigger fires.

Second, it is completely free. There are no server costs or subscription fees to worry about failing years down the line.

Finally, there is the infrastructure argument. Google Apps Script runs on the exact same enterprise-grade backbone that powers Google Workspace globally. It has massive redundancy. Unlike a local server that might lose power or a startup that might go bankrupt, this script lives natively within one of the most resilient cloud networks on the planet.

Here is the ultimate guide to setting up your own automated, serverless emergency email system.


Step 1: Create Your Serverless Project

You do not need any local IDE or code editors to do this; everything happens right in your browser.

  1. Go to script.google.com and log in with your primary Google account.

  2. Click on the New Project button.

  3. Click on "Untitled project" at the top left and give it a memorable name, like Emergency Switch.

  4. Delete any code in the editor and paste the JavaScript code provided for this project.

  5. Hit the Save icon (or press Ctrl+S / Cmd+S).

[code]
/**
 * DEAD MAN'S SWITCH
 * Automated Check-in & Emergency Email System
 * @osmanonurkoc
 */

// ==========================================
// SYSTEM CONFIGURATION
// ==========================================

const CONFIG = {
    ownerEmail: "yourmail@example.com",
    daysReminder: 10, // Days without check-in before a reminder is sent
    daysDeadman: 14,  // Days without check-in before activating the dead man's switch
    maxExecutionCount: 7, // How many times the final emails will be sent
    enableReminderEmails: true // Toggle to true/false to enable or disable reminder emails
};

// --- Warning & Reminder Email Contents ---
const REMINDER_SUBJECT = "REMINDER: Please check-in";
const REMINDER_MESSAGE = "Please check-in. You can reply to this email to automatically check-in, or run the manualCheckIn() function.";

const ACTIVATION_SUBJECT = "Dead Man's Switch Activated";
const ACTIVATION_MESSAGE = "You forgot to check-in. The dead man's switch has been activated and emails are being sent.";

// ==========================================
// EMERGENCY EMAIL PLANS
// ==========================================
// You can create as many specific plans as you want.
// A plan can be sent to a single person or a group.

const DEAD_MAN_PLANS = [
    {
        // Example 1: Group email with attachments
        recipients: ["family1@example.com", "family2@example.com"],
        subject: "I Am Gone - General Message",
        message: "If you are reading this, the automated system has been triggered. Please find the attached files.",
        enableAttachments: true, // Set to false if you don't want to use attachments here
        attachmentDriveIds: ["YOUR_DRIVE_FILE_ID_1", "YOUR_DRIVE_FILE_ID_2"]
    },
{
    // Example 2: Specific email to a single person without attachments
    recipients: ["lawyer@example.com"],
    subject: "Confidential - Legal Instructions",
    message: "This is an automated message containing specific legal instructions...",
    enableAttachments: false, // Attachments disabled, array below will be ignored
    attachmentDriveIds: []
},
{
    // Example 3: Simple goodbye message
    recipients: ["friend@example.com"],
    subject: "Goodbye",
    message: "Thank you for everything.",
    enableAttachments: false,
    attachmentDriveIds: []
}
];

// ==========================================
// CORE SYSTEM FUNCTIONS
// ==========================================

/**
 * Initializes the system and sets up the time-driven trigger.
 * Run this function ONCE to start the dead man's switch.
 */
function initializeSystem() {
    const props = PropertiesService.getScriptProperties();

    props.setProperties({
        'lastCheckinTime': new Date().getTime().toString(),
                        'counter': '0',
                        'status': 'ALIVE' // Possible states: ALIVE, REMINDER_SENT, TRIGGERED
    });

    const triggers = ScriptApp.getProjectTriggers();
    triggers.forEach(t => ScriptApp.deleteTrigger(t));

    ScriptApp.newTrigger('checkDeadman')
    .timeBased()
    .everyHours(1)
    .create();

    Logger.log("System successfully initialized. Status: ALIVE");
}

/**
 * Manually resets the timer and prevents the switch from triggering.
 */
function manualCheckIn() {
    const props = PropertiesService.getScriptProperties();
    props.setProperty('lastCheckinTime', new Date().getTime().toString());
    props.setProperty('status', 'ALIVE');
    props.setProperty('counter', '0');
    Logger.log("Manual check-in successful. Timers reset.");
}

/**
 * Main routine to evaluate time conditions and trigger actions.
 * Runs automatically every hour.
 */
function checkDeadman() {
    const props = PropertiesService.getScriptProperties();
    const lastCheckinTime = parseFloat(props.getProperty('lastCheckinTime'));
    const currentTime = new Date().getTime();

    const timeDifferenceDays = (currentTime - lastCheckinTime) / (1000 * 60 * 60 * 24);

    // Check if owner replied to the reminder to auto check-in
    if (checkForCheckinReply()) {
        manualCheckIn();
        return;
    }

    if (timeDifferenceDays > CONFIG.daysReminder) {

        if (timeDifferenceDays > CONFIG.daysDeadman) {
            // --- TRIGGER PHASE ---
            const status = props.getProperty('status');

            if (status !== 'TRIGGERED') {
                GmailApp.sendEmail(CONFIG.ownerEmail, ACTIVATION_SUBJECT, ACTIVATION_MESSAGE);
                props.setProperty('status', 'TRIGGERED');
                Logger.log("Dead man switch triggered! Activation email sent to owner.");
            }

            executeDeadmanSwitch();

        } else {
            // --- REMINDER PHASE ---
            if (CONFIG.enableReminderEmails) {
                const status = props.getProperty('status');

                if (status === 'ALIVE') {
                    GmailApp.sendEmail(CONFIG.ownerEmail, REMINDER_SUBJECT, REMINDER_MESSAGE);
                    props.setProperty('status', 'REMINDER_SENT');
                    Logger.log("Reminder email sent to owner.");
                }
            }
        }
    }
}

/**
 * Iterates over DEAD_MAN_PLANS and sends customized emails based on their settings.
 */
function executeDeadmanSwitch() {
    const props = PropertiesService.getScriptProperties();
    let counter = parseInt(props.getProperty('counter') || '0');

    if (counter < CONFIG.maxExecutionCount) {

        DEAD_MAN_PLANS.forEach(plan => {
            let attachments = [];

            // Process attachments only if the boolean flag is true and array has items
            if (plan.enableAttachments && plan.attachmentDriveIds && plan.attachmentDriveIds.length > 0) {
                plan.attachmentDriveIds.forEach(id => {
                    try {
                        if (id && id.trim() !== "" && !id.includes("YOUR_DRIVE_FILE_ID")) {
                            let file = DriveApp.getFileById(id);
                            attachments.push(file.getAs(file.getMimeType()));
                        }
                    } catch (e) {
                        Logger.log("Error loading attachment ID: " + id + " - " + e.message);
                    }
                });
            }

            const toEmails = plan.recipients.join(",");

            // Send email depending on whether there are valid attachments retrieved
            if (attachments.length > 0) {
                GmailApp.sendEmail(toEmails, plan.subject, plan.message, { attachments: attachments });
            } else {
                GmailApp.sendEmail(toEmails, plan.subject, plan.message);
            }
        });

        props.setProperty('counter', (counter + 1).toString());
        Logger.log("Emergency emails sent. Batch: " + (counter + 1) + "/" + CONFIG.maxExecutionCount);
    } else {
        Logger.log("Maximum execution count reached. System idle.");
    }
}

/**
 * Scans the owner's inbox for unread replies to the specific reminder thread.
 * @returns {boolean} True if an unread reply is found.
 */
function checkForCheckinReply() {
    const searchQuery = `subject:"${REMINDER_SUBJECT}" is:unread label:inbox`;
    const threads = GmailApp.search(searchQuery);

    if (threads.length > 0) {
        GmailApp.markThreadsRead(threads);
        return true;
    }
    return false;
}
[/code]

Step 2: Configure Your Settings

At the very top of the code, you will see a CONFIG object. This is where you dictate the strict rules of your system.

  • ownerEmail: Change this to your actual email address. This is where the system will ping you with "Are you alive?" reminders.

  • daysReminder: The number of days the system will wait silently before checking in on you (e.g., 10 days).

  • daysDeadman: The absolute limit. If you have not checked in by this day, the system triggers the emergency protocols (e.g., 14 days).

  • maxExecutionCount: How many times the final emails should be sent. Setting this to 3 ensures your critical emails are not buried in spam or overlooked, without creating an infinite loop.

  • enableReminderEmails: Keep this true so you get the warning emails before the system activates!

Step 3: Set Up Your Emergency Plans

Below the configuration, you will find the DEAD_MAN_PLANS array. You do not have to send a generic blast to everyone. You can tailor your messages for specific audiences by creating different blocks of instructions.

For example:

  • Plan A: Send financial info and master passwords to your spouse.

  • Plan B: Send specific legal instructions to your lawyer.

  • Plan C: Send a simple goodbye message to a broader group of friends.

Just change the recipients array, the subject, and the message for each block to match your needs.


Step 4: Adding Attachments (The Google Drive Secret)

If you want to send a file, like an encrypted ZIP of your source code, a PDF of your will, or a digital journal, change enableAttachments to true in your specific plan.

How to find your Google Drive File ID:

  1. Upload your file to your Google Drive.

  2. Right-click the file and select Share then Copy link.

  3. You will get a link that looks like this: https://drive.google.com/file/d/1A2b3C4d5E6f7G8h9I0j/view

  4. The ID is the long string of characters between /d/ and /view. In this case: 1A2b3C4d5E6f7G8h9I0j.

  5. Paste that exact ID into the attachmentDriveIds: ["YOUR_ID_HERE"] array in your code.

Do these files need to be public? Absolutely NOT. This is the biggest security advantage of Google Apps Script. Because the script runs as you (authenticated under your Google account), it has full access to your private Drive files. Keep your sensitive files completely private. The script will simply fetch them from your private Drive and attach them directly to the outgoing emails when the switch triggers.


Step 5: Authentication and The First Run (Crucial!)

Google takes security seriously. It will not let a script read your inbox or send emails without your explicit, manual permission. You must authorize the script the very first time.

  1. In the Google Apps Script editor, look at the top toolbar. You will see a dropdown menu.

  2. Select initializeSystem from that menu.

  3. Click Run.

  4. A prompt will appear saying "Authorization required." Click Review permissions.

  5. Choose your Google account.

  6. Do not panic at the warning: Google will throw a scary warning saying "Google hasn’t verified this app." This is completely normal because you just wrote the app, and Google has not reviewed your personal code.

  7. Click Advanced at the bottom, and then click the Go to Emergency Switch (unsafe) link.

  8. Click Allow to grant your script access to your Gmail and Google Drive.

Once you do this, look at the Execution Log at the bottom of the screen. If it says "System successfully initialized. Status: ALIVE", congratulations! Your Dead Man's Switch is now armed and running.


Living with Your New Switch

You do not need to touch the code ever again.

  • The Check-In: Every hour, the script wakes up in the cloud, checks the math, and goes back to sleep.

  • The Reminder: If 10 days pass without activity, you will get an email saying "REMINDER: Please check-in".

  • The Reset: To reset the timer, simply hit Reply to that reminder email and type anything (like "I am here"). The next time the script checks your inbox, it will see your unread reply, reset the timer back to zero, and go back to sleep.

It is completely frictionless, incredibly secure, and gives you ultimate peace of mind without costing a single dime or requiring server maintenance. Set it, forget it, and get back to automating the rest of your life.

[link] Contribute the project [/link]