Skip to content
streamneo.
Setup Guides13 min read

How to Stream a 24/7 YouTube Playlist with FFmpeg Without Re-Encoding

Set up a headless Linux FFmpeg playlist stream for YouTube, with stream-copy limits, systemd supervision, key handling and log checks.

sn.
StreamNeoPublished 4 October 2026
Worth sharing?

FFmpeg can send an existing playlist to YouTube continuously without re-encoding, but only when the files, timestamps, codecs and output container are compatible. The reliable way to set it up is to test the complete command in a terminal first, then run that same command under a Linux service manager.

Stream copying reduces CPU work because FFmpeg passes encoded packets through instead of decoding and encoding them. It does not remove problems in the source files, the network, YouTube ingest or the playlist transition, so you still need validation and monitoring.

Check the FFmpeg build and your files

Start by checking that FFmpeg is installed and that the build includes the protocols and formats you need. On the machine that will run the stream, use:

ffmpeg -version

You are looking for a normal FFmpeg build with support for the FLV muxer and an RTMPS output. You can inspect the available formats and protocols with:

ffmpeg -formats | grep flv
ffmpeg -protocols | grep -E 'rtmp|tls'

The exact output varies between Linux distributions and package sources. If the relevant entries are absent, install a current package from your distribution or follow the FFmpeg project's official documentation rather than downloading an unknown binary.

Before building the service, inspect every file in the playlist. Record at least:

  • video codec and audio codec
  • width and height
  • frame rate and time base
  • number and order of streams
  • audio sample properties
  • duration
  • whether the file contains extra audio, subtitle or data streams

ffprobe, which is distributed with FFmpeg, is useful for this inspection. The important point is not a particular display format but comparing the files consistently. A playlist made from several 1920×1080 H.264/AAC files with the same general timing characteristics has a better chance of being copied cleanly than a mixture of phone recordings, screen captures and downloaded broadcasts.

Stream copy is not a compatibility converter. FFmpeg's explanation of stream copying describes it as copying packets without decoding, filtering or encoding. That means the source streams must already be suitable for the destination. If you need a logo, crop, resize, overlay, frame-rate conversion, loudness change or codec change, the affected stream must be decoded and encoded.

The concat demuxer is intended for an ordered list of compatible files. It is different from joining arbitrary media files together. Matching codecs alone may not be enough if stream layouts, time bases or other timing details differ. If the transition produces errors or a frozen picture, normalise the files before streaming, or accept live transcoding instead of presenting the result as a no-reencode setup.

Test the complete command interactively

Make a small working directory and create a playlist file. A common format is one file entry per line:

file '/srv/stream/media/morning.mp4'
file '/srv/stream/media/afternoon.mp4'
file '/srv/stream/media/night.mp4'

Use paths that the FFmpeg process can read. The concat demuxer's safe-path behaviour depends on how the paths are written and on the command options, so do not copy a list containing untrusted or unusual path syntax without checking it. The FFmpeg concat demuxer documentation explains the format and its limitations.

For a single file that should repeat, the basic pattern is:

ffmpeg -re -stream_loop -1 -i /srv/stream/media/channel.mp4 \
  -map 0:v:0 -map 0:a:0 \
  -c copy -f flv \
  "$YT_SERVER/$YT_KEY"

-stream_loop -1 applies to the input that follows it and asks FFmpeg to read that input repeatedly. -re makes FFmpeg read at the source rate rather than sending the file as quickly as the machine can process it. The -map options select the first video and first audio streams instead of leaving FFmpeg to choose among extra tracks.

For a playlist, use the concat demuxer:

ffmpeg -re -f concat -safe 0 -i /srv/stream/playlist.txt \
  -map 0:v:0 -map 0:a:0 \
  -c copy -f flv \
  "$YT_SERVER/$YT_KEY"

This reads the entries in order. It does not automatically restart at the first entry forever. A playlist that must repeat indefinitely needs an arrangement that reopens the list, or a list-generation and restart strategy. Do not assume that adding -stream_loop -1 to every concat command will solve playlist repetition in the way you expect. Test the exact command and the transition from the final file back to the first file.

Keep the first interactive test private or unlisted where appropriate. Enter the server URL and stream key from YouTube Live Control Room, start the command, then check the preview and stream health there. Let the test run through at least one file transition and listen for audio continuity. A command that works for the first few minutes may still fail when a file ends or its timestamps change.

If a source has no audio, the explicit -map 0:a:0 selection will fail. Do not quietly remove the audio mapping if your intended YouTube profile requires audio. Decide whether to add silence or prepare the source first; the guide on adding silence to an FFmpeg YouTube stream covers that separate case.

Create a supervised Linux service

Once the command works interactively, put it under a service manager. On many Linux systems, systemd is already available. A service makes startup and exit behaviour explicit, gives you a consistent log location, and allows another process to restart FFmpeg after an ordinary process exit.

Create a dedicated account rather than running the stream as root:

sudo useradd --system --home /srv/stream --shell /usr/sbin/nologin stream
sudo mkdir -p /srv/stream/media /srv/stream/logs
sudo chown -R stream:stream /srv/stream

The account and directory commands differ between distributions. Make sure the account can read the media and playlist files, write any intended local logs, and execute the FFmpeg binary. It should not have permission to change unrelated system files.

Create a small wrapper such as /usr/local/bin/youtube-playlist-stream:

#./bin/sh
set -eu

exec /usr/bin/ffmpeg -hide_banner -loglevel info \
  -re -f concat -safe 0 -i /srv/stream/playlist.txt \
  -map 0:v:0 -map 0:a:0 \
  -c copy -f flv \
  "$YT_SERVER/$YT_KEY"

Use the actual path reported by command -v ffmpeg if it is not /usr/bin/ffmpeg. The wrapper keeps the FFmpeg arguments in one place, which makes interactive testing and service execution easier to compare. It also uses exec, so the service manager receives FFmpeg's exit status directly.

Make the wrapper executable and owned by root:

sudo chown root:root /usr/local/bin/youtube-playlist-stream
sudo chmod 755 /usr/local/bin/youtube-playlist-stream

Then create /etc/systemd/system/youtube-playlist.service:

[Unit]
Description=YouTube playlist stream with FFmpeg
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=stream
Group=stream
EnvironmentFile=/etc/youtube-playlist.env
ExecStart=/usr/local/bin/youtube-playlist-stream
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Restart=on-failure is useful when FFmpeg exits with an error. It is not a guarantee of an uninterrupted broadcast. A service manager cannot repair a broken input, an unsuitable codec, a failed network route, an expired key or a YouTube ingest problem. It can only apply the restart policy when the process exits in a condition the policy recognises.

If your playlist reaches its end and FFmpeg exits normally, Restart=on-failure may not restart it. That is one reason to test the repeat design separately. You may need a wrapper that deliberately reopens the playlist, or a different playlist-generation method. Choose that only after confirming the basic input and output command.

Use noninteractive operation and protect the key

A headless stream must not depend on a terminal prompt, a graphical login or a shell session that remains open. The service should have all required paths, permissions and credentials available before it starts.

Create an environment file readable only by root:

sudo sh -c 'umask 077; cat > /etc/youtube-playlist.env <<EOF
YT_SERVER=rtmps://your-server-url-from-youtube
YT_KEY=replace-with-your-stream-key
EOF'
sudo chmod 600 /etc/youtube-playlist.env

Do not put a real stream key in a public script, a screenshot, a tutorial, a Git repository or a support ticket. Treat it like a password. Anyone who obtains it may be able to send content to the broadcast. If you believe it has been exposed, replace or reset it in YouTube Live Control Room and update the environment file.

The example above keeps the key out of the wrapper and playlist. It may still be visible to an administrator inspecting the service environment or process details, so do not treat an environment variable as secret storage. Its practical purpose here is to avoid embedding the key in a file you may edit, copy or publish.

The YouTube setup page explains how to obtain the current server URL and stream key, and how to enter them in an encoder. Check the current YouTube Live encoder instructions before using a production key. YouTube may change the available ingest details, and the value shown in your Live Control Room is the one that matters.

A managed service also needs a predictable working directory and permissions. If you use relative paths in the playlist, set WorkingDirectory=/srv/stream in the unit or use absolute paths throughout. Absolute paths are usually easier to diagnose on a headless machine.

Match the source to YouTube's ingest settings

-c copy does not make a source conform to YouTube. It sends the existing encoded video and audio onwards. Check the source against YouTube's current encoder guidance before deciding that no-reencode streaming is suitable.

YouTube's published live encoder settings list RTMP or RTMPS ingest, H.264, H.265 or AV1 video, AAC or MP3 audio, constant bitrate operation, frame rates up to 60 fps, and a recommended two-second keyframe interval with a four-second maximum. These are ingest requirements and recommendations as listed on YouTube Help on 3 October 2026. A copied file whose keyframes, codec or audio do not fit the selected profile cannot be corrected by the service manager.

For orientation, YouTube's table lists H.264 at 1080p30 with a 5 Mbps minimum and 14 Mbps recommended bitrate, and 720p30 with a 3 Mbps minimum and 8 Mbps recommended bitrate, as listed on YouTube Help on 3 October 2026. Those figures are tied to the resolution and frame rate. Do not apply them to every source, and do not treat them as a measured guarantee of network performance.

Choice What FFmpeg can copy Main condition to check
One repeating file The file's existing video and audio packets The file must suit the chosen YouTube profile and repeat cleanly
Ordered playlist Packets from each compatible file Stream layouts, codecs and timing must remain compatible across transitions
Mixed or repaired files Only streams that already match You may need to normalise or re-encode before streaming
Filtered output Not the filtered stream Filters require decoding, processing and encoding

Upload capacity must exceed the total stream bitrate with room for ordinary network variation. YouTube recommends testing with representative audio and motion, rather than checking only a static opening frame. A devotional loop with a mostly still image and a local news loop with scrolling text can behave differently even when their nominal bitrate is similar.

If your goal is a usable archive, do not rely on one continuous YouTube replay. YouTube says streams under 12 hours can be automatically archived, while streams exceeding 12 hours may not be captured at all, as stated in its archive guidance on 3 October 2026. Keep a local recording or schedule shorter sessions if the archive matters. A local recording also gives you evidence of what the source machine actually sent when a viewer reports a problem.

For broader operational planning, the bandwidth limits comparison for 24/7 YouTube streaming explains why a low-CPU stream can still consume a steady amount of outbound data. Stream copy reduces encoding work, but it does not remove the need to read the files, mux the output, transmit it and supervise the process.

Start the service and verify YouTube receives it

Reload the unit definitions and start the service only after checking the file paths and permissions:

sudo systemctl daemon-reload
sudo systemctl enable youtube-playlist.service
sudo systemctl start youtube-playlist.service
sudo systemctl status youtube-playlist.service

enable arranges for the service to start during the relevant boot target. It does not prove that FFmpeg will connect successfully after reboot. Test a reboot or a controlled stop and start before treating the setup as unattended.

In YouTube Live Control Room, check that the preview displays the expected picture and that the stream health panel is receiving data without warnings. Watch a transition between two representative files. Confirm that video continues, audio is present, and the next file begins in the expected order.

Also check the machine itself. Observe CPU, memory, disk space and network traffic while the service runs. Stream copy normally avoids the main cost of video encoding, but FFmpeg still has to demux files, handle timestamps, create the output and send it continuously. A small computer may be entirely adequate for one compatible stream, while a machine handling several streams or local recordings has a different workload.

A server that has no display can still be tested carefully. Use the preview in YouTube, inspect the service status, and save enough logs to compare a healthy run with a failed one. If you need scheduled starts rather than an always-running service, YouTube's current scheduling guidance is covered separately in how to schedule a 24/7 YouTube Live stream.

A scheduled or restarted process is not the same thing as an end-to-end health check. The process can remain alive while input timestamps are problematic, the connection is impaired, or YouTube is not accepting the stream as expected. Plan a human check, an external alert, or both if the channel matters overnight.

Inspect logs when the stream stops

Start with the service status and recent journal entries:

sudo systemctl status youtube-playlist.service
sudo journalctl -u youtube-playlist.service -n 100 --no-pager
sudo journalctl -u youtube-playlist.service -f

The last command follows new entries while you reproduce a problem. Look for the first meaningful error, not only the final line saying that the process exited. Useful clues include an inability to open a media file, an invalid playlist entry, an unmapped audio stream, a timestamp or muxing error, a TLS or connection failure, and a rejected output connection.

If the service restarts repeatedly, stop it before making changes so that the log is easier to read:

sudo systemctl stop youtube-playlist.service

Then run the same wrapper command manually as the stream account, without exposing the key in shell history. This separates permission problems from network or media problems. After each change, test one variable at a time: first one known-good file, then the playlist, then the unattended service.

A failure at the first file points to input access, credentials, output settings or the source format. A failure only at a transition points more strongly towards concat compatibility, timestamps or a damaged next file. A connection that starts but later drops may involve the network, YouTube ingest or a source that ends unexpectedly. These are different faults even though a restart policy may treat them all as process exits.

Keep log retention practical. FFmpeg can produce substantial output over a long period, especially at a more verbose log level. Use your distribution's journal retention settings or redirect logs deliberately, and ensure that the service account cannot fill the entire disk with a recording or an unbounded log. A full disk can prevent both useful logging and normal operation.

For content questions, technical logs are not enough. Copyright claims, repeated content and channel eligibility are separate YouTube matters. Read the current official policies and, where relevant, the guide on avoiding copyright claims on a YouTube radio station livestream. A successful FFmpeg connection does not establish that the content may be broadcast or that a channel will qualify for any programme.

If the operational burden is the part you do not want to maintain, StreamNeo removes the need to keep a Linux machine, service unit and stream process running yourself: upload the video, provide the YouTube key, and let the hosted stream handle the continuous process with monitoring and automatic restarts. You still need to check your content, credentials and YouTube account, and it remains a YouTube-only option.

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

Can FFmpeg loop an MP4 forever with -c copy?

Yes, a single compatible file can be read repeatedly with -stream_loop -1 and sent with -c copy. The file still needs suitable codecs, timing and output compatibility, and a repeated process does not resolve network or YouTube ingest failures.

Can I concatenate any MP4 files without re-encoding?

No. The concat demuxer works best when the files have matching stream layouts and compatible codecs and timing. If a transition fails, prepare consistent files first or re-encode the affected material.

Does systemd make a 24/7 stream reliable?

It can start FFmpeg at boot and restart it after some process failures. It cannot repair an invalid input, a broken connection, an unsuitable encoder profile or a YouTube-side rejection, so verification and monitoring remain necessary.

Why did YouTube not keep my 24/7 replay?

YouTube says streams exceeding 12 hours may not be captured at all. Keep a local recording or divide the broadcast into shorter planned sessions when a replay is important.

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 ↗