Skip to content
streamneo.
Setup Guides13 min read

How to Set Up an FFmpeg YouTube Stream with a Cron Job on Linux

Schedule an FFmpeg YouTube stream with cron on Linux, with secure keys, absolute paths, logs, testing and separate process supervision.

sn.
StreamNeoPublished 4 October 2026
Worth sharing?

A cron job can start an FFmpeg command at a chosen time, but it cannot tell you whether YouTube accepted the feed or keep the process healthy afterwards. To make the setup dependable, prepare the YouTube encoder stream first, test the complete FFmpeg command manually, then schedule the same command with absolute paths and useful logs.

The important distinction is between launching a process and operating a live channel. Cron is a scheduler. It does not replace Live Control Room checks, process supervision, or a plan for what should happen when the input ends or the network connection fails.

Prepare a YouTube encoder stream

Before writing a crontab entry, make sure the channel can use live streaming. YouTube says the channel must be verified, must not have live-streaming restrictions in the previous 90 days, and the account holder must meet its age requirement. Check the current YouTube live-streaming eligibility guidance rather than diagnosing FFmpeg when the channel itself is not ready.

Open YouTube Studio and enter Live Control Room. Create a stream or select a scheduled stream, then choose the encoder workflow. YouTube will provide the ingest URL and stream key for that stream. The destination, privacy setting and schedule should match what you intend to run from Linux.

Treat the stream key as a password. Do not paste it into a public repository, a screenshot, a support forum, or a log that other users can read. It can also appear in shell history or process listings, depending on how the command is launched. If you think it has been exposed, reset it in Live Control Room and update the protected configuration used by FFmpeg. YouTube documents the process in its encoder stream key instructions.

YouTube recommends RTMPS for secure ingestion. RTMPS is RTMP carried over TLS or SSL, so it is not interchangeable with every plain RTMP endpoint or every FFmpeg deployment. Copy the exact server URL shown by Live Control Room. If your build supports the required protocol, prefer the rtmps:// address; if it does not, investigate the build and deployment before changing the endpoint. YouTube explains how to find the RTMPS URL in its live encoder settings.

The encoder settings also need to suit the actual file and upload connection. YouTube’s current guidance covers H.264, H.265 and AV1 video, AAC or MP3 audio, constant bitrate encoding, frame rates up to 60 fps, and a recommended two-second keyframe interval that should not exceed four seconds. Use the current YouTube encoder settings guide for the selected resolution, codec and frame rate rather than copying a bitrate from an unrelated stream.

For a devotional loop, local news sequence or study video, test the same material you will schedule. A short test file with a different frame rate or silent audio can hide problems that only appear in the overnight stream.

Build and test the FFmpeg command manually

First establish a command that works from an interactive shell. The general FFmpeg structure is global options, input options and input, output options, then the output URL. A file, device, generated source and network input each need different input options, so there is no single command that is correct for all channels.

The following is a structural example. Every capitalised value is a placeholder, not a value to copy literally, and the stream key below is deliberately not real:

/usr/bin/ffmpeg -nostdin -re -i /absolute/path/to/input \
  -c:v libx264 -preset veryfast -b:v VIDEO_BITRATE \
  -maxrate VIDEO_BITRATE -bufsize VIDEO_BUFFER \
  -g KEYFRAME_INTERVAL -keyint_min KEYFRAME_INTERVAL \
  -c:a aac -b:a AUDIO_BITRATE \
  -f flv 'rtmps://YOUTUBE_INGEST_URL/YOUR_STREAM_KEY'

For a pre-recorded file, -re asks FFmpeg to read it at approximately its natural playback rate instead of consuming the file as quickly as the machine can process it. That is generally relevant to a live output, but it does not turn a finite file into an endless broadcast. If the file ends, FFmpeg may end as well unless you build looping into the input or use a playlist and a wrapper suited to that job.

Choose the video bitrate, audio bitrate, resolution, frame rate and keyframe interval together. The placeholder KEYFRAME_INTERVAL should reflect the frame rate and YouTube’s current guidance. The buffer value must also be appropriate for the selected output. Review the available encoders and options in the local FFmpeg build rather than assuming that libx264, AAC or RTMPS is present everywhere.

Run the command manually with the real input and the current URL from Live Control Room. Watch both the terminal output and the YouTube preview. A process that prints progress locally has only shown that FFmpeg started processing; it has not proved that YouTube has accepted a healthy feed.

FFmpeg’s official FAQ recommends using -nostdin, or redirecting standard input from /dev/null, for background execution. This prevents a long-running process from waiting for console input that will not exist when cron launches it. Do not remove that detail simply because the command worked in your terminal.

You can also keep the key outside the main script and restrict the permissions on the file containing it. The exact method depends on the account and deployment, but the principle is consistent: the command needs access to the credential while ordinary users and diagnostic logs should not receive it.

Choose the Linux account and absolute paths

Cron runs the command as the owner of the crontab. Decide that account before scheduling anything. It must be able to read the media file, execute FFmpeg, read any configuration containing the key, write the log, and access the network. A command that succeeds as your login user may fail when installed in another user’s crontab.

This is particularly easy to miss on a shared VPS. You may test as an administrator, install the job for a service account, and then discover that the service account cannot read /home/example/videos/channel.mp4. The reverse can also happen: a broad permission change makes the stream run but exposes the key or media to users who should not see them.

Use absolute paths for the executable, shell script, input files, configuration files and logs. For example, use /usr/bin/ffmpeg rather than ffmpeg, and /srv/stream/input.mp4 rather than input.mp4. The working directory used by cron is not the directory you happen to be in when editing the crontab.

A small wrapper script is usually easier to inspect than a long, quoted crontab line. Give it a clear location, such as /usr/local/bin/start-channel.sh, and make its assumptions visible:

#./bin/sh

exec /usr/bin/ffmpeg -nostdin -re \
  -i /srv/channel/input.mp4 \
  -c:v libx264 -preset veryfast \
  -b:v VIDEO_BITRATE -maxrate VIDEO_BITRATE -bufsize VIDEO_BUFFER \
  -g KEYFRAME_INTERVAL -keyint_min KEYFRAME_INTERVAL \
  -c:a aac -b:a AUDIO_BITRATE \
  -f flv 'rtmps://YOUTUBE_INGEST_URL/YOUR_STREAM_KEY'

The values in this example remain placeholders. In a real setup, consider keeping the stream key in a separate protected file and having the wrapper read it without printing it. Check the resulting permissions and test the wrapper directly as the same Linux account that will own the crontab.

If the stream is running from a VPS, check the file system and network assumptions as well as the command. A useful comparison is the practical discussion in how to host a 24/7 study stream on an Indian VPS, especially if the machine is also serving other tasks. A VPS gives you control over the process, but it also leaves permissions, updates, storage and recovery in your hands.

Create a cron schedule

Edit the crontab for the chosen account with crontab -e. A user crontab has five time and date fields followed by the command. For example:

0 6 * * * /usr/local/bin/start-channel.sh >> /var/log/channel-ffmpeg.log 2>&1

This illustrative entry asks cron to launch the wrapper at a particular minute and hour each day. Replace the schedule with the required start time and confirm the server’s time zone. A system configured for UTC will not behave like a laptop configured for India Standard Time simply because the channel owner is in India.

Do not treat the schedule as a duration control. If the previous FFmpeg process is still running when the next invocation arrives, cron can start another instance unless you add a separate duplicate-prevention design. Two encoders using the same key can produce confusing results and make it difficult to identify which process is writing to the log.

For a finite daily broadcast, decide how the process should stop. You might use a finite input, a wrapper with explicit lifecycle handling, or a service design that owns the process. The correct choice depends on whether the channel should stop at the end of a file, continue through a playlist, or run until deliberately stopped.

Cron’s role is to request a launch at the scheduled time. It does not know whether the command reached YouTube, whether the feed is visible, or whether FFmpeg later exited. This is why a crontab that looks syntactically correct can still produce a channel that was absent by morning.

Before relying on the schedule, run the wrapper manually as the crontab owner. Then trigger it through the same cron path at a convenient test time, using a temporary schedule if necessary. The test should exercise permissions, paths, environment variables, logging and network access, not merely the FFmpeg command copied into a terminal.

Capture output and account for cron’s environment

Cron provides a sparse environment. The documented default shell is /bin/sh; it does not promise the interactive login shell, your graphical session, your current working directory, or the PATH assembled by a profile. The crontab owner’s environment also matters, and the target distribution’s cron implementation should be checked where behaviour differs.

This is why absolute paths are more than a style preference. A script relying on ffmpeg, python, yt-dlp, a relative input name or an unexported variable can work interactively and fail silently under cron. If a variable is required, set it explicitly in the wrapper or crontab after considering whether it contains a secret.

Redirect both standard output and standard error to a protected log while testing:

0 6 * * * /usr/local/bin/start-channel.sh >> /var/log/channel-ffmpeg.log 2>&1

Make sure the crontab owner can write that path. If not, choose a log location owned by the account or configure the system’s logging arrangement. A log that cron cannot open can hide the original failure. Cron may also mail command output to the owner or a configured MAILTO address, but relying on mail without checking the local configuration leaves a gap in the diagnosis. The crontab documentation describes the schedule format, environment and output handling.

Log enough to identify the failure without logging the stream key. FFmpeg’s normal diagnostics can show input discovery, codec selection, connection errors and termination reasons, but review them before sharing them with somebody else. Scrub URLs or credentials if the command line appears in the output.

A useful first check is whether the cron event happened at all. Confirm the scheduler’s service is running on the distribution, inspect the system’s cron records where available, and then read the FFmpeg log. Separate these questions:

  • Did cron launch the wrapper?
  • Did the wrapper find FFmpeg and the input?
  • Did FFmpeg open and encode the input?
  • Did it connect to the exact YouTube endpoint?
  • Did YouTube show a usable preview and acceptable health?

Each question has a different evidence source. Combining them into “the cron job ran” makes troubleshooting slower.

Test that YouTube receives the stream

The operational test is in Live Control Room, not only in the Linux terminal. YouTube recommends testing the stream, checking the preview and reviewing stream-health messages. Use audio and movement similar to the real channel so that a silent or static test does not conceal an input problem.

Start with the manual command and confirm that the preview appears. Check that the video is moving, the audio is present when expected, and the reported health does not show an issue that the terminal has missed. Then stop that test cleanly and exercise the scheduled path with the same input, account and wrapper.

A successful cron launch is not confirmation that YouTube accepted the feed. An FFmpeg process can exist while the URL is wrong, the key is stale, the protocol is unsupported by the local build, the input is unreadable, or the output is unsuitable for the selected stream settings. Conversely, a connection failure may be visible in the log before the Live Control Room message catches up.

If the connection fails, check the exact ingest URL and key first. Confirm that the URL uses the protocol displayed for the stream and that the installed FFmpeg build has the required support. If an SSL error persists, YouTube’s connection guidance discusses port 443 as a possible setting to investigate. Do not replace the current URL with a guessed endpoint.

Then check the Linux side: file permissions, free storage, the server clock, outbound network access, firewall rules, and whether another FFmpeg process is already using the key. If the key may have leaked, reset it in YouTube Studio before continuing and update the protected configuration. Never print the replacement key while troubleshooting.

For channels that loop religious videos, ambient footage or a local information sequence, confirm that the input behaves at the point where the overnight run is expected to continue. A manual test that lasts only long enough to show the first frame cannot demonstrate what happens when a file ends, a playlist changes, or the network briefly disappears.

Plan supervision separately from scheduling

Cron is suitable for fixed-time launches. It is not a long-running process supervisor. It does not restart FFmpeg after a crash, prevent every duplicate instance, confirm that YouTube accepted the feed, monitor stream health, or guarantee a controlled shutdown.

If the requirement is “start this at six in the morning”, cron may be part of the solution. If the requirement is “keep this channel operating, restart it after failure, order it after another service, and record its lifecycle”, evaluate a service manager or a purpose-built wrapper separately. A service manager can own restart policy and dependencies, while a scheduler can request a start at a chosen time. They solve different problems.

FFmpeg options can help with particular failure modes, but they do not turn a command into a complete operations plan. For example, FFmpeg documentation includes a FIFO output example that attempts recovery after a temporary network failure. That is a component behaviour, not proof that the whole channel will recover correctly or that YouTube will accept the resulting feed.

Avoid adding a second scheduler or watchdog before you know what the first one does. Overlapping cron entries, a shell loop, a service manager and an external monitoring script can all restart the same command. Keep one clear owner for starting the process and document how another operator can stop it.

If your main concern is that a personal computer must remain switched on, compare that operational burden with the alternatives discussed in why a YouTube livestream stops when the computer is turned off. If you are deciding between a local Linux process and a hosted workflow, the real cost of 24/7 streaming is a useful way to separate electricity, hardware, maintenance and cloud costs without assuming that one arrangement suits every channel.

A hosted workflow such as StreamNeo removes the need to leave your own computer running: you upload the video, provide the YouTube stream key, and the broadcast can run from the service with monitoring and automatic restarts. It remains YouTube-only, and you should still test the channel and check the current operating terms before relying on it for an important schedule.

Before committing, compare the operating options on the pricing page. When the file and channel are ready, start free — 24-hour trial, no card.

FAQ

How do I stream a video to YouTube with FFmpeg?

Prepare an encoder stream in YouTube Live Control Room, copy its current ingest URL and key, then build an FFmpeg command for the actual input file or source. Test the command manually and confirm the preview and health in Live Control Room before scheduling it.

How do I schedule an FFmpeg stream with cron?

Put a wrapper with absolute paths in the crontab of the Linux account that should own the process. Redirect output to a protected log and remember that cron starts the command at the specified time but does not supervise it afterwards.

How do I keep FFmpeg running in the background?

Use -nostdin or redirect standard input from /dev/null, and choose a process supervisor if restart policy or continuous monitoring is required. Cron alone is not a process supervisor and cannot confirm that the YouTube feed remains healthy.

Why does my cron FFmpeg job fail?

Start by checking the account, executable path, input permissions, working-directory assumptions, environment variables and captured stderr. Then check the exact YouTube URL, current key, protocol support and Live Control Room preview, because a locally running FFmpeg process does not prove that YouTube accepted the stream.

YOU’VE REACHED THE END

Keep the ideas coming.

More guides, useful tools and a little help for your next broadcast.

Back to the journal ↗
YOUR NEXT READ

A little more to explore.

More Setup Guides guides ↗ · All topics ↗