On this page
The n8n Shorts Factory workflow turns one topic string into a finished, captioned 9:16 MP4. It is 28 nodes: Groq writes a six-scene script, ElevenLabs voices it, one image is generated per scene, and a self-hosted ffmpeg service renders and captions the cut. The finished video arrives by email. The JSON is MIT-licensed.
Every service business owner I talk to has the same unshipped list: the ten things they know they should be posting, and the ten things that never get posted. The ideas are not the problem. Turning one idea into a finished vertical video means a script, a voice, six pictures, an edit, captions and an export, and that is most of an afternoon for something that lives for a day.
Episode 01 of the n8n Workflows series is the machine that removes the afternoon. You hand it one line of text. It writes a six-scene script, voices every scene, generates one vertical image per scene, turns each image into a slow-zoom clip cut to the exact length of its own voiceover, muxes the voice onto the clip, concatenates the six, burns word-highlight captions, and emails you the finished 9:16 MP4 with a title, a description and a tag list. The YouTube upload node is wired at the end and shipped disabled, on purpose. Nothing publishes until a person says so.
If iframes are blocked where you are reading this, the video is at youtu.be/5p7IYgR-c2E.
Specifications
| Item | Value |
|---|---|
| Workflow name |
SF-01 Shorts Factory (script -> voice -> clips -> render
-> upload)
|
| Nodes | 28 |
| Trigger |
Manual Execute in the n8n editor, or POST to the
shorts-factory webhook
|
| Script model | Groq chat completions, forced to a JSON object response |
| Voice | ElevenLabs, one MP3 per scene |
| Images | One generated vertical image per scene, six per video |
| Render and captions | No-Code Architects Toolkit, an open-source media API you self-host |
| Storage | Any S3-compatible bucket (MinIO, Backblaze B2, Wasabi, AWS S3) |
| Delivery | SMTP email with both render URLs, title, description and tags |
| Output | 1080x1920 MP4, word-highlight captions burnt in |
| Public JSON includes |
All 28 nodes, all connections, all prompts and ffmpeg parameters.
Credentials stripped to REPLACE_ME, hosts stripped to
YOUR-NCA-HOST and YOUR-N8N.
|
| Licence | MIT |
How does the n8n Shorts Factory workflow turn a topic into a video?
It runs the topic through five stages in order: script, voice, timing, images, render. Groq writes six scenes as structured JSON, ElevenLabs voices each scene, the media API measures the real length of each MP3, an image is generated per scene, and each image becomes a clip of exactly its own voiceover length before the six are joined and captioned.
You start it one of two ways. In the editor you open the workflow, edit the
Topic node, and press Execute. Or you POST to the
shorts-factory webhook path with a JSON body containing a topic.
The webhook responds the moment it receives the request and runs the rest in
the background, which matters, because the full run is minutes long and no
HTTP client is going to hold that connection open. Both entry points converge
on the same node, Build Groq request.
The timing stage is the part people skip and regret. The clip length is not guessed from reading speed; the media API is asked how long each MP3 actually is, in seconds, and the clip is set to that duration plus 0.4 seconds, rounded up to the nearest tenth. Every later ffmpeg parameter descends from that measurement.
Then the image loop. One scene at a time: wait, request the image, check whether a binary actually came back, and if it did not, wait again and ask again. Once all six images exist the render chain runs. Each image becomes a 30fps slow-zoom clip of exactly its own scene length, the voice is muxed on with a crop that removes the image provider's watermark and a scale-and-crop to 1080x1920, the six clips are concatenated, and captions are burnt on by a Whisper pass on the server. The caption node is deliberately set to continue on error, so a failed caption pass delivers an uncaptioned render rather than destroying nine minutes of work.
| Stage | Node | What it does |
|---|---|---|
| Input | Topic / Topic from webhook |
The topic string plus the system prompt. The only thing you edit. |
| Script | Groq: write script |
HTTP POST to the Groq chat-completions endpoint, forced to a JSON object response. |
| Script | Parse script |
Code. JSON becomes six items, one per scene. Builds the run id and the per-scene image URL. |
| Voice | ElevenLabs: voice |
One MP3 per scene, batch size 1, spaced 800ms apart. |
| Storage | Upload VO to MinIO |
S3 node. Writes to sf/<run_id>-<index>.mp3.
|
| Timing | NCA: VO duration |
Asks the media API for the real length of each MP3 in seconds. |
| Timing | Scene plan |
Code. Pairs duration back to scene. Clip length is voiceover plus 0.4s. |
| Images |
Loop over scenes, Breathe,
Got image?
|
Batch size 1, a 20-second wait, a request, and an explicit retry lane when nothing came back. |
| Render | NCA: image to clip |
Still image becomes a 30fps slow-zoom clip of that scene's length. |
| Render | Mux body, NCA: mux voice |
Crops the watermark, scales and crops to 1080x1920, pads the audio, hard-caps duration. |
| Render | NCA: concatenate |
Six scene clips become one cut. |
| Captions | Caption body, NCA: captions |
Word-highlight style, centred, four words a line, caps. Continues on error. |
| Delivery | Email me the Short |
SMTP. Both URLs, the title, the description, the tags, and the expiry note. |
| Publish | YouTube: upload |
Shipped disabled. Defaults to unlisted if you connect an account and enable it. |
What is real in the public JSON and what is a placeholder?
Every node, connection, prompt and ffmpeg parameter in the public JSON is
real. Only the secrets and the hostnames are placeholders. Credentials ship as
REPLACE_ME, the media-API host reads YOUR-NCA-HOST,
and the webhook example reads YOUR-N8N. Nothing else has been
simplified for publication.
-
Five credentials, all
REPLACE_ME. A Groq header-auth key, an ElevenLabs header-auth key, one media-API key reused across five nodes, an S3 credential for the two upload nodes, and an SMTP credential for the email. Create them in your own n8n and re-select them on the matching nodes after import. The disabled YouTube node needs a sixth only if you enable it. -
YOUR-NCA-HOSTappears in seven places. Five HTTP node URLs, and two object-URL bases inside theScene planCode node. Find and replace all seven or the render chain will fail on the first call. -
The bucket name is
ncaand the prefix issf/. If your bucket is called something else, changebucketNameon both upload nodes.
What did the measured run actually produce?
The run this workflow was built from, during the Episode 01 build published on 2026-09-16, produced a 41-second Short in about nine minutes end to end. Roughly two of those nine minutes were deliberate waiting rather than processing, caused by the image provider's rate limit described below.
I did not record an n8n execution id for that run, so I am not quoting one. Those two figures are what was observed, and nothing beyond them was measured. Your own run time will move with your render box, the length of your script, and whichever image source you point it at.
Which build gotchas cost a failed run?
Six of them, and each is the reason a node is shaped the way it is. Refactor them out and you will re-earn them.
ffmpeg -shortest does not truncate when apad is in the filter chain
This is the one that will take your evening. apad pads audio
indefinitely, so the shortest stream never ends, so
-shortest never fires. Six clips of about five seconds each
produced a 2217-second container. The file was not corrupt and ffmpeg did not
complain. It simply wrote a 37-minute video consisting of a five-second clip
and a very long silence.
The fix is to stop relying on -shortest and pass an explicit
-t per clip, computed from the voiceover duration already
measured a few nodes earlier. That is why the timing branch exists at all. If
your output comes out absurdly long, this is almost certainly the cause.
The anonymous image tier allows exactly one queued request per IP
Parallel calls, or serial calls sent too quickly, come back with a 429 and the
message Queue full for IP. That is the entire reason there is a
loop with a wait node in it rather than node-level batching. Six images
therefore cost about two minutes of pure waiting, which is the price of the
tier rather than a defect in the workflow. Swap in a paid image provider and
you can delete the wait node with it.
n8n caps waitBetweenTries at 5000ms
Related, and worth knowing on its own. A node's built-in retry setting cannot express a 20-second backoff, so the backoff has to be a real Wait node inside a loop. The image node keeps its own three tries at five seconds as a first line of defence, with continue-on-error and always-output-data set, so a hard failure falls through to the IF node instead of killing the execution.
An n8n Code node in per-item mode must return a bare object
return { json: {...} }, not
return [{ json: {...} }]. The array form is only valid in
run-once-for-all-items mode. Mux body is the single per-item node
in this workflow and every other Code node is per-run. Getting this backwards
produces an error that does not obviously point at the mode setting.
Whisper captioning is killed at a 2GB container limit
The caption endpoint runs Whisper on CPU. At a 2GB memory limit the process is killed on the server and the n8n node sees a 502. 3GB works. Give the media-API container at least that much, and leave the caption node on continue-on-error so that when it does fail you still receive the uncaptioned render.
The watermark crop is tied to the image source
The anonymous image tier stamps a bottom-right watermark, which the mux step
removes with crop=in_w:in_h-72:0:0 before scaling. Change the
image source and you must delete that crop, or you will be cutting 72 pixels
off a clean frame for no reason.
How do I run the Shorts Factory workflow myself?
Import the free workflow JSON (MIT) from github.com/waseemnasir2k26/n8n-workflows/tree/main/workflows/01-shorts-factory into your own n8n, create the five credentials, replace the seven host placeholders, and press Execute. It will load with every credential blank and fail loudly until you fill them.
What you have to supply:
- A Groq API key, as header auth.
- An ElevenLabs API key, as header auth, sent raw rather than as a bearer token.
- A deployment of the No-Code Architects Toolkit, the open-source media API this workflow calls for every video operation. The workflow renders nothing itself.
- An S3-compatible bucket that both n8n and the toolkit can read and write, serving objects at a predictable public URL, because the workflow constructs those URLs itself from the run id and the scene index.
- An SMTP host, and a from-address that host is allowed to send as.
On running costs, in general terms: the model, the voice and the images are metered APIs billed to your own accounts, the n8n instance and the media API are servers you host, and the marginal cost of one more video is mostly the text-to-speech characters. No figures from me, because they would be my usage and not yours. Check your own provider dashboards before you run this at volume.
Frequently asked questions
Can n8n make a YouTube Short on its own?
Yes. n8n orchestrates the steps but renders nothing itself. In this workflow n8n calls Groq for the script, ElevenLabs for the voice, an image generator per scene, and a self-hosted media API for every video operation. The result is a 1080x1920 MP4 with captions burnt in, delivered by email.
Does the Shorts Factory workflow upload to YouTube automatically?
No. The YouTube upload node exists at the end of the chain and ships disabled. You connect your own Google OAuth credential and enable it if you want that behaviour. Left as shipped, the workflow emails you the finished file and a person decides whether it is good enough to publish.
Why does the workflow wait 20 seconds between images?
Because the anonymous image tier allows exactly one queued request per IP
address and returns a 429 with the message
Queue full for IP otherwise. n8n caps its own retry backoff at
5000ms, so the 20-second gap has to be a real Wait node inside a loop rather
than a node retry setting.
Why did my ffmpeg output come out far longer than the clips?
Because apad pads audio indefinitely, so
-shortest never fires. During this build, six clips of about five
seconds each produced a 2217-second container with no error. Pass an explicit
-t per clip, computed from the measured voiceover duration,
instead of relying on -shortest.
What do I need to run this workflow in my own n8n?
Five credentials and one self-hosted service: a Groq key, an ElevenLabs key, a
media-API key, an S3-compatible bucket, and an SMTP host, plus your own
deployment of the No-Code Architects Toolkit with at least 3GB of memory.
Every credential in the public JSON ships as REPLACE_ME.
The rest of the series
One workflow per video, the JSON given away under MIT each time, built by Waseem Nasir at SkynetLabs. The other three published episodes:
- Episode 02, n8n speed-to-lead: a form fill gets an AI reply in about two seconds, a second model qualifies the follow-up and proposes a visit window, with nothing free-text-parsed.
- Episode 03, n8n Maps lead harvest: a trade and a city in, a deduped lead table out, keyed on place id, written to Postgres.
- Episode 04, n8n freight quote parser: a quote email in, eight validated fields in Postgres out, with the reply left as a draft.
WhatsApp +92 300 1001957 · Waseem Nasir, SkynetLabs
Hire SkynetLabs, our Top Rated agency on Fiverr: https://www.fiverr.com/agencies/skynetjoellc