A systemd service can start FFmpeg at boot and restart it after some process failures on a Vultr Linux host. That supervision does not prove YouTube is receiving valid video and audio, so you must check the broadcast separately in Live Control Room and from a viewer’s perspective.
The practical setup is to prepare a host for the specific media workload, get the ingest URL and stream key from YouTube Live Control Room, run FFmpeg in the foreground under systemd, and test both process recovery and stream recovery before relying on it. There is no universally correct server size or turnkey command: the source, codec, resolution, frame rate, and whether you transcode all matter.
Prepare the Vultr Linux host
Start by deciding what FFmpeg will send. A video file that loops indefinitely has different requirements from a capture device, generated visuals, or a playlist that changes on a schedule. Write down the input path or capture source, whether it includes audio, the output resolution and frame rate, the codec, and whether FFmpeg will copy the existing streams or encode them again. These are workload decisions, not details systemd can infer.
Create a Linux instance with a supported distribution release, enough storage for the media and logs, and network capacity appropriate to the intended upload. Do not choose an instance solely because an example used it. Vultr’s Ubuntu and OBS guide gives a profile for that scenario, but it does not establish a minimum for your FFmpeg encode or confirm that a particular instance can sustain your upload. A stream-copy job and a software transcode can place very different demands on the host.
Install FFmpeg from the distribution’s supported package source or another build you trust. Check the installed version and available encoders and protocols before building a service around it. For example, a command using a particular video encoder will fail if that encoder is absent, and an RTMPS output requires a build with the relevant protocol support. Use documentation for your distribution’s current release rather than copying old package instructions without checking them.
You will also need a stable location for the media, a service account with permission to read it, and a way to inspect logs remotely. Keep the machine’s software updated according to your normal maintenance process. If you change the input file or FFmpeg arguments later, test the new command interactively before restarting the service; a service restart can faithfully repeat a bad configuration.
If the source is a single file, a loop may be all you need. If it is a rotating playlist or a sequence of episodes, plan how transitions and repeated items should work before starting the service. The guide to streaming a looping ambience video with FFmpeg covers the file-loop problem in more depth. For a changing story playlist, also consider how to prevent duplicate episodes; systemd will not manage editorial order for you.
Get YouTube ingest details
Set up or select the live stream in YouTube Live Control Room, then copy the server URL and stream key shown for that stream. YouTube explains that a stream key is password-like: it identifies where the encoder sends the feed and enables YouTube to accept it. Treat it as a credential. Do not put it in a public script, paste it into a support forum, include it in a screenshot, or leave it in shell history. If it is exposed, reset it in YouTube and update the service configuration.
Use the RTMPS URL provided in Live Control Room when available and supported by your FFmpeg build. Do not assume that a generic address copied from another channel or an old tutorial is interchangeable with the URL shown for your stream. YouTube describes RTMPS as a secure ingest option, and FFmpeg documents RTMPS as streaming over a secure SSL connection. You can read YouTube’s live encoder guidance and FFmpeg’s protocol documentation for the current details.
Some channels need to enable live streaming before they can send a broadcast; first-time enablement can take time. Complete that step well before the day you plan to rely on the service. If this is new to you, the walkthrough on finding your YouTube stream key explains the Control Room side. Confirm you are working with the intended channel and stream, especially if you manage more than one.
Store the key outside the command line itself. A root-owned environment file is one practical approach: restrict its permissions so ordinary local users cannot read it, and make the systemd unit reference that file. The exact secret-storage method depends on your environment, but the principle does not: anyone who can read a broadly accessible unit file or its logs may be able to copy a key embedded there. Avoid shell tracing or diagnostic output that prints the credential.
Before proceeding, confirm you can identify three separate things: the stream URL, the key, and the YouTube stream entry that should receive the output. That avoids a common failure in which FFmpeg connects successfully to a different stream or the wrong channel, while the intended Control Room preview remains empty.
Create an FFmpeg command
Build and test the FFmpeg command as the same Linux user that will run the service. Keep it in the foreground; do not append &, use nohup, or start a detached process. systemd needs to track the actual FFmpeg process as the service’s main process so it can observe exit status, send signals, and apply restart policy.
The command depends on the input. A looping file might begin with a structure like this, but it is only a shape, not a tested turnkey command:
ffmpeg -re -stream_loop -1 -i /path/to/input.mp4 [explicit video/audio options] -f flv "$YOUTUBE_RTMPS_URL/$YOUTUBE_STREAM_KEY"
Replace the bracketed portion with real options appropriate to the source. Check that the input has the audio and video streams you expect and map them deliberately if the file contains multiple tracks. If copying compatible streams, use stream-copy options rather than encoding options; if YouTube’s output requirements or your source require conversion, choose encoders and settings that the installed build supports. The -re option is commonly used to read a file at its native rate for live output, while a capture input has its own pacing and device options. Do not combine example fragments blindly.
YouTube’s current encoder guidance recommends constant bitrate (CBR) and a two-second keyframe interval, not exceeding four seconds. It gives codec-specific bitrate guidance, so select a value based on the codec, resolution, frame rate, and what the source and upload can sustain. For H.264, its table lists 1080p at 30 fps at 5 Mbps minimum and 14 Mbps recommended, while 720p at 30 fps is listed at 2 Mbps minimum and 8 Mbps recommended. Those are YouTube recommendations, not a promise that your Vultr host or network can encode and deliver them continuously. Check the current table rather than carrying figures forward from an old command.
Test the exact command manually before writing the unit. Verify that FFmpeg opens the input, finds the expected streams, and connects to the supplied destination. Do not paste a real key into a terminal command if that would expose it in shell history or process listings on your system. Use a protected environment file or another secret mechanism and expand variables in a way the command actually receives; systemd does not automatically interpret shell syntax in every unit field.
If FFmpeg exits immediately, inspect the error before adding a restart loop. Common causes include a wrong input path, unreadable media, unsupported encoder, invalid option, expired or mistaken credentials, or an incompatible output URL. A restart policy can turn one error into repeated attempts, but it cannot correct any of these causes.
Define a systemd service
Create a unit under the system’s systemd service directory, using a clear service name such as youtube-live.service. Choose a dedicated, non-root user where practical, and ensure that account can read the media and any required configuration file. Set a working directory only if your command relies on relative paths; absolute paths are easier to reason about when diagnosing a failure.
A minimal unit structure could look like this:
[Unit]
Description=FFmpeg YouTube live stream
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=stream
WorkingDirectory=/srv/stream
EnvironmentFile=/etc/youtube-live.env
ExecStart=/usr/bin/ffmpeg [your explicit input and output options]
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Treat this as a template, not a complete service for every input. Replace the executable path and ExecStart arguments with the command you tested, and check the unit syntax for the target distribution. Do not put a shell pipeline, redirection, variable expansion, or & into ExecStart expecting a shell to interpret it. If shell behavior is genuinely needed, invoke a shell explicitly and understand what that changes; for a normal FFmpeg job, direct execution is simpler.
Restart=on-failure asks systemd to restart FFmpeg when it exits unsuccessfully, and RestartSec= inserts a delay between attempts. A deliberate systemctl stop is not a failure and is not automatically undone. Repeated rapid failures may also encounter systemd start-rate limits, after which the unit can be marked failed instead of retrying indefinitely. Read the systemd service unit documentation and your distribution’s manual for the options supported there.
Keep the key in a separately protected environment file if you use that method, and set restrictive ownership and permissions on it. The account running FFmpeg must have the access it needs, but other local accounts should not. Also consider whether the media directory is writable by the service; read-only access can reduce the consequences of a compromised process where the job does not need to modify its source.
Systemd’s journal captures standard output and error by default for a typical service configuration, which makes FFmpeg diagnostics available through journalctl. That is useful, but logs can contain sensitive details depending on the command and errors. Avoid printing secrets and check log retention against the amount of output a long-running process produces. Service access controls and file permissions should fit the host, not be copied as decorative settings from a generic example.
Start and enable the service
After saving or changing the unit, ask systemd to reload unit definitions, then enable the service for future boots and start it now. The usual sequence is:
sudo systemctl daemon-reload
sudo systemctl enable --now youtube-live.service
sudo systemctl status youtube-live.service
If you change the unit later, reload definitions again before restarting it. Enabling configures the service to start during the relevant boot target; it does not prove that the network is ready, credentials work, media is valid, or YouTube accepts the stream. After=network-online.target expresses an ordering relationship, but the target’s meaning and network manager behaviour depend on the distribution configuration.
Use the status output to see whether systemd considers the unit active and whether FFmpeg has recently exited or restarted. Read recent service logs with:
sudo journalctl -u youtube-live.service --since "15 minutes ago"
Choose a time range that fits the incident you are investigating. Look for input-open errors, encoder initialization failures, authentication or connection errors, and recurring restarts. If the process is active but the logs show repeated reconnects, you have a process that is alive, not necessarily a usable broadcast.
Test reboot behaviour before relying on the machine unattended. Confirm the media is available after boot, the secret file permissions are correct, and the service starts under the intended user. Then deliberately test a controlled process failure in a safe window and observe what systemd does. Do not run a failure test during an event that matters, and do not treat a successful restart on the same host as proof that the YouTube feed recovered.
Verify YouTube stream health
Check the receiving end independently. Open the stream in Live Control Room and confirm that a preview appears, the stream health indicator has no unresolved warning, and both audio and video are present. Then view the public watch page or an appropriate viewer-facing preview from a separate browser or device. A green active status in systemctl only tells you about the local service process; it does not certify ingest, media validity, or playback for viewers.
YouTube recommends testing before a live stream and monitoring during it. Use a private or otherwise suitable test stream if you need to check the complete path without disrupting a public broadcast. Confirm that the preview continues through a representative period, that sound is audible at a sensible level, and that the picture is not frozen or unexpectedly cropped. Check for warnings in Live Control Room rather than guessing from FFmpeg output alone.
Recovery has several stages: systemd notices FFmpeg exited, starts a new process, FFmpeg can read its input and reach YouTube, YouTube accepts the feed, and viewers can play it. The service can recover at the first two stages and still fail at a later one. For example, the key may have been reset, the upload may not have recovered, or the restarted input may be malformed. This is why restart supervision is useful but not a guarantee of uninterrupted broadcasting.
For connection symptoms that persist, compare the host’s logs with YouTube’s stream health messages and investigate the route and available upload capacity. The troubleshooting guide to a YouTube live stream marked unstable is relevant when the process remains up but the feed degrades. Record what a successful test looked like, including the expected preview and audio, so a later check has a baseline rather than a vague impression.
Tune encoder settings and monitor
Choose output settings as a set, not as isolated numbers. A higher resolution or frame rate changes the amount of work and bandwidth involved; a codec choice depends on YouTube’s current support and the encoders available in your FFmpeg build. Stream-copying avoids a new encode when the source is suitable, but it cannot alter the source’s codec, frame rate, or bitrate. Transcoding gives you control over those properties, at the cost of CPU or hardware-encoder requirements.
| Decision | What to check | Practical trade-off |
|---|---|---|
| Stream copy or transcode | Source codec, resolution, frame rate, and target needs | Copying uses less encoding work but preserves source properties; transcoding adds control and processing demand. |
| Codec | YouTube’s current guidance and FFmpeg encoder availability | Support and resource use differ; confirm the build and end-to-end test rather than assuming. |
| Resolution and frame rate | Source detail, desired presentation, encode capacity, and upload | More demanding output can require more processing and bandwidth. |
| Bitrate and keyframes | YouTube’s codec-specific table; CBR and keyframe interval guidance | Recommendations guide configuration but cannot establish your connection’s sustained capacity. |
| RTMPS destination | Exact URL from Live Control Room and protocol support | Secure ingest is recommended where supported; use the stream-specific address displayed by YouTube. |
Monitor more than the unit’s active state. Look at FFmpeg’s recent logs, the Control Room’s preview and health messages, and the actual viewer playback. Keep an eye on host CPU, memory, disk space, and network transfer so you can distinguish encoding pressure or local resource trouble from an ingest-side issue. The right signals depend on whether your workload transcodes, reads a file, or captures live input.
A continuous loop also needs an operational plan for source changes, maintenance, and key rotation. If the source is updated, validate it before switching the service to it. If the stream key changes, update the protected configuration and restart the service deliberately, then verify reception again. If you need different operational choices rather than maintaining a VPS and its unit yourself, the article on Vultr costs for a 24/7 YouTube stream helps frame the host-side trade-offs without supplying a universal instance size.
When manual upkeep is the recurring problem, StreamNeo removes the need to keep this FFmpeg process running on your own computer: it accepts an uploaded video and your YouTube stream key, then runs the broadcast with monitoring and automatic restarts. It is YouTube-only, so it is not a substitute for a capture workflow or a encoder.
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 systemd guarantee that my YouTube stream stays live?
No. It can start FFmpeg at boot and restart it after some failures, but the process may restart without restoring a valid feed. Check Live Control Room health and viewer playback as well as service status.
Should I use the Vultr instance size in its OBS guide?
Treat it as an example for that guide’s scenario, not a minimum or benchmark for your FFmpeg job. Your required capacity depends on the input, codec, resolution, frame rate, encoding method, and sustained upload capacity.
Can I put my stream key in the service file?
Avoid placing it in a broadly readable unit file or command. A protected environment file or another secret-storage method can limit access, and you should reset the key if it is exposed.
Why is the service active when YouTube shows no preview?
An active service means systemd sees a running main process; it does not confirm that YouTube accepted the feed. Check the FFmpeg logs, the URL and key, input and encoder errors, and Live Control Room’s stream health.