fix: run OG image generation in subprocess to prevent OOM kill
The OG generation in the eleventy.before hook consumed too much memory alongside Eleventy's data cascade, causing the Eleventy process to be OOM-killed on Cloudron. Fix by running OG generation in a separate child process with its own 768MB heap limit. Also write the manifest incrementally (every 10 images) to preserve progress if interrupted.
This commit is contained in:
@@ -10,7 +10,6 @@ import { minify } from "html-minifier-terser";
|
||||
import { createHash } from "crypto";
|
||||
import { execFileSync } from "child_process";
|
||||
import { readFileSync, existsSync } from "fs";
|
||||
import { generateOgImages } from "./lib/og.js";
|
||||
import { resolve, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
@@ -526,12 +525,20 @@ export default function (eleventyConfig) {
|
||||
});
|
||||
|
||||
// Generate OpenGraph images for posts without photos
|
||||
eleventyConfig.on("eleventy.before", async () => {
|
||||
eleventyConfig.on("eleventy.before", () => {
|
||||
const contentDir = resolve(__dirname, "content");
|
||||
const cacheDir = resolve(__dirname, ".cache");
|
||||
const siteName = process.env.SITE_NAME || "My IndieWeb Blog";
|
||||
try {
|
||||
await generateOgImages(contentDir, cacheDir, siteName);
|
||||
execFileSync(process.execPath, [
|
||||
resolve(__dirname, "lib", "og-cli.js"),
|
||||
contentDir,
|
||||
cacheDir,
|
||||
siteName,
|
||||
], {
|
||||
stdio: "inherit",
|
||||
env: { ...process.env, NODE_OPTIONS: "--max-old-space-size=768" },
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[og] Image generation failed:", err.message);
|
||||
}
|
||||
|
||||
19
lib/og-cli.js
Normal file
19
lib/og-cli.js
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* CLI entry point for OG image generation.
|
||||
* Runs as a separate process to isolate memory from Eleventy.
|
||||
*
|
||||
* Usage: node lib/og-cli.js <contentDir> <cacheDir> <siteName>
|
||||
*/
|
||||
|
||||
import { generateOgImages } from "./og.js";
|
||||
|
||||
const [contentDir, cacheDir, siteName] = process.argv.slice(2);
|
||||
|
||||
if (!contentDir || !cacheDir || !siteName) {
|
||||
console.error("[og] Usage: node og-cli.js <contentDir> <cacheDir> <siteName>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await generateOgImages(contentDir, cacheDir, siteName);
|
||||
@@ -288,6 +288,7 @@ export async function generateOgImages(contentDir, cacheDir, siteName) {
|
||||
let generated = 0;
|
||||
let skipped = 0;
|
||||
const newManifest = {};
|
||||
const SAVE_INTERVAL = 10;
|
||||
|
||||
for (const filePath of mdFiles) {
|
||||
const raw = readFileSync(filePath, "utf8");
|
||||
@@ -320,6 +321,11 @@ export async function generateOgImages(contentDir, cacheDir, siteName) {
|
||||
writeFileSync(join(ogDir, `${slug}.png`), pngBuffer);
|
||||
newManifest[slug] = { title: slug, hash };
|
||||
generated++;
|
||||
|
||||
// Save manifest periodically to preserve progress
|
||||
if (generated % SAVE_INTERVAL === 0) {
|
||||
writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2));
|
||||
|
||||
Reference in New Issue
Block a user