Web Development & Deployment

Running a NodeJS script through a Cron job

Chris Dyer

Doing something on a regular basis should almost always be a job for Cron. The following setup is reliable and easy to understand, both initially and when you need to modify the timings in future.

As much as it is easy to just use a setInterval to make something happen every x seconds, anything more complicated should be setup as a Cron task. It will also allow you to specify doing something at a specific time of day, even if your Node process were to crash.

My solution for creating this setup easily and in a manageable way is as follows. I have the following script which needs to be run every day at 10.30am which sends a text message via Twilio.

#!/usr/bin/env node

var twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);

var done = false;

twilio.sendMessage({
  to: "[recipient]",
  from: "[sender]",
  body: "I was sent at 10.30am today!"
}, function(err, response) {
  done = true;
  
  if (err)
    console.log("Error: " + response.status);
  else
    console.log("Sent successfully");
});

setInterval(function() {
  if (done) {
    setTimeout(function() {
      process.exit();
    }, 1000);
  }
  // Check every second then wait another second before killing
}, 1000);

In a real world application based on this, I want to send multiple texts, so the setInterval block at the end was neccessary because I want the script to wait for Twilio to return all results, then gracefully exit. Does anyone have a suggestion on improving this? It doesn't feel like the most elegant solution. If you were just performing one task, including process.exit() in it's callback function would be fine.

Making it run at a specific time

Personally I really like the Whenever gem, which is Ruby based, rather than Node, however I find the syntax super easy to use and most importantly pick up again in 18 months time and work out what it is doing.

Get ruby installed and install the whenever gem.

$ gem install whenever

Somewhere on your server run this Whenever task which will create a config folder and a schedule.rb file which will hold your tasks.

$ wheneverize .

Alongside the config folder I also like to setup a logs folder where my task can dump it's output to.

Now that you have Whenever installed customize the schedule.rb file created by the wheneverize command. Mine looks like this:

set :output, {:error => 'logs/error.log', :standard => 'logs/cron.log'}

every 1.day, :at => '10:30' do
  command '/usr/local/bin/node /home/deployer/service/current/bin/notify.js'
end

The file paths given to set :output are relative to your user home directory (~).

My version of Node is installed into /usr/local/bin/node which you may need to customize. Run which node to find out where your NodeJS binary is located.

The second argument to Node is the absolute path of my .js script to run.

Whenever has lots of other configuration properties documented in it's README. You could run the same command on a specific day of the week, every hour or at a particular time as we have done here.

Most importantly to finish and actually save your setup run: whenever -w

If all goes well it should return...

[write] crontab file written

To confirm your crontab file is setup correctly (the file Cron uses for all configuration), run crontab -l. You should see your JS script command in amongst various other Cron configuration values which Whenever has generated for us.

This is my recommended setup for running a NodeJS script at regular intervals. I hope you find it useful.