Skip to content
streamneo.
Troubleshooting15 min read

How to Run an FFmpeg YouTube Stream with systemd

Run FFmpeg as a systemd service for YouTube Live, then monitor the encoder, host, network and YouTube ingest separately.

sn.
StreamNeoPublished 4 October 2026
Worth sharing?

FFmpeg can send a continuous video stream to YouTube while systemd starts it at boot, records its logs and restarts the local process after a failure. That supervision is useful, but it does not prove that YouTube is receiving a healthy stream.

A reliable setup checks four separate places: FFmpeg progress, host resources, outbound network and YouTube ingest. This distinction helps you identify whether the encoder is stuck, the VPS is exhausted, the connection is failing, or YouTube is rejecting or degrading the feed.

Prepare the YouTube stream and FFmpeg command

Open the stream in YouTube Studio and go to the Live Control Room. Copy the stream URL and stream key shown for that broadcast. Use the address supplied for the stream rather than assuming that an older endpoint, hostname or port is still correct. YouTube explains this encoder hand-off in its live streaming setup guidance.

Treat the stream key as a password. Do not put it in a public script, paste it into a support forum or include it in a command that other users can read through process inspection. If you believe it has been exposed, reset it in Live Control Room and replace the protected value used by the service.

Prefer the RTMPS address supplied by YouTube. RTMPS encrypts the connection between FFmpeg and the ingest endpoint. You still need to copy the complete address, including its scheme and hostname. If an SSL connection fails, check the exact address first; YouTube also documents port 443 as a troubleshooting option where it applies to the supplied endpoint. Its RTMPS troubleshooting guidance should take precedence over an old command found in a forum.

Build and test the command interactively before creating a service. The exact input options depend on what you are streaming. A file, a camera, a desktop capture and a generated audio source do not need the same input arguments. FFmpeg documents the relevant RTMP and RTMPS protocol options, but your installed FFmpeg build and input formats still matter.

A representative file-based shape is:

/usr/bin/ffmpeg -re -stream_loop -1 -i /srv/channel/programme.mp4 \
  -c:v libx264 -preset veryfast -b:v 10M -maxrate 10M -bufsize 20M \
  -g 60 -c:a aac -b:a 128k -f flv \
  "rtmps://YOUR-INGEST-ADDRESS/YOUR-STREAM-KEY"

This is a starting pattern, not a universal prescription. Confirm that the input has the expected audio and video streams, and select settings from YouTube's current encoder table. YouTube lists H.264, H.265 and AV1 video options, AAC or MP3 audio, constant bitrate operation and frame rates up to 60 fps. For H.264, its current examples include 10 Mbps for 1080p at 30 fps and 17 Mbps for 1080p at 60 fps. It recommends a two-second keyframe interval and says not to exceed four seconds. These are platform recommendations, not a guarantee that every input or FFmpeg build will behave identically.

The -re option makes a file read at its normal rate rather than as quickly as the machine can decode it. -stream_loop -1 repeats the input, although a single file may still produce a visible or audible join at the loop point. The GOP setting must match the intended frame rate: -g 60 represents a two-second interval at 30 fps, while a 60 fps source would need a different value. Check the actual output frame rate rather than copying that number blindly.

You may be able to use stream copy instead of re-encoding, but only when the source codecs, containers, timestamps, frame rate and audio format are suitable for YouTube and the output muxer. Re-encoding gives you control over the output but consumes CPU. Copying reduces CPU demand but leaves compatibility and timing problems in the source. YouTube's current live encoder requirements are the reference point for that decision.

Check FFmpeg process and progress

Once the interactive command connects, do not stop at seeing a running process. A process can remain present while its input is blocked, its output is stalled or its connection is repeatedly failing. FFmpeg's terminal output and journal are more useful when they show advancing frame counts, timestamps, bitrate and speed.

For a basic process check, use:

systemctl status youtube-stream.service
pgrep -a ffmpeg

For the service journal, use:

journalctl -u youtube-stream.service -n 100 --no-pager
journalctl -u youtube-stream.service -f

The first command shows recent messages. The second follows new entries while you observe a restart or a connection attempt. On some distributions, viewing a system service journal may require appropriate privileges.

Look for progress that changes over time. If the frame number and output timestamp stop advancing, FFmpeg may be alive but not producing useful media. If FFmpeg exits and systemd starts it again, the journal should show the exit and the next start. Repeated starts can indicate a bad input path, an invalid option, an authentication problem, a rejected connection or a resource limit.

Do not hide all FFmpeg output simply to make the journal quiet. Redirecting only errors can make a service look tidy while removing the evidence needed to distinguish a frozen encoder from a working one. At the same time, review the command and logging policy so the stream key is not printed. Use a protected credential source and avoid putting the key in a world-readable unit file.

A useful comparison is the automatic FFmpeg reconnect approach. Reconnection logic can address a dropped transport, but it cannot correct an exhausted VPS, an invalid key or a YouTube-side stream-health warning. Treat it as one response within a larger monitoring plan.

Watch VPS CPU, memory and disk headroom

The second failure place is the host. Re-encoding video can consume substantial CPU, and a machine can appear reachable while it is too busy to deliver frames on time. Memory pressure can trigger process termination or cause severe swapping. A full disk can prevent logs, temporary files or the input from being read correctly.

Check the host while the stream is running, not only immediately after setup:

top
free -h
df -h
systemctl show youtube-stream.service -p MainPID -p MemoryCurrent -p CPUUsageNSec

In top, observe whether FFmpeg is using the expected CPU and whether the load is increasing. High CPU is not automatically a fault: encoding has work to do. The important question is whether the output progresses at the intended speed while the host retains headroom for the operating system and other services.

free -h helps you see available memory and whether swap is being used. A system that slowly moves into swap may continue running while output timing deteriorates. df -h shows whether the filesystem containing the input, logs or temporary data is filling. Also check inode exhaustion if a workflow creates many small files.

Keep the input on a stable local path or mounted volume that remains available after boot. A network-mounted file can introduce a second network dependency and may be unavailable when systemd starts the service. If the input is a large loop file, check that its permissions allow the dedicated service account to read it.

A dedicated unprivileged account limits the damage from a mistake in the command. It also makes ownership and permissions easier to reason about. The account needs access to the input and any credential file, but it should not need an interactive shell or broad administrative access.

If the machine cannot re-encode the chosen resolution and frame rate in real time, lower the output workload, use a less demanding encoder preset, or choose a source that is already compatible. Do not respond to a CPU problem by adding more automatic restarts. Restarting an overloaded encoder leaves the underlying capacity problem unchanged.

For a wider comparison of the operating environment, see VPS versus a spare PC for a 24/7 YouTube stream. The relevant choice is not only purchase cost. It includes power, connectivity, remote access, cooling, storage and who will respond when the stream stops.

Check outbound network health

The third place is the path from the host to YouTube. FFmpeg may be running and the VPS may have spare CPU, yet packets can be delayed, dropped or blocked before they reach the ingest endpoint.

Start with the practical checks. Confirm that the host resolves the hostname used in the current RTMPS address, that outbound connections are allowed, and that the system clock is sensible. Check the firewall and any hosting-provider egress policy. If the address provided by YouTube specifies a port, test that port rather than assuming the default.

For a simple TCP reachability test, an administrator might use a tool such as nc where it is installed:

nc -vz ingest.example.invalid 443

Replace the placeholder with the actual hostname. This tests whether a TCP connection can be made; it does not prove that the TLS handshake, stream authentication or media ingest will succeed. Do not treat a successful ping as proof either. ICMP behaviour and video delivery are separate things.

Watch the host's upload capacity while the service is active. The required capacity depends on the selected video bitrate, audio bitrate, protocol overhead and any other traffic sharing the connection. Leave room for normal variation rather than selecting a bitrate equal to the line's advertised maximum. A speed test is only a snapshot, so compare it with the stream's own behaviour and YouTube's health messages.

A bitrate that looks reasonable on paper can still fail when the route has unstable loss or when another process consumes the uplink. Repeated reconnects, increasing connection errors and long pauses in FFmpeg output point towards transport trouble, but confirm the timing against the host and YouTube layers before changing settings.

If RTMPS produces a timeout or SSL error, re-copy the address from Live Control Room. Check for a missing rtmps scheme, a changed hostname, an accidental space or a port mismatch. Do not switch to an unencrypted endpoint merely because it is shorter. If a supplied RTMPS endpoint supports port 443, that can help where outbound filtering affects another port, but use the current YouTube instructions for the actual address.

Verify YouTube ingest health

YouTube is the fourth and separate failure place. A local process can be active, its counters can advance and the network socket can remain open while YouTube reports a poor or missing stream. The platform sees the received media, its timing, format and health; your shell does not.

Open Live Control Room and check the preview and stream-health messages. Look for the receiving state, video and audio warnings, dropped frames, bitrate observations and any message about the selected resolution or frame rate. The YouTube stream-health guidance recommends a representative test and monitoring these messages rather than relying only on the encoder terminal.

Test with the same type of content you intend to run. A quiet devotional loop, an ambience station and a local news video may have very different motion, audio and scene changes. Confirm that the preview shows both picture and sound, that the output is not delayed indefinitely, and that YouTube accepts the selected codecs and timing.

If the stream is unhealthy, compare four things with the encoder configuration: resolution and frame rate, video and audio codecs, bitrate and keyframe interval. Then compare them with actual FFmpeg output. A command may request one frame rate while the input or filter produces another. A source may contain no audio even though the output settings expect it.

The YouTube event state is also separate from systemd's state. A local restart may create a new connection, but it does not promise a seamless platform-side event. YouTube says streams under 12 hours are automatically archived after the encoder stops sending content. That behaviour should not be confused with a guarantee that a process restart preserves one uninterrupted broadcast.

For a workflow that loops pre-recorded material, YouTube stream disconnections when looping a video covers related symptoms. Use it alongside the current Live Control Room messages, not as a replacement for them.

Set alerts for meaningful failure signals

An alert should tell you that a useful condition has changed, not merely that a process exists. A systemd unit in the active state is a weak signal if FFmpeg is blocked or YouTube is not receiving usable media.

Start with signals available on the host:

Layer Useful signal What it can tell you What it cannot prove
Encoder Service exit, repeated restarts, stalled progress FFmpeg stopped or stopped advancing YouTube is displaying the stream correctly
Host CPU saturation, low available memory, full filesystem The VPS may be unable to encode or log normally The network path is healthy
Network Connection errors, timeout patterns, upload saturation The transport may be failing or constrained YouTube accepted the media
YouTube Offline state, preview failure, health warning The platform is not receiving or liking the feed The local process is healthy

Use systemctl is-active youtube-stream.service for a basic service-state check, but pair it with a progress check. A small monitoring script can record the last observed FFmpeg timestamp and alert when it has not changed for a defined period. Choose that period from your content and connection behaviour; do not copy a threshold without testing it.

Alert on repeated restarts rather than every individual restart. One restart during planned maintenance is different from a cycle that repeats throughout the night. Also alert on a full filesystem, sustained memory pressure and a host that has lost network connectivity.

YouTube-side alerts require checking Live Control Room or an integration that exposes the relevant platform state. Do not invent a platform notification from a local shell command. If you need an external watcher, confirm what it actually reads and whether it can distinguish an offline event from a delayed dashboard update.

Keep alerts actionable. Each alert should name the stream, the observed layer, the first timestamp and the next command or page to inspect. An alert saying “stream down” without evidence encourages random restarts and makes the recovery slower.

Correlate logs across the four layers

When a stream fails overnight, write down a timeline before changing several settings. Start with the first time FFmpeg stopped advancing, the first service exit, the first resource warning, the first network error and the first YouTube health change. The order matters.

For systemd and FFmpeg, use a bounded journal query:

journalctl -u youtube-stream.service --since "2026-10-04 00:00" --until "2026-10-04 06:00" --no-pager

Use the relevant date and time range for your incident. Compare those entries with host monitoring, provider network graphs and the timestamps visible in Live Control Room. A service restart before a YouTube warning suggests one path; a YouTube warning while the process and host remained steady suggests another.

Record configuration changes as well. Note when the stream key was replaced, when the input file changed, when the VPS was rebooted and when a bitrate or codec changed. Without that context, a later healthy run can make the original cause difficult to reproduce.

Keep credentials out of the incident notes. Redact stream keys, signed URLs and private addresses before sharing logs. FFmpeg may print the output URL in an error line, and a unit file or shell history may contain it even when the journal does not.

A good incident record answers four questions: was FFmpeg producing frames, did the host have capacity, could the host deliver traffic, and did YouTube report healthy ingest. If one answer is missing, the next test should gather that evidence rather than restart again.

Respond to an offline or stalled stream

If YouTube is offline and FFmpeg is not running, inspect the service status and recent journal first. Correct a missing input, permission error, invalid option or protected credential problem before starting it again. Then run the command interactively, if practical, so you can see its first connection and output messages.

If FFmpeg is repeatedly restarting, temporarily stop treating restart as the solution. Find the first exit reason. A bad stream key, wrong RTMPS hostname, unavailable file or unsupported codec will produce the same visible symptom as a transient connection problem if you only look at the service state.

If FFmpeg is running but progress has stalled, inspect the input path, CPU, memory and disk. A blocked network-mounted input, a full filesystem or severe swapping can leave the process present without useful output. Compare the last advancing timestamp with the time of the YouTube warning.

If the host is healthy but the connection fails, verify the current RTMPS address and port, then check outbound filtering and upload capacity. If the address or key is wrong, update the protected credential source and restart the service deliberately. If the key may have been exposed, reset it in Live Control Room before resuming.

If YouTube reports an unhealthy stream while local progress continues, review the platform warning rather than adding more restarts. Check the actual output resolution, frame rate, bitrate, codecs, audio presence and keyframe interval. Run a representative preflight test and confirm the preview before leaving the stream unattended.

A systemd unit can be structured like this, with placeholders for your paths and credential method:

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

[Service]
User=streamer
WorkingDirectory=/srv/channel
ExecStart=/usr/bin/ffmpeg [tested input and output arguments]
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Treat this as a shape to adapt, not a tested unit for every Linux distribution. Verify the installed systemd documentation and the directives supported by the target host. The stream key should come from a protected credential mechanism or file with restrictive permissions, not from a publicly readable unit. If the key is passed directly in ExecStart, it may be visible through service metadata or process inspection, so choose the protected approach supported by your distribution and FFmpeg workflow.

After saving the unit, the usual administrative workflow is:

sudo systemctl daemon-reload
sudo systemctl enable youtube-stream.service
sudo systemctl start youtube-stream.service
sudo systemctl status youtube-stream.service
journalctl -u youtube-stream.service -f

Enabling a service controls whether it starts during boot. It does not validate the stream key, confirm that the input is readable or prove that YouTube ingest is healthy. Start it, inspect the local evidence and then verify the preview and health messages in Live Control Room.

If you would rather remove the burden of keeping a Linux process supervised overnight, StreamNeo removes the need to install and maintain FFmpeg and systemd for a YouTube-only channel: you upload the file, provide the YouTube key and let the service run the broadcast while your computer is off.

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

Does an active systemd service prove that YouTube is receiving the stream?

No. It proves only that systemd considers the local process active. FFmpeg can be blocked, resource-starved or disconnected while the process remains present, so check its advancing progress and YouTube's Live Control Room preview and health messages separately.

Should I use RTMP or RTMPS for FFmpeg?

Prefer the RTMPS address provided by YouTube because it encrypts the transport. Copy the current scheme, hostname and port from Live Control Room, and troubleshoot that exact address before changing protocols.

Where should the YouTube stream key go in a systemd setup?

Keep it out of public scripts, source repositories, shell history and ordinary logs. Use a protected credential file or another mechanism supported by the target distribution, restrict its permissions and reset the key in Live Control Room if it has been exposed.

Will Restart=on-failure keep one YouTube broadcast uninterrupted?

It can restart a failed local FFmpeg process, but it cannot guarantee continuous platform-side ingest or preserve one uninterrupted YouTube event. After a restart, verify the new connection and inspect YouTube's stream state rather than assuming that the local service recovery was enough.

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 Troubleshooting guides ↗ · All topics ↗