How I built a fully automated blog writing workflow with n8n and local LLMs


I get ideas for blog posts at the worst times. Usually when I am in the middle of debugging a weird hydration error in Next.js or cooking dinner. If I don’t write them down immediately, they vanish. But sitting down to draft, format, and organize those thoughts into a cohesive Markdown file with frontmatter feels like a chore.

To solve this, I built an automated pipeline. Now, I send a voice note or a quick text message to a webhook, and a fully formatted Markdown draft lands in my GitHub repository. No manual copy-pasting, no messing around with YAML metadata.

Here is how I set it up using self-hosted n8n, Ollama running llama3.1 locally, and the GitHub API.

The entry point: Catching the brain dump

I needed a friction-free way to trigger the workflow. I settled on a simple n8n webhook node. I exposed this webhook using my Cloudflare Tunnel setup.

To send data to it, I use an iOS Shortcut on my phone. When I run the shortcut, it asks for text input. If I am driving or walking, I use the dictation feature to just ramble for two minutes. The shortcut takes that raw text and sends a POST request to my n8n webhook with a JSON payload:

{
  "raw_idea": "We should talk about why we stopped using serverless databases for small hobby projects and went back to SQLite on a single VPS. Mention the cold start times and the latency when querying from Vercel edge functions."
} 

Refining the raw dump with local AI

Once n8n receives the payload, the real work begins. I don’t use OpenAI for this. I have an old RTX 3060 Ti running in a home server under my desk, so I run Ollama locally. It costs me nothing to run queries, and I don’t have to worry about API keys or monthly limits.

In n8n, I drag in the Ollama node. I connect it to my local Ollama instance over the local network (http://192.168.1.105:11434).

I set the model to llama3.1:8b and use a system prompt that forces the model to act as a technical editor. The prompt is specific:

You are an editor for a technical blog written by a software engineer. 
Take the raw, chaotic input provided by the user and turn it into a structured Markdown draft. 
Do not use corporate jargon. Write in a direct, opinionated, first-person voice.
Output ONLY valid Markdown. 
Include a YAML frontmatter block at the very top with 'title', 'date', and 'tags'.

Because LLMs sometimes get chatty and add conversational filler like “Here is your draft:”, I use a basic JavaScript code node in n8n right after the Ollama node. This node uses a regex to strip out any text that exists outside the main triple-backtick Markdown block or the YAML frontmatter.

The GitHub integration

Now I have the clean Markdown content stored in an n8n variable. The next step is getting it into my website’s repository.

My personal site is built with Astro, and it reads content from a src/content/posts/ directory. Each post needs to be its own .md file.

To automate this, I use the GitHub node in n8n.

First, I generate a slug. I use another small JavaScript node to convert the title generated by Ollama into a kebab-case string. If the LLM generates “Why SQLite is Great”, my code converts it to why-sqlite-is-great.md.

Next, the GitHub node performs a CREATE action. It targets my blog repository, points to the main branch, and writes the file to src/content/posts/{{ $json.slug }}.md. The content of the file is the exact output from my cleaning node.

Why this setup actually works

I tried doing this with pre-bundled automation tools before, but they always felt brittle. With n8n, I have complete control over the logic. If my local Ollama server is offline, I can configure n8n to send a fallback notification to my Discord channel using a Discord webhook node, telling me to turn my server on.

I don’t auto-publish these files directly to production. The GitHub push triggers a preview deployment on Cloudflare Pages. I get a preview URL sent to my phone. I can read through the draft, make minor edits to the Markdown file directly in the GitHub web editor if needed, and merge it to main when I am happy with it.

This removes the initial intimidation of a blank text editor. I can go from a vague thought in my head to a formatted draft in my repository in under sixty seconds, without opening VS Code.