Before you start: versions, package names and storage identifiers change over time. Check the commands against the official documentation for your setup before running them, especially anything that creates, deletes or overwrites data.
Introduction
By the end of this guide you will have a self‑hosted n8n instance that runs on a schedule, pulls research data from an external API, sends that data to a language‑model service, receives an article draft, and publishes the draft as a new post on your WordPress site. I estimate the whole process takes about 45 minutes if you already have Docker and a WordPress site ready; otherwise add a few minutes for the initial setup.
More Tutorials & Self-Hosting guides on TechPulseMind →
Prerequisites
Before you begin, make sure you have the following items in place.
| Category | Requirement | Details |
|---|---|---|
| Hardware | A machine that can run Docker | At least 2 GB RAM and 2 CPU cores. n8n and WordPress can share the host if resources allow; otherwise use separate VMs or containers. |
| Operating System | Linux (Ubuntu 22.04 LTS, Debian 12, or similar) or Windows 10/11 with WSL2 | The Docker commands are Linux‑centric; adjust paths if you use Windows PowerShell. |
| Docker | Docker Engine ≥ 20.10 and Docker Compose v2 (the docker compose sub‑command) |
Verify with docker version and docker compose version. |
| WordPress | A reachable WordPress installation with the REST API enabled (default in WP ≥ 4.7) | You need an Application Password (WP ≥ 5.6) or a JWT token from a plugin such as “JWT Authentication for WP‑API”. |
| LLM Provider | An API key for the LLM service you plan to use (OpenAI, Azure OpenAI, HuggingFace Inference API, or a self‑hosted endpoint) | Keep the key secret; you will store it as an n8n credential. |
| Optional Research Source | Access to a search/research API (SerpAPI, Google Custom Search, Bing Search, or a public Wikipedia API) | If you do not have one, you can skip the research step and hard‑code a prompt. |
| Network | Port 5678 TCP open on the host for the n8n UI | Ensure firewall rules allow inbound traffic to this port if you need external access. |
| Permissions | Ability to run docker commands (via the docker Unix group or sudo) |
No root is required inside containers; the official n8n image runs as the node user. |
For the exact latest tags of the n8n Docker image, consult Docker Hub; the same applies to the WordPress version that supports Application Passwords and to the model name you want from your LLM provider. I will point you to the official documentation whenever a version‑specific value is needed.
Step‑by‑step guide
-
Prepare WordPress authentication
First create an Application Password that n8n will use to call the WordPress REST API.
Log in to your WordPress admin dashboard, go to Users → Your Profile (or the user you intend to use for posting), scroll to the Application Passwords section, enter a name such as “n8n‑poster”, and click Add New Application Password. Copy the generated password – it is shown only once.
To verify that the credentials work, run a simple curl request (replace
your-usernameandAPP_PASSWORDwith your values andexample.comwith your site domain):curl -u "your-username:APP_PASSWORD" https://example.com/wp-json/wp/v2/posts?_fields=idYou should receive a JSON array (possibly empty) with an HTTP 200 status. If you see 401, double‑check the username and password.
Note: This step is not destructive; it only creates a password that you can revoke later from the same screen.
-
Deploy n8n via Docker Compose
Create a directory for the project and navigate into it:
mkdir -p ~/n8n-wp && cd ~/n8n-wpCreate a file named
docker-compose.ymlwith the following contents. I use thelatesttag for n8n; you can replace it with a specific tag after checking Docker Hub for the current stable release.version: "3.8" services: n8n: image: n8nio/n8n:latest restart: unless-stopped ports: - "5678:5678" environment: - N8N_HOST=0.0.0.0 - N8N_PORT=5678 - N8N_PROTOCOL=http - EXECUTIONS_PROCESS=main - WEBHOOK_URL=http://:5678/ volumes: - ./n8n_data:/home/node/.n8n Replace
with the hostname or IP address you will use to reach n8n (for example,http://n8n.example.comorhttp://192.168.1.100).Pull the image and start the stack:
docker compose pull docker compose up -dVerify that n8n is reachable by opening
http://in a browser. You should see the n8n login/setup screen. If you get a connection refused or timeout, check the container logs::5678 docker compose logs n8nAlso confirm that the service is up with:
docker compose psYou should see the
n8nservice with stateUp.Note: This step is not destructive; stopping the containers with
docker compose downwill preserve your data in the./n8n_datafolder. -
Create n8n credentials
In the n8n UI, click the key icon in the left‑hand menu to open the Credentials page, then click New Credential.
Add two credentials:
- LLM API – choose the authentication type that matches your provider (most commonly API Key or Header Auth).
Enter a name such asOpenAI API. For OpenAI, set Header Name toAuthorizationand Value toBearer <YOUR_KEY>. - WordPress – choose HTTP Basic Auth.
Name itWordPress API, enter your WordPress username, and paste the Application Password you copied earlier.
After saving each credential, you can test it by using the built‑in Test button (if available) or by creating a temporary HTTP Request node that points to a known endpoint (for example,
https://api.openai.com/v1/modelsfor the LLM orhttps://example.com/wp-json/wp/v2/users/mefor WordPress) and confirming a 200 response.Note: Mistyping the credential details will cause authentication errors later; double‑check before saving.
- LLM API – choose the authentication type that matches your provider (most commonly API Key or Header Auth).
-
Build the workflow
Click Workflows → New Workflow and rename it to something descriptive, e.g.,
Scheduled LLM → WP Publisher.-
Cron Trigger
Add a Trigger node → Cron. Set the interval to every 6 hours (or use a custom cron expression like
0 */6 * * *).To verify, click Execute Node on the Cron node; you should see a green success banner with a timestamp.
-
Research (HTTP Request) – optional
Add an HTTP Request node, name it
Fetch Research. Set Method to GET and enter the URL of your research API (for example, a SerpAPI endpoint).If the API requires an API key, add it as a query parameter or header; do not hard‑code the key – you can pull it from an environment variable or a credential if you prefer.
Set Response Format to JSON. Execute the node and inspect the output; you should see a JSON object containing search results. If you receive 401/403, verify the key and header names.
-
Prepare Prompt for LLM (Function or Set)
Add a Function node, name it
Build LLM Prompt. The following JavaScript assumes the research data is in the first item of the input JSON and extracts a snippet; adjust the field names to match your research API’s response.// Assume research data is in $json[0] from previous node const research = $json[0]?.organic_results?.[0]?.snippet ?? "No research available"; const prompt = `Write a 800‑word, SEO‑friendly blog post about the following topic, using the research points provided:\n\nTopic: "Latest advances in large language models"\nResearch:\n${research}\n\nPlease output only the article body in HTML format (use <p>, <h2>, <h3> tags).`; return [{ json: { prompt } }];Execute the node; you should see a
json.promptstring containing the assembled prompt. -
Call LLM API (HTTP Request)
Add another HTTP Request node, name it
Generate Article via LLM. Set Method to POST.For the URL, use the chat completions endpoint of your provider (for OpenAI:
https://api.openai.com/v1/chat/completions).Under Authentication, select the credential you created for the LLM (e.g., OpenAI API).
Set Header: Content‑Type to
application/json(this is added automatically when you choose JSON body).In the Body field, choose JSON and paste the following template, adjusting the model name to whatever your provider supports (check the provider’s model list for the exact string):
{ "model": "gpt-3.5-turbo", "messages": [ { "role": "system", "content": "You are a helpful assistant that writes blog articles." }, { "role": "user", "content": {{$json["prompt"]}} } ], "temperature": 0.7, "max_tokens": 1500 }Set Response Format to JSON. Execute the node; you should see a JSON response with
choices[0].message.contentcontaining the generated article in HTML. A 401 indicates a credential problem; a 429 means you are hitting rate limits. -
Prepare WordPress payload (Function)
Add a Function node, name it
Build WP Post. Use the following JavaScript to turn the LLM output into a WordPress post object:const content = $json["choices"][0]["message"]["content"]; return [{ json: { title: "AI Generated Article – {{ $now.format('YYYY-MM-DD') }}", content: content, status: "draft", // change to "publish" if you want immediate publishing // optional: categories, tags, featured_media IDs, etc. } }];Execute the node; you should see a JSON object with
title,content, andstatus. -
Publish to WordPress (HTTP Request)
Add the final HTTP Request node, name it
Post to WordPress. Set Method to POST and URL to your WordPress REST API endpoint for posts, e.g.,https://example.com/wp-json/wp/v2/posts.Under Authentication, select the WordPress credential you created earlier.
Set Header: Content‑Type to
application/json.Leave the Body as RAW; the output from the previous Function node will be sent automatically because the nodes are connected.
Execute the node; you should receive a JSON response that includes the new post’s
id,link,date_gmt, etc. Open the link in a browser to confirm the post appears (as draft or publish according to the status you set).
Finally, connect the nodes in this order: Cron → Research → Build LLM Prompt → LLM API → Build WP Post → Post to WordPress. Click Save, then toggle the Activate switch at the top‑right. The workflow list will show a green Active badge and the next scheduled run time.
Note: The Function nodes contain JavaScript that may need tweaking if your research API returns a different structure. I recommend testing each node individually before activating the workflow.
-
Verification
After you have activated the workflow, you can confirm that each milestone works as expected.
- WordPress authentication – Run the curl command from Step 1 again; a successful 200 response confirms the Application Password is valid.
- n8n container running –
docker compose psshows the n8n service with stateUp; the UI is reachable athttp://.:5678 - Credentials stored – In the n8n UI, under Credentials, you see both the LLM API and WordPress API entries with a green check‑indicating they are saved.
- Workflow execution – Go to Executions, select the most recent run, and inspect each node’s output. You should see data flowing from the Cron trigger through to the final WordPress node, ending with a post object.
- Post appears in WordPress – Open the post link returned by the final HTTP Request node; the title and content should match what the LLM generated.
Troubleshooting common failures
Below are the most frequent issues I have seen when building this automation, along with quick fixes.
-
Authentication error (401) from WordPress
Make sure you are using the Application Password, not your regular login password. Also verify that the username has theauthorrole or higher, which is required to create posts via the REST API. -
Authentication error (401) from the LLM provider
Double‑check that the credential’s header is exactlyAuthorization: Bearer <YOUR_KEY>. Some providers expect the key alone; consult their API docs for the exact header format. -
Empty research results
If the HTTP Request node returns an empty array, the prompt will contain “No research available”. Verify the research API endpoint, query parameters, and any required API key. You can also temporarily replace the research node with a Set node that hard‑codes a snippet to ensure the rest of the workflow works. -
LLM returns 429 (rate limit)
Reduce the frequency of the Cron trigger (e.g., run once per day instead of every six hours) or request a higher quota from your provider. You can also add a Wait node with exponential back‑off if you prefer to retry automatically. -
n8n UI fails to load (connection refused)
Check that port 5678 is mapped correctly (docker compose psshows0.0.0.0:5678->5678/tcp). Ensure no firewall is blocking the port and that theWEBHOOK_URLenvironment variable matches the host you are using to reach the UI. -
Post appears with garbled HTML
The LLM node must return plain HTML (no extra markdown fences). If you see<or similar entities, adjust the LLM prompt to ask for raw HTML only, or add a Function node that strips unwanted characters before sending to WordPress.
Conclusion and next steps
You now have a fully automated pipeline that researches a topic, writes an article with a language model, and publishes it to WordPress on a schedule. From here you can extend the workflow in several ways:
- Add a node that extracts featured images from the research results and uploads them to WordPress via the Media endpoint.
- Introduce a conditional branch that sends the draft to a review email before publishing.
- Swap the Cron trigger for a webhook so you can start the pipeline manually from another service.
- Experiment with different LLM models or temperatures to adjust the tone and length of the generated content.
Refer to the official n8n documentation for details on nodes, credentials, and error handling, and consult your LLM provider’s API reference for model‑specific parameters.
Frequently Asked Questions
How do I change the schedule from every six hours to once a day?
Open the Cron trigger node in your workflow, change the interval field to “Every 1 days” or set the cron expression to 0 0 * * * (runs at midnight). Save and reactivate the workflow for the new schedule to take effect.
Can I use a self‑hosted LLM endpoint instead of OpenAI?
Yes. In the LLM API HTTP Request node, set the URL to your endpoint’s chat/completions URL, choose the appropriate authentication (often API Key or Bearer token), and adjust the body JSON to match the endpoint’s expected format. Consult your LLM service’s documentation for the exact payload structure.
What should I do if the WordPress post appears with the content escaped?
This usually means the LLM output contains HTML entities or extra quotation marks. Add a Function node after the LLM node that returns { { json: { content: $json[\"choices\"][0][\"message\"][\"content\"
Some links on this page may be affiliate links. If you buy through them we may earn a commission at no extra cost to you. See our affiliate disclosure.