For WordPress agencies managing multiple client websites, having a robust backup strategy is essential. In the event of an unexpected outage, plugin failure, or human error, backups ensure data can be quickly restored — minimizing downtime and reducing client risk.

At Kinsta, we understand the critical role backups play, which is why we offer six backup options: automatic daily, optional hourly (and every six hours), manual, system-generated, downloadable, and external backups sent directly to Amazon S3 or Google Cloud Storage.

While managing these backups is straightforward via the MyKinsta dashboard, Kinsta’s API opens up great possibilities for automating repetitive processes.

Imagine simply typing a command in Slack or setting up a cron job to trigger backups for all your WordPress sites in your Kinsta agency account without manually navigating through dozens or even hundreds of site dashboards.

This flexibility is one of the many advantages of using a host that prioritizes its clients’ needs by providing them with the tools to create custom, time-saving solutions.

This guide explains how to leverage the Kinsta API to automate backups across multiple sites. Whether you’re integrating with your preferred stack, using tools like Slack, or setting up automated schedules, this guide provides you with the knowledge to streamline your backup process and enhance your workflow.

Implementing backups for all sites and selected sites

Before diving into scheduling, it’s important to first understand how to trigger backups for all sites in your Kinsta account and how to target specific sites or environments using the Kinsta API.

Once we have the foundation for creating backups, we can easily integrate scheduling to automate the process.

Triggering backups for all sites in a Kinsta account

Like with every API, there isn’t always a single endpoint to do everything you need — you often have to combine multiple endpoints to achieve your desired result.

The Kinsta API is no different. While there are specific endpoints for managing backups, these endpoints require certain parameters, such as environment IDs, which you obtain by making additional requests to other endpoints.

For example, to trigger a manual backup for a site, you need the environment ID for that particular environment. To get the environment ID, you first need the site ID. This means you must make multiple API calls: one to get the site ID, another to retrieve the environment ID, and finally, a request to trigger the backup.

Step 1: Fetch all WordPress sites with Kinsta API

The first step is to retrieve a list of all the sites associated with your Kinsta account. Kinsta’s API provides an endpoint to fetch this data, which includes site IDs, names, and other relevant details. Using the GET /sites endpoint, you can pull a list of all sites under your company’s account.

Here’s an example using Node.js and Fetch API:

require('dotenv').config();

const KINSTA_API_URL = 'https://api.kinsta.com/v2';
const API_KEY = process.env.KINSTA_API_KEY;

async function getAllSites() {
    const response = await fetch(`${KINSTA_API_URL}/sites`, {
        headers: {
            Authorization: `Bearer ${API_KEY}`
        }
    });
    const data = await response.json();
    return data.company.sites; // Returns array of all sites
}

This function returns an array of all the sites in your account. Each site contains information such as the site’s ID, name, and environment(s).

Step 2: Fetch environment IDs for each WordPress site

Each site can have multiple environments (like production or staging), and backups are triggered per environment. To retrieve the environment IDs for each site, you make another API call to the GET /sites/{site_id}/environments endpoint.

Here’s an example function that fetches environments for a specific site:

async function getSiteEnvironments(siteId) {
    const response = await fetch(`${KINSTA_API_URL}/sites/${siteId}/environments`, {
        headers: {
            Authorization: `Bearer ${API_KEY}`
        }
    });
    const data = await response.json();
    return data.site.environments;
}

Step 3: Trigger backups for each environment

Once you have the environment IDs, you can trigger backups for each environment using the POST /sites/environments/{env_id}/manual-backups endpoint. The API allows you to create a manual backup by providing an environment ID and an optional tag to identify the backup.

Here’s how to trigger a backup for a given environment:

async function createBackup(environmentId, tag) {
    const response = await fetch(`${KINSTA_API_URL}/sites/environments/${environmentId}/manual-backups`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${API_KEY}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ tag })
    });

    if (response.ok) {
        console.log(`Backup created for environment ${environmentId} with tag: ${tag}`);
    } else {
        console.error(`Failed to create backup for environment ${environmentId}`);
    }
}

This function triggers a manual backup for the given environment ID. You can also tag your backup for easier identification.

Step 4: Automate backups for all sites

Now that you have functions to fetch all sites, retrieve environment IDs, and trigger backups, you can combine them to create a script that automates backups for every site in your Kinsta account.

Here’s how you can tie everything together:

async function backupAllSites() {
    const sites = await getAllSites();

    for (const site of sites) {
        const environments = await getSiteEnvironments(site.id);

        for (const environment of environments) {
            await createBackup(environment.id, `Backup-${new Date().toISOString()}`);
        }
    }

    console.log('Backups for all sites have been triggered.');
}

This function loops through all the sites, retrieves their environments, and triggers backups for each environment with a timestamped tag.

Now, when you run backupAllSites(), it triggers a backup for every environment in your Kinsta account.

Slack command example

You can integrate this backup process into a Slack command for even easier management. With a Slack app setup, users can trigger backups across all sites with a single command like /backup_all_sites.

Here’s a quick example of how this could work:

app.command('/backup_all_sites', async ({ ack, say }) => {
    await ack();
    await backupAllSites();
    say('Backups for all sites have been triggered.');
});

Triggering backups for selected sites using environment IDs

In some cases, you may want to trigger backups for only specific sites or environments rather than all sites in your Kinsta account. This could be useful if you’re interested in backing up only production environments or certain high-priority sites.

Using the Kinsta API, we can automate backups for selected environments by passing an array of environment IDs. Let’s walk through how to implement this.

Step 1: Pass environment IDs

When you want to trigger backups for selected sites, you need the environment IDs for those sites. You can either hardcode these IDs, retrieve them from the Kinsta API, or pass them dynamically (like through a command-line argument, Slack command, etc.).

Here’s a function that accepts an array of environment IDs and triggers backups for each one:

async function backupSelectedEnvironments(environmentIds, tag) {
    for (const envId of environmentIds) {
        await createBackup(envId, tag);
    }
}

The function above receives an array of environment IDs that you want to back up (environmentIds). The createBackup(envId, tag) function triggers backup for each environment in the array using the createBackup() function (explained in Step 2).

Step 2: Trigger the backup

To trigger the actual backup for each environment, use the Kinsta API’s POST /sites/environments/{env_id}/manual-backups endpoint as we did for all sites. Here’s how it works:

async function createBackup(environmentId, tag) {
    const response = await fetch(`${KINSTA_API_URL}/sites/environments/${environmentId}/manual-backups`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${API_KEY}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ tag })
    });

    if (response.ok) {
        console.log(`Backup created for environment ${environmentId} with tag: ${tag}`);
    } else {
        console.error(`Failed to create backup for environment ${environmentId}: ${response.statusText}`);
    }
}

Step 3: Execute backups for selected environments

Now that we have functions to trigger backups and handle multiple environments, we can combine them to back up specific environments. This example assumes we already have the environment IDs that we want to back up.

const selectedEnvironments = ['12345', '67890']; // Replace with actual environment IDs
backupSelectedEnvironments(selectedEnvironments, 'Manual Backup');

In this case:

  • The selectedEnvironments array contains the environment IDs you want to back up.
  • The backupSelectedEnvironments() function loops through each ID and triggers the backup with the tag ‘Manual Backup’.

Slack command example

If you’re using a Slack app or command-line interface, you can also allow users to specify which environments to back up.

Here’s how you might implement this in a Slack app:

app.command('/backup_selected_envs', async ({ command, ack, say }) => {
    await ack();
    const [tag, ...envIds] = command.text.split(' ');  // First part is the tag, rest are env IDs
    await backupSelectedEnvironments(envIds, tag);
    say(`Backups triggered for selected environments with tag ${tag}.`);
});

The user inputs a command like /backup_selected_envs Backup-Tag 12345 67890, where Backup-Tag is the tag, and 12345, 67890 are the environment IDs.

The command’s text is split, and the environment IDs are passed to the backupSelectedEnvironments() function.

After triggering the backups, the app responds to the user confirming the backup.

Scheduling backups for your WordPress sites

One of the most powerful aspects of automation is the ability to schedule tasks at specific times without manual intervention.

Now that we’ve covered how to trigger backups for all sites and selected sites, the next step is to automate these processes by adding scheduling.

Whether you want to schedule backups locally, on a hosted platform like Kinsta, or dynamically via Slack, there are various ways to implement this.

Understanding cron expressions

Before exploring different approaches to scheduling backups, it’s important to understand cron expressions, which are used to specify the timing for scheduled tasks across various platforms and tools.

Cron expressions define when a task should run. They are used in many scheduling libraries and services, such as node-cron, node-schedule, and even server-side cron jobs like those available in Kinsta’s Application Hosting platform.

Cron expressions consist of five fields, each controlling a specific aspect of when the task should be executed. A typical cron expression looks like this:

* * * * *
| | | | |
| | | | └─── Day of the week (0 - 7) (Sunday to Saturday, 0 and 7 represent Sunday)
| | | └────── Month (1 - 12)
| | └──────── Day of the month (1 - 31)
| └────────── Hour (0 - 23)
└──────────── Minute (0 - 59)

Let’s break down what each field represents:

  • Minute: The minute of the hour when the task should run (0 – 59).
  • Hour: The hour of the day when the task should run (0 – 23).
  • Day of the month: The specific day of the month to run the task (1 – 31).
  • Month: The month of the year to run the task (1 – 12).
  • Day of the week: The specific day of the week to run the task (0 – 7, where 0 and 7 both represent Sunday).

You can use specific values, wildcards (*), or ranges in each field to define the schedule.

Examples of cron expressions:

  • 0 0 * * *: Every day at midnight (00:00)
  • 0 3 * * 1: Every Monday at 3 a.m.
  • */10 * * * *: Every 10 minutes
  • 0 9-18 * * *: Every hour between 9 a.m. and 6 p.m.

With a solid understanding of cron expressions, we can now move on to different methods of scheduling backups for your WordPress sites using the Kinsta API.

Approach 1: Scheduling locally with node-cron

The Node.js package node-cron can run scheduled tasks based on a cron-like syntax. It’s ideal for running automated tasks in local or standalone Node.js applications, and it’s a popular choice for scheduling backups, sending emails, or executing other recurring tasks.

In node-cron, you define a task (like your backup function) and use a cron expression to specify when the task should run. The cron expression consists of five fields that define the minute, hour, day of the month, month, and day of the week when the task should be executed.

First, install node-cron in your project:

npm install node-cron

Let’s say you want to schedule a daily backup of all sites at midnight using node-cron. Here’s how you can do it:

const cron = require('node-cron');
const { backupAllSites } = require('./app');  // Import the backup function

// Schedule to run backups for all sites at midnight every day
cron.schedule('0 0 * * *', () => {
  console.log('Running scheduled backup for all sites at midnight...');
  backupAllSites();
});

Similarly, if you want to back up selected environments at 2 a.m. every day, you could schedule it like this:

cron.schedule('0 2 * * *', () => {
  const environmentIds = ['env12345', 'env67890']; // Specific environments to back up
  console.log('Running scheduled backup for selected environments...');
  backupSelectedEnvironments(environmentIds, 'Scheduled Backup');
});

Approach 2: Using cron jobs in cloud hosting (like Kinsta)

When deploying your Node.js application to a platform like Kinsta’s, you can leverage the platform’s built-in cron job functionality to schedule tasks like backups. However, setting up cron jobs in cloud environments requires a slightly different structure from local scheduling tools like node-cron.

When you deploy your app to Kinsta, it needs to have a running web process (even if it’s not actually serving web traffic).

To ensure your project runs correctly and doesn’t automatically call backup functions, you can create a file that runs a simple web server. This file acts as a “dummy” web process, while cron jobs handle the backup logic.

You can add this code:

require('http').createServer((req, res) => {
    res.writeHead(302, { Location: 'https://www.google.com' });
    res.end();
}).listen(process.env.PORT);

This way, you can set up script command to distinguish between the web process (start) and the cron job process (cron).

  "scripts": {
    "start": "node index.js",  // Web process
    "cron": "node app.js"  // Cron job process
  },

Finally, configure the cron job in Kinsta to call your backup function at the specified time. Using the cron job settings, you can define the command to run the backup.

In the MyKinsta dashboard applications’ Processes tab, set the command for the web process to:

npm run start

And set the cron job command to:

npm run cron

To run the cron job at a specific time (every day at 11:30 a.m.), set the cron expression like this:

30 11 * * *

This command will trigger the backupAllSites() function every day at 11:30 AM.

Setting up a cron job in Kinsta's Application Hosting
Setting up a cron job in Kinsta’s Application Hosting.

Setting up a cron job in Kinsta’s Application Hosting.

Approach 3: Scheduling with node-schedule

Another Node.js library, node-schedule, can schedule tasks using the cron format and also supports more complex schedules.

Here’s an example that allows users to schedule backups via Slack using the node-schedule cron format:

const schedule = require('node-schedule');
const { backupAllSites } = require('./app');

// Slack command to schedule backups dynamically
app.command('/schedule_backup', async ({ command, ack, say }) => {
    await ack();

    // Extract hour and minute from the command (expects HH:MM format)
    const [hour, minute] = command.text.split(':');

    // Validate input
    if (!hour || !minute) {
        say('Please specify the time in HH:MM format.');
        return;
    }

    // Schedule the backup using node-schedule's cron-like format
    const job = schedule.scheduleJob(`${minute} ${hour} * * *`, () => {
        console.log(`Running scheduled backup at ${hour}:${minute}`);
        backupAllSites();  // This triggers the backup for all sites
    });

    say(`Backup scheduled at ${hour}:${minute} successfully.`);
});

For example, a user could run the following Slack command to schedule a backup for 11:30 p.m.:

/schedule_backup 23:30

After running this command, the backup will be scheduled to run at 11:30 p.m. every day. The response from Slack might look like:

Backup scheduled at 23:30 successfully.

This approach allows users to dynamically schedule backups from Slack by simply specifying the time without needing to interact with the server or the application code. It’s a flexible and powerful way to handle scheduled tasks like backups in a user-friendly way.

Summary

Scheduling backups across multiple WordPress sites is nice for agencies managing numerous client websites. Automating these backups not only saves time but also ensures consistency, reduces the risk of human error, and provides peace of mind.

Would this solution benefit your agency? We’d love to hear your thoughts. Share them in the comments section below!

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 300 technical articles majorly around JavaScript and it's frameworks.