How to Build a cron-Based Content Publishing System
Why Automate Content Publishing?
When I started building my blog, I wanted the ability to queue up posts in a JSON file and have them publish themselves on schedule. No CMS login, no manual deploys, just a cron job that checks for posts with today's date and generates the HTML.
This approach has been running my blog for weeks now, and it works brilliantly. In this post I will walk you through exactly how I built it, from the JSON schema to the cron configuration.
The Architecture
The system has three components:
- A posts.json file that stores all blog post data with dates and publication status
- A Python generator script that reads the JSON, applies templates, and writes HTML files
- A cron job that runs the generator once daily
The beauty of this setup is its simplicity. There is no database, no web framework, no build pipeline. Just a JSON file, a Python script, and cron.
The JSON Schema
Each post in the array looks like this:
{
"title": "My Blog Post",
"slug": "my-blog-post",
"meta_description": "A description under 155 chars",
"tags": ["python", "automation"],
"date": "2026-03-21",
"status": "published",
"body": "<h2>Content here</h2><p>HTML body...</p>"
}The key fields are date and status. The generator only processes posts where the date is today or earlier and the status is "published". This means I can write posts in advance and they will appear automatically on the right day.
The Generator Script
The Python script is about 150 lines and handles everything:
def load_posts():
with open(POSTS_FILE, "r") as f:
return json.load(f)
def should_publish(post):
post_date = datetime.strptime(post["date"], "%Y-%m-%d").date()
return (
post["status"] == "published"
and post_date <= date.today()
)For each publishable post, the script reads an HTML template, replaces placeholders like {{TITLE}} and {{BODY}}, and writes the output to the blog directory. It also regenerates the blog index page and updates the sitemap.xml.
Template Substitution
I use simple string replacement rather than a templating engine like Jinja2. For a static blog this is perfectly adequate:
html = template.replace("{{TITLE}}", post["title"])
html = html.replace("{{SLUG}}", post["slug"])
html = html.replace("{{BODY}}", post["body"])
html = html.replace("{{META_DESCRIPTION}}", post["meta_description"])This keeps dependencies minimal. The only requirement is Python 3 and the standard library.
Setting Up the Cron Job
The cron configuration is straightforward:
# Run blog generator every day at 06:00
0 6 * * * /usr/bin/python3 /home/user/blog/generate.py >> /home/user/blog/cron.log 2>&1I redirect output to a log file so I can debug issues. The script also sends a Telegram notification on success or failure, which I will cover in a separate post.
Deploying to Multiple Targets
One nice feature of my setup is multi-target deployment. The generator copies output to both a local preview directory and the production web root:
DEPLOY_TARGETS = [
SITE_DIR / "public_html",
Path("/home/user/web/site.com/public_html"),
]This means I can preview locally before the cron job deploys, or let the cron handle everything automatically.
Handling Edge Cases
A few things I learned while building this:
- Idempotent generation: The script regenerates all published posts every run, not just new ones. This means template changes apply retroactively to all posts.
- Date parsing: Always use ISO format (YYYY-MM-DD) in your JSON. It sorts naturally and avoids locale issues.
- File encoding: Explicitly open files with UTF-8 encoding to avoid issues with special characters in post content.
- Atomic writes: Write to a temporary file first, then rename. This prevents serving partial HTML if the script crashes mid-write.
Why Not Use a Static Site Generator?
Tools like Hugo, Jekyll, or Eleventy are excellent. But for my use case, a custom 150-line Python script gives me exactly what I need with zero learning curve and complete control. I understand every line of the code, and I can extend it in any direction without fighting a framework.
For a simple blog that publishes from JSON, this approach is hard to beat.
Results
This system has published over 50 posts automatically without any manual intervention. The total codebase is under 200 lines of Python, and the cron job has been running reliably for weeks. If you want a dead-simple publishing system with no dependencies, this pattern is worth considering.