Why OG images matter
When someone shares your blog post on Twitter, LinkedIn, Slack, or iMessage, the platform fetches the og:image tag and renders a preview card. If that tag is missing, they use a generic placeholder. If it points to a low-quality or off-brand image, clicks drop.
Studies consistently show that posts with custom OG images get 2–3x more clicks than posts with generic previews. The problem is that creating a custom OG image for every post takes time — unless you automate it.
The automation pattern
The workflow has three steps:
- Trigger — new blog post published (webhook, RSS feed, or CMS event)
- Render — inject post metadata into an HTML template, call RenderPix, get a 1200×630px PNG back
- Store & link — upload to S3 or Cloudinary, update the post’s
og:imagemetadata
Once set up, every new post gets a unique, on-brand OG image automatically.
Step 1: Install the RenderPix n8n node
In your n8n instance: Settings → Community Nodes → Install. Search for n8n-nodes-renderpix and install. Then add a credential under Credentials → RenderPix API with your API key from the RenderPix dashboard.
Step 2: Build the OG image template
A good OG image template needs to work at 1200×630px and communicate the post title clearly at a glance. Here’s a clean starting point:
<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 1200px; height: 630px;
font-family: Arial, sans-serif;
background: #0a0a0b;
display: flex; align-items: center; justify-content: center;
position: relative; overflow: hidden;
}
.glow {
position: absolute; top: -100px; left: 50%;
transform: translateX(-50%);
width: 800px; height: 400px;
background: radial-gradient(ellipse, rgba(34,211,238,0.1) 0%, transparent 70%);
}
.card {
position: relative; z-index: 1;
width: 1080px; padding: 60px;
border: 1px solid rgba(42,42,48,0.8);
border-radius: 16px;
background: rgba(20,20,22,0.8);
}
.tag {
display: inline-block;
padding: 4px 12px; border-radius: 999px;
font-size: 13px; font-weight: 600;
background: rgba(34,211,238,0.1);
color: #22d3ee; margin-bottom: 20px;
text-transform: uppercase; letter-spacing: 0.05em;
}
.title {
font-size: 52px; font-weight: 700;
color: #e8e8ed; line-height: 1.15;
letter-spacing: -1px; margin-bottom: 24px;
}
.meta {
font-size: 16px; color: #8b8b96;
}
.logo {
position: absolute; bottom: 60px; right: 60px;
font-size: 20px; font-weight: 700;
color: #8b8b96; letter-spacing: -0.5px;
}
.logo span { color: #22d3ee; }
</style></head>
<body>
<div class="glow"></div>
<div class="card">
<div class="tag">${category}</div>
<div class="title">${title}</div>
<div class="meta">${readingTime} min read · renderpix.dev</div>
</div>
<div class="logo">render<span>pix</span></div>
</body></html>
Step 3: Build the n8n workflow
Node 1 — Webhook trigger
Add a Webhook node. Set it to POST. Copy the webhook URL — you’ll configure your CMS or blog platform to call this URL when a post is published.
The payload should include: title, category, slug, and optionally readingTime.
Node 2 — Code node (build HTML)
const { title, category, slug, readingTime = '5' } = $input.first().json;
const truncatedTitle = title.length > 60
? title.substring(0, 60) + '...'
: title;
const html = `<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<style>/* your CSS here */</style></head>
<body>
<div class="glow"></div>
<div class="card">
<div class="tag">${category}</div>
<div class="title">${truncatedTitle}</div>
<div class="meta">${readingTime} min read · renderpix.dev</div>
</div>
<div class="logo">render<span>pix</span></div>
</body></html>`;
return [{ json: { html, slug, title } }];
Truncate long titles. OG images are viewed at small sizes on most platforms. Titles over 60 characters often get cut off or become unreadable. Truncating in the Code node (not in CSS) gives you a clean result.
Node 3 — RenderPix
Add a RenderPix node: Operation → Render HTML, HTML → {{ $json.html }}, Width → 1200, Height → 630, Format → PNG, Return as → Binary.
Node 4 — Upload to S3
Add an AWS S3 node. Set the key to og/{{ $json.slug }}.png so each post gets a predictable, addressable URL. Set the ACL to public-read.
The resulting URL will be: https://your-bucket.s3.amazonaws.com/og/your-post-slug.png
Node 5 — Update CMS metadata
Use an HTTP Request node (or a CMS-specific node) to update your post’s og:image field with the S3 URL. The exact API call depends on your CMS.
Triggering the workflow from your CMS
| CMS | Trigger method |
|---|---|
| WordPress | Webhooks plugin or Action hook on publish_post |
| Ghost | Built-in webhooks (Settings → Integrations) |
| Contentful | Webhook on entry.publish |
| Sanity | GROQ-powered webhook |
| Notion | n8n Notion trigger node |
| Static site (GitHub) | GitHub Actions workflow dispatch |
Performance and cost
| Metric | Value |
|---|---|
| Render time | ~230ms per image |
| Output size (typical) | 80–150 KB PNG |
| Cost at 100 posts/month | Free tier (100 renders) |
| Cost at 2,000 posts/month | $9/mo (Starter) |
For most blogs publishing fewer than 100 posts per month, this workflow runs entirely within the free tier.
Variations worth building
- Author-specific templates — include the author’s name and avatar in the OG image
- Category-specific colors — switch the gradient or accent color based on the post category
- Dark/light variants — generate both and pick based on the platform
- Batch regeneration — run the workflow against your entire post archive to backfill missing OG images
Replace Figma with a single fetch call
Full HTML and CSS support. Pre-warmed Chromium — no cold starts. Free tier included.
Also see: Instagram carousel automation with n8n and OG images with Next.js.