Using ffmpeg to convert video for Instagram Reels
A practical ffmpeg command reference for converting video into a format Instagram Reels will actually accept.
One ffmpeg pass makes almost any video Reels-compatible: scale and pad to a centered 1080×1920 9:16 frame, encode H.264 with the yuv420p pixel format, AAC audio, a fixed 30fps, and -movflags +faststart to put the moov atom at the front. Verify the result with ffprobe before uploading; Meta has revised Reels duration limits more than once, so reconfirm current numbers.
Reels rejects a surprising share of otherwise-fine video files over encoding details that have nothing to do with content — an odd pixel format, a landscape frame it has to letterbox, metadata sitting at the end of the file instead of the front. ffmpeg fixes every one of those in a single pass. This is a practical command reference, not a theory piece — copy the command, adjust the two file paths, and verify the output before you upload it.
PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.
What does Reels actually require?
Meta's own content-publishing documentation specifies an MP4 or MOV container with the moov atom at the front of the file (the "faststart" property), H.264 or HEVC video, AAC audio, and a recommended 9:16 aspect ratio — video significantly outside that ratio risks being letterboxed or cropped rather than rejected outright. Separately, and worth checking against directly: PostGate's Instagram target currently validates an uploaded video against video/mp4 or video/quicktime, a 300 MB size ceiling, and a 3-second-to-15-minute duration range before it ever reaches Instagram. Duration limits for Reels specifically have been revised by Meta more than once, so treat any fixed number — including this one — as worth reconfirming against Meta's current content-publishing docs rather than assumed permanent.
What is the baseline conversion command?
This single command scales and pads any source to a centered 1080×1920 frame, re-encodes video to H.264 High profile with the standard 8-bit yuv420p pixel format mobile decoders expect, encodes audio to AAC, and relocates the moov atom to the front of the file with +faststart.
ffmpeg -i input.mp4 \
-vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
-c:v libx264 -profile:v high -level 4.2 -crf 20 -preset slow \
-pix_fmt yuv420p -r 30 \
-c:a aac -b:a 128k -ar 44100 -ac 2 \
-movflags +faststart \
output_reel.mp4What is each flag actually doing?
scale=...:force_original_aspect_ratio=decrease fits the source inside 1080×1920 without distorting it; the follow-up pad=... then centers that scaled frame on a black 1080×1920 canvas, so a landscape or square source becomes vertical without stretching. -pix_fmt yuv420p is not optional — many editing tools export 10-bit or 4:2:2 footage that libx264 will otherwise leave in a format some mobile decoders can't play. -crf 20 -preset slow trades encode time for a smaller file at visually comparable quality; drop to -preset fast if you're converting in bulk and file size matters less than turnaround. -movflags +faststart moves file metadata to the front so the video is playable — and processable by the receiving platform's validators — before the full file has downloaded.
How do you trim duration and normalize frame rate?
Add -t 60 before the output filename to cap the exported clip at 60 seconds, or -ss 00:00:05 -t 60 to start 5 seconds in. If your source footage is well below the 3-second floor most Reels-style validation enforces, pad or extend it rather than uploading a clip that's guaranteed to be rejected on duration alone. The -r 30 flag in the command above normalizes frame rate to a fixed 30fps regardless of the source's native rate — useful when combining clips shot at different frame rates, since a mismatched or variable frame rate is a separate, common cause of processing failures on the receiving end.
How do you verify the output before uploading?
Confirm the encode actually did what you expect before sending it anywhere — a single ffprobe call reports codec, pixel format, dimensions, and duration together, so you can check all four against the target spec in one pass instead of discovering a mismatch after a rejected upload.
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,pix_fmt,width,height,r_frame_rate \
-show_entries format=duration \
-of default=noprint_wrappers=1 \
output_reel.mp4
# codec_name=h264
# pix_fmt=yuv420p
# width=1080
# height=1920
# r_frame_rate=30/1
# duration=58.020000What if validation still rejects it?
PostGate revalidates every asset against the target platform's actual capability matrix at ingest, independent of what ffprobe reports locally — see what PostGate's pipeline normalizes automatically versus what stays your responsibility. A rejection surfaces as a structured media_invalid error rather than a generic failure, so check the response body for the specific dimension before re-encoding blind — see Errors & retries for the full error contract.
Frequently asked questions
What video format does Instagram Reels require?
Meta's content-publishing documentation specifies an MP4 or MOV container with the moov atom at the front of the file (the faststart property), H.264 or HEVC video, AAC audio, and a recommended 9:16 aspect ratio — video significantly outside that ratio risks letterboxing or cropping rather than outright rejection. PostGate's Instagram target additionally validates video/mp4 or video/quicktime, a 300 MB size ceiling, and a 3-second-to-15-minute duration range before the file reaches Instagram.
Why does the ffmpeg command need -pix_fmt yuv420p?
Because many editing tools export 10-bit or 4:2:2 footage, and libx264 will otherwise leave the video in a format some mobile decoders cannot play. Forcing the standard 8-bit yuv420p pixel format is not optional — it is one of the encoding details that gets otherwise-fine files rejected even though the codec name looks correct.
What does -movflags +faststart actually do?
It relocates the file metadata (the moov atom) to the front of the file, so the video is playable — and processable by the receiving platform’s validators — before the full file has downloaded. Metadata sitting at the end of the file instead of the front is one of the encoding details Reels rejects files over, independent of the content itself.
How do I verify a video before uploading it?
Run a single ffprobe call that reports codec, pixel format, dimensions, frame rate, and duration together — expect h264, yuv420p, 1080×1920, 30/1, and a duration inside the allowed range. Checking all of those against the target spec in one pass beats discovering a mismatch after a rejected upload attempt.
What if the file is still rejected after converting?
PostGate revalidates every asset against the target platform's actual capability matrix at ingest, independent of what ffprobe reports locally, and a rejection surfaces as a structured media_invalid error rather than a generic failure. Check the response body for the specific dimension that failed before re-encoding blind.
