You do not need a graphical desktop to keep an FFmpeg YouTube stream running on a VPS. FFmpeg can run from an SSH shell, while systemd keeps the process independent of your login session and can start it again after a process failure.
That supervision is not the same as a healthy broadcast. A restart policy cannot repair an unreadable source, a wrong stream key, an unsuitable encoding command, a broken network route or a YouTube ingest problem. You need to check both the Linux process and the live status shown by YouTube.
Why a desktop is not required
A desktop is useful for applications that expect windows, buttons or a logged-in graphical session. FFmpeg does not need those things. It reads an input, encodes or copies the media, and sends the resulting stream to an output URL. Those actions can all take place from a command line on a remote Linux VPS.
You normally connect with SSH, test the command while you are watching its output, and then move it into a service managed by the operating system. After that, closing your laptop or ending the SSH session does not have to stop the stream.
This is different from simply starting FFmpeg and hoping it remains available. A shell process may be tied to the session, may receive an unexpected input signal, or may stop without anyone noticing. A desktop does not solve those problems either. It mainly gives you a visible place to launch and observe the application.
For a prerecorded channel, a headless VPS can be suitable when you are comfortable checking logs and replacing a failed command. If you do not want to maintain a Linux process, StreamNeo removes the need to leave your own computer or VPS running by taking an uploaded video and publishing it to YouTube from the cloud. It remains a YouTube-only approach, so it does not replace a general-purpose streaming server.
Your first decision is therefore operational rather than graphical: do you want to administer FFmpeg yourself, or do you want the video delivery process managed outside your own machine? If you choose the VPS route, keep the setup small, explicit and testable.
Prepare the VPS and YouTube ingest destination
Start with a supported Linux distribution and a user account intended for the stream. Install FFmpeg from the distribution’s supported package source or from a trusted FFmpeg build. Then check the installed binary rather than assuming that an online command matches it:
command -v ffmpeg
ffmpeg -version
ffmpeg -encoders
FFmpeg’s online documentation is regenerated and can describe a newer revision than the package installed on your VPS. Use the local help output when an option behaves differently from the documentation you found. The project’s documentation page is useful for finding the relevant manuals, but the target host remains the authority for what is installed.
Before creating a service, prepare the YouTube live event and obtain its current ingestion details. Use the RTMPS destination supplied by YouTube rather than copying an old URL from a forum post. YouTube describes RTMPS as the secure ingestion route and identifies the endpoint hostname and port as important parts of the connection. The YouTube RTMPS ingestion guide is the place to check the current endpoint format and connection requirements.
RTMPS is RTMP carried over a secure SSL connection. In practical terms, that means the protocol, hostname and port must agree. A command that points at a cleartext RTMP endpoint, uses the wrong hostname, or omits the expected port can fail before FFmpeg sends useful media. YouTube also uses the hostname during TLS and SNI authentication, so do not replace it casually with an IP address.
Treat the stream key as a secret. Do not paste it into a public tutorial, screenshot, ticket, shared shell history or broadly readable service file. If the key has appeared in a public place, rotate it in YouTube Studio before continuing. A restricted environment file, a protected credentials file, or another secret-handling method appropriate to your distribution is preferable to placing it in a unit file readable by every local user.
Test the input before you test the service. Confirm that the VPS user can read the file, reach the source URL, and access any directories used by the command. Check the available disk space if the input is a local file, and confirm the source does not depend on a mounted path that will disappear after boot.
For a file that should be sent in real time, FFmpeg’s protocols documentation illustrates the general pattern of using -re before the input and sending an FLV output to an RTMP URL. Adapt that pattern to the current YouTube RTMPS destination and to your actual codecs, resolution and audio arrangement. It is an illustration, not a complete command for every file:
ffmpeg -re -i /srv/stream/input.mp4 -c:v copy -c:a copy -f flv 'rtmps://current-youtube-endpoint/app/STREAM_KEY'
Do not assume that stream copying will work for every source. Copying avoids an additional encode, but the source codecs, timestamps and container behaviour still need to be acceptable to the output. Transcoding gives you more control over the output format but uses more CPU and may require a VPS with greater headroom. The correct choice depends on the media you have, not on a universal VPS specification.
A useful starting point is to create a private or unlisted test event where appropriate, run the command interactively, and wait long enough to observe both FFmpeg’s output and YouTube’s live status. The 24/7 stream pre-flight checks can help you examine the source, account and handover details before you leave the process unattended.
Run FFmpeg without interactive stdin
FFmpeg normally checks console input. That is helpful when you are running it interactively and want to respond to a command, but it can be a problem when the process is placed in the background. A background process may suspend when it tries to read from the terminal.
Add -nostdin to a service command when FFmpeg should not read commands from the terminal:
ffmpeg -nostdin -re -i /srv/stream/input.mp4 [output options] -f flv 'rtmps://current-youtube-endpoint/app/STREAM_KEY'
The FFmpeg FAQ gives the same reason for using this option: it prevents input checks so FFmpeg can run as a background task. Redirecting standard input from /dev/null is another approach:
ffmpeg -re -i /srv/stream/input.mp4 [output options] -f flv 'rtmps://current-youtube-endpoint/app/STREAM_KEY' < /dev/null
For a systemd service, -nostdin is usually clearer because the intent is visible in the FFmpeg command itself. You should still make sure that the service has no dependency on terminal input, prompts or interactive confirmation.
This setting does not make the command reliable by itself. It only addresses one class of background-process behaviour. If the input ends, the destination rejects the connection, or FFmpeg exits because of an invalid option, -nostdin does not identify or repair that cause.
When you test interactively, leave -nostdin in the command you plan to use in production. That reduces the chance that the command works in an SSH shell but behaves differently after you put it under supervision.
Choose a background process approach
There are two broad ways to keep a command running after you disconnect: a shell-oriented background method, or an operating-system service. They are not equivalent.
| Approach | What it does well | What it does not provide by itself |
|---|---|---|
nohup or a shell background job |
Quick testing and simple one-off runs | Clear boot behaviour, structured restart policy and a consistent log view |
tmux or screen |
Lets you reconnect to a visible terminal session | Automatic recovery from every process exit or a defined service dependency |
| systemd service | Starts independently of SSH, centralises logs and can restart after failure | Diagnosis of bad inputs, successful YouTube ingestion or a correction to a broken command |
A command such as nohup ffmpeg ... > stream.log 2>&1 & can be useful for a short test. It is less suitable as the long-term control plane for a 24/7 channel because you must remember the exact command, log location and process ID. A terminal multiplexer is more convenient when you are actively observing a session, but it is still a session you must manage.
Systemd is normally the better fit for a long-running Linux service. It can wait for network availability, run under a dedicated account, start at boot, collect output in the journal and apply a deliberate restart policy. It also gives you standard commands for starting, stopping and inspecting the service.
For a local file playlist or a repeated prerecorded video, decide how the input should behave before you write the service. A deliberately configured loop may keep the input producing media, but it does not fix a disconnected output. After a restart, the input can also begin again from the start, depending on the command. If you need a playlist rather than one file, the guide to setting up an automatic FFmpeg video playlist covers the input design separately from process supervision.
Put the command under systemd supervision
Create a service only after the interactive command can reach YouTube and produce a valid test stream. A unit can look like this:
[Unit]
Description=FFmpeg YouTube live stream
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=stream
Group=stream
WorkingDirectory=/srv/stream
ExecStart=/usr/bin/ffmpeg -nostdin [input and encoding options] -f flv [current YouTube RTMPS ingestion URL]
Restart=on-failure
RestartSec=10s
[Install]
WantedBy=multi-user.target
This is a template, not a tested unit. Replace the bracketed parts with a correctly escaped command and use the actual path returned by command -v ffmpeg. Create the stream account and directories according to your distribution’s policies, then give that account only the read and execute access it needs.
Do not put a real stream key into a unit file that is readable by ordinary users. If you use an environment or configuration file, restrict its permissions and check how your distribution exposes service properties to local users. Be aware that a secret passed directly in an ExecStart command can become visible through process inspection while FFmpeg is running. Choose a secret-handling method with that exposure in mind.
After saving the unit, reload systemd and start it deliberately:
sudo systemctl daemon-reload
sudo systemctl enable --now ffmpeg-youtube.service
sudo systemctl status ffmpeg-youtube.service
Use the actual filename you chose. If the service does not start, do not keep changing restart settings first. Read the error, run the command manually as the service account, and verify the path, permissions and syntax.
Restart=on-failure tells systemd to try again when the service exits unsuccessfully or is terminated by a failure condition. RestartSec=10s adds a delay rather than making the process reconnect in a tight loop. The systemd service reference recommends on-failure for long-running services, while also documenting start-rate limiting. Read the systemd service reference for the behaviour of restart settings on the system you administer.
A restart policy is not a delivery guarantee. If the input path is wrong, the stream key is invalid, the endpoint is incorrect, the codec arrangement is rejected, or the VPS cannot reach YouTube, systemd may simply launch the same failing command again. Repeated starts can eventually hit systemd’s rate limit, leaving the service stopped until you correct the underlying problem.
Separate restart behaviour from stream health
There are two different questions to answer:
- Is the FFmpeg process currently running?
- Is YouTube receiving a healthy stream that viewers can watch?
systemctl status answers part of the first question. It can show that the service is active, but an active process may still be producing no useful output, waiting on a source, reconnecting, or sending media that YouTube cannot accept as expected. The second question must be checked in YouTube Studio’s live control room and, where suitable, through the actual viewer output.
This distinction matters after a silent failure. A process can remain alive while the network connection is unusable. Conversely, FFmpeg can exit promptly and systemd can restart it, while YouTube shows repeated interruptions. In both cases, the status line alone is insufficient.
Use the current YouTube control-room information to check whether the event is receiving data and whether the preview or viewer output is healthy. Compare that with the time-stamped FFmpeg messages in the journal. If YouTube shows no incoming stream while systemd reports an active service, investigate the output URL, connection state, input timing and FFmpeg errors rather than increasing the restart count.
For channels built from repeated videos, also consider what viewers see at a restart. The command may begin the file again, may reconnect at a different point, or may stop because the input has reached its end. A loop option can address a finite input, but it does not establish that the resulting broadcast is suitable for your channel’s content or YouTube’s current policies. If your channel uses other people’s material, review the practical guidance on reused content and 24/7 loops before leaving the channel unattended.
Read logs and find the real failure
The first log command for a systemd unit is:
sudo journalctl -u ffmpeg-youtube.service -n 100 --no-pager
To follow new messages while the service runs, use:
sudo journalctl -u ffmpeg-youtube.service -f
Look for the first meaningful error, not only the final message after several restart attempts. A connection failure may be followed by shutdown messages that describe the consequence rather than the cause. Record the time of the failure and compare it with YouTube’s live status and any VPS provider network or resource information available to you.
If the service exits immediately, check these areas in order:
- The FFmpeg path in
ExecStartis correct. - The service account can read the input and enter the working directory.
- The input URL or file exists and remains available after boot.
- The RTMPS hostname, protocol and port are exactly the current YouTube values.
- The stream key is current and has not been exposed or revoked.
- The command’s codecs, container and timing are appropriate for the input and output.
- The VPS has enough CPU, memory, disk and network capacity for this workload.
Do not choose a VPS size from a generic “minimum requirement” table. Resource demand changes depending on whether FFmpeg copies or re-encodes the source, the resolution and bitrate, the number of simultaneous outputs, the type of input and the network conditions at the provider. Watch the target workload rather than treating one machine size as universal.
If FFmpeg reports that it cannot open the input, process supervision is not the remedy. Fix the path, permissions or source first. If it reports a failed connection, verify the endpoint and credentials before changing the restart policy. If the process is consuming excessive CPU, inspect the encoding settings and workload rather than asking systemd to restart it more often.
Keep logs readable and private. They may include source URLs, file paths or command details. They should not contain a stream key. If a tool or shell expansion causes the key to appear in output, rotate it and change the way the service receives the secret.
Test reconnects and failure behaviour before leaving it alone
A service is not ready for overnight operation merely because it started once. Test the failure modes you can safely reproduce while watching both systemd and YouTube.
First, stop FFmpeg through systemd and confirm that the service reaches the expected inactive state. Then start it again and check whether the command reconnects cleanly. Next, interrupt the network path in a controlled maintenance window, if your hosting environment allows that, and observe whether FFmpeg exits, waits or reports a connection error. Do not treat a successful restart as proof that every network interruption will behave the same way.
You can also test the source boundary. Temporarily use a short, valid input and see what happens when it reaches the end. This reveals whether the service stops, loops, or remains in a state that needs attention. A loop designed for a prerecorded channel may restart the media after a process restart, so note where playback begins after recovery.
For each test, record four observations:
- What happened to the FFmpeg process.
- What systemd reported and whether it attempted a restart.
- What appeared in
journalctl. - What YouTube showed in the control room and viewer output.
This gives you a useful runbook instead of an assumption. If the service repeatedly starts and stops, inspect the journal before changing RestartSec. If systemd applies its start-rate limit, that is a signal to stop retrying and fix the cause. If FFmpeg stays active but YouTube does not show a healthy broadcast, process supervision is working only in the narrow sense that the process remains present.
Keep a tested command copy somewhere private, but do not store the stream key in a shared document. Note the unit name, input location, log command, YouTube event and the steps for rotating the key. The next failure should be diagnosable by you or another administrator without searching through an old terminal session.
For a broader comparison of ways to keep a channel operating without your personal computer, see whether a PC must stay on for a 24/7 YouTube live stream. The decision depends on how much Linux maintenance and failure diagnosis you want to own.
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 I run FFmpeg after closing my SSH window?
Yes. A systemd service runs independently of your interactive SSH session. For a quick test, a multiplexer or nohup may keep a command available, but systemd is more suitable when you need boot handling, logs and a defined restart policy.
Why does FFmpeg stop when I put it in the background?
FFmpeg normally checks console input, and a background process can be suspended when it tries to read from the terminal. Use -nostdin, or redirect standard input from /dev/null, and test the same command under the conditions in which it will run.
Will systemd fix a disconnected YouTube stream?
No. It can restart FFmpeg after a process failure, but it cannot correct a wrong stream key, invalid input, unsuitable encoding options, a bad RTMPS endpoint or an unresolved YouTube ingest problem. Check the journal and YouTube’s live control room before adjusting the restart policy.
Should I copy the source or re-encode it?
Copying uses less processing than re-encoding, but the source still needs to be suitable for the output. Re-encoding gives you more control over the delivered media and uses additional VPS resources. Test the actual file, codecs and workload rather than relying on a universal setting.