DEV Community

Cover image for How to build an automated content calendar using Redis and cron jobs
Mactrix XR
Mactrix XR

Posted on

How to build an automated content calendar using Redis and cron jobs

How to build an automated content calendar using Redis and cron jobs

You write a blog post on Monday. You check the box. But how are you bringing in traffic the other six days?

In a startup world filled with noise, product bugs, and endless feature requests, it is easy to fall behind on marketing. You go through the motions, ship a quick patch, and tell yourself you will write an SEO article tomorrow. We convince ourselves that we have time. We convince ourselves that users will find us anyway.

But the truth is much more urgent. Your startup runway is not guaranteed. Your product needs consistent traffic to survive.

When I was building my SaaS, I realized that writing articles by hand every week was killing my development speed. I needed a system that runs on schedule without my manual input. I needed a reliable engine to manage content strategy.

In this guide, I will show you step by step how to build an automated content calendar using Redis and simple cron jobs. This solution gives you full control over your scheduling, prevents duplicate runs, and keeps your content publishing automatically.


The problem with simple cron jobs

Most developers start with a basic system. They set up a system cron job or a Node.js process using node-cron. The script fires at 9:00 AM, calls an external API, generates an article, and posts it to a blog.

That works fine for a week. Then, real life happens.

Your server restarts right when the cron job is supposed to fire. The execution gets missed entirely. Or worse, your script hits a API timeout while generating text, hangs indefinitely, and spawns another process an hour later. Suddenly, you publish three duplicate posts in ten minutes.

To build a clean automated content calendar, you need two distinct components:

  1. A Scheduler: A lightweight process that pushes publishing tasks into a queue at specific times.
  2. A Worker: A resilient background process that pulls tasks from the queue, handles retries, and publishes the post.

By decoupling the schedule from the execution, your system becomes bulletproof. If your worker crashes, the schedule is not lost. If your generation task takes three minutes, the scheduler does not care.


Setting up Redis for your automated content calendar

Redis is perfect for this task. It is fast, lightweight, and supports data structures that make job queues easy to implement.

For our queue, we will use Redis sorted sets (ZSET). A sorted set allows us to store tasks with a timestamp as their score. This turns Redis into a time-based task broker.

Step 1: Install dependencies

Start a new Node.js project and install the required packages:

npm init -y
npm install ioredis node-cron dotenv @google/genai
Enter fullscreen mode Exit fullscreen mode

Step 2: Initialize the Redis connection

Create a file named redis.js to manage your connection.

// redis.js
const Redis = require('ioredis');
require('dotenv').config();

const redis = new Redis(process.env.REDIS_URL || 'redis://127.0.0.1:6379');

redis.on('connect', () => {
  console.log('Connected to Redis');
});

redis.on('error', (err) => {
  console.error('Redis connection error:', err);
});

module.exports = redis;
Enter fullscreen mode Exit fullscreen mode

Building the queue and scheduler workflow

Now we need a way to schedule upcoming content. We will push content topics into a Redis sorted set called content_schedule. The score for each item will be the UNIX timestamp (in milliseconds) when the post should be generated and published.

Step 3: Pushing topics into the calendar

Create a script named schedule.js. This script populates your calendar for the next 30 days.

// schedule.js
const redis = require('./redis');

async function scheduleArticles() {
  const topics = [
    { id: 'post-1', keyword: 'best database for saas', publishAt: Date.now() + 60000 }, // 1 min from now
    { id: 'post-2', keyword: 'how to lower AWS costs', publishAt: Date.now() + 86400000 }, // 1 day from now
    { id: 'post-3', keyword: 'building a micro saas in 2025', publishAt: Date.now() + 172800000 } // 2 days from now
  ];

  for (const item of topics) {
    const payload = JSON.stringify(item);
    // ZADD schedule_queue timestamp payload
    await redis.zadd('schedule_queue', item.publishAt, payload);
    console.log(`Scheduled "${item.keyword}" for timestamp: ${item.publishAt}`);
  }

  process.exit(0);
}

scheduleArticles();
Enter fullscreen mode Exit fullscreen mode

Step 4: Setting up the cron trigger

Instead of having your cron job do the heavy lifting of writing articles, the cron job will only check Redis every minute to see if any job is ready.

Create a file named scheduler.js:

// scheduler.js
const cron = require('node-cron');
const redis = require('./redis');

// Run every minute
cron.schedule('* * * * *', async () => {
  const now = Date.now();

  // Fetch jobs that were scheduled for 'now' or earlier
  const jobs = await redis.zrangebyscore('schedule_queue', 0, now);

  if (jobs.length === 0) {
    return;
  }

  for (const jobString of jobs) {
    // Move job from schedule_queue to active_queue to prevent re-processing
    const removed = await redis.zrem('schedule_queue', jobString);

    if (removed) {
      await redis.lpush('active_queue', jobString);
      console.log('Moved job to active queue:', JSON.parse(jobString).keyword);
    }
  }
});

console.log('Scheduler running...');
Enter fullscreen mode Exit fullscreen mode

Integrating Gemini AI content generation

Once a job hits the active_queue, our background worker processes it. This is where ai content automation comes into play.

We will use Google Gemini API for fast, high-quality text generation. This acts as an ai blog writer for saas that drafts full articles on autopilot.

Step 5: Creating the worker script

Create a file named worker.js:

// worker.js
const redis = require('./redis');
const { GoogleGenAI } = require('@google/genai');

const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

async function processQueue() {
  while (true) {
    // BRPOP blocks until a job is available in active_queue (timeout 5 seconds)
    const result = await redis.brpop('active_queue', 5);

    if (result) {
      const jobData = JSON.parse(result[1]);
      console.log(`Processing article for keyword: ${jobData.keyword}`);

      try {
        const articleText = await generateArticle(jobData.keyword);
        await publishToCMS(jobData.keyword, articleText);
        console.log(`Successfully published: ${jobData.keyword}`);
      } catch (error) {
        console.error(`Failed to process job ${jobData.keyword}:`, error.message);
        // Put the job back in queue or handle retry logic here
      }
    }
  }
}

async function generateArticle(keyword) {
  // Utilizing gemini ai content generation
  const response = await ai.models.generateContent({
    model: 'gemini-2.5-flash',
    contents: `Write a clear, engaging 1000-word blog post targeted at developers about: "${keyword}". Use clear H2 headings and practical tips.`,
  });

  return response.text;
}

async function publishToCMS(title, content) {
  // Mock function for CMS publishing (e.g., WordPress, Ghost, Webflow)
  console.log(`[CMS] Publishing post: "${title}" (${content.length} characters)`);
  return true;
}

processQueue();
Enter fullscreen mode Exit fullscreen mode

Overcoming real-world technical trade-offs

When building an automated seo content pipeline, tutorials often make it look easy. In production, you will hit edge cases. Here are three specific technical details I learned the hard way.

1. Redis atomic operations and race conditions

If you scale your scheduler across multiple server instances or serverless functions, two processes might read the same job from Redis at the exact same millisecond.

Using zrangebyscore followed by zrem is not thread-safe. To fix this, you should use Lua scripts inside Redis to fetch and remove the item in a single atomic operation.

Here is an atomic Lua script snippet for Redis:

local val = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1], 'LIMIT', 0, 1)
if #val > 0 then
    redis.call('ZREM', KEYS[1], val[1])
    return val[1]
end
return nil
Enter fullscreen mode Exit fullscreen mode

2. Gemini API rate limits

When generating long-form articles with gemini ai content generation, you can hit token limit errors (HTTP 429). If your worker tries to process five posts at once, the API will block your requests.

To solve this, implement an exponential backoff in your worker. If the API returns a 429 status code, delay the worker for 30 seconds before re-queuing the job.

3. Memory leaks in long-running Node.js processes

Node.js workers running continuous while(true) loops can slowly leak memory over weeks if garbage collection is not handling string buffers properly. Set up a simple Docker constraint or system service that restarts your worker process once every 24 hours.


Streamlining content ops for indie hackers

Building your own custom pipeline gives you complete control over your code. But maintaining your own server workers, error retry queues, prompt templates, and CMS integrations takes time away from building your primary product.

When running my startup, managing content ops for indie hackers became a full-time job. I spent more time debugging rate limits and formatting markdown than writing actual product features.

I ended up automating this whole flow with a dedicated background tool I built called SleepPublish.

It functions as an ai seo tool for startups and a complete wordpress ai autopilot. It picks low-competition keywords, drafts long-form content with Gemini, formats markdown, and publishes directly to platforms like WordPress, Webflow, Ghost, and Notion without needing self-hosted Redis queues or custom scripts.


Scale your organic traffic automatically

Consistency is everything in SEO. Search engines reward websites that publish high-quality content regularly over months, not sites that drop ten articles in one day and disappear for half a year.

By building an automated content calendar using Redis and cron jobs, you remove manual effort from your growth strategy. You ensure that your marketing pipeline runs smoothly behind the scenes while you focus on shipping features.

Set up your Redis queues, configure your cron tasks, and let your automated pipeline handle the rest.

Try SleepPublish free for 7 days, it plans, writes, and publishes SEO content straight to your CMS: https://sleeppublish.mactrixxr.space

Top comments (0)