Skip to content
streamneo.
Troubleshooting11 min read

Nginx RTMP YouTube Stream Stops When SSH Disconnects: Fix on Ubuntu

Find which process publishes to YouTube, then keep an Ubuntu FFmpeg publisher running with systemd or diagnose an NGINX RTMP relay.

sn.
StreamNeoPublished 5 October 2026
Worth sharing?

If your YouTube stream stops when you disconnect SSH, first find which process owns the outgoing connection to YouTube. An FFmpeg command started at the shell needs a persistent process manager; an NGINX relay already running as a system service needs its own state and logs checked before you blame the SSH logout.

SSH closing is a clue about how a process was launched, not proof that NGINX failed or that YouTube stopped receiving video. Work through the publisher, its supervisor, and YouTube’s ingest health as separate checks.

Find the process publishing to YouTube

A common arrangement has FFmpeg reading a file or other source and publishing directly to YouTube. Another has a local encoder sending a stream to NGINX, with NGINX RTMP configured to push that stream onward to YouTube. Sometimes NGINX only receives or relays a local stream and does not own the final connection. The word “NGINX” in a setup description does not identify which process makes the outbound connection.

Before you log out, inspect the running processes and their command lines. On Ubuntu, ps -eo pid,ppid,user,args gives a useful overview; narrow it with a search for ffmpeg or nginx if the output is long. Do not publish or paste an unredacted command line: it may contain a stream key or another credential. Record the PID, account, parent PID, executable and relevant options privately, redacting secrets.

Then match the process to its job. An FFmpeg command that names an input and an RTMP or RTMPS destination is likely the publisher. NGINX may be the publisher if an RTMP application has a push directive for YouTube; it may instead be accepting an input from FFmpeg or OBS. Check the effective configuration rather than inferring from a process name. The NGINX RTMP module documentation describes module directives, but the installed package and configuration determine what your server actually supports.

The destination is another useful clue. FFmpeg documents RTMP URL components and shows FLV as an output format in its protocol reference. Confirm the output format, input, codecs and destination against your actual command and YouTube Live Control Room. A syntactically plausible command is not evidence that YouTube is ingesting a healthy signal.

Check whether SSH owns the process

If you ran FFmpeg directly at an interactive SSH prompt, the shell session is not a service supervisor. When you close the session, its process group may be signalled or otherwise lose the conditions that kept it running. The exact outcome depends on how the process was launched and the shell environment. Do not conclude that every process started over SSH must die: a system service launched from SSH is managed separately, and a detached session can outlive the terminal without becoming a properly supervised service.

Compare the PID you recorded before logout with the process list after reconnecting. If it is gone, check whether there is a systemd unit for it and whether that unit failed, was stopped, or never owned the process. If it remains, the stream may still have failed at the encoder, relay, network or YouTube ingest. The SSH event alone does not settle the cause.

For a persistent server-owned publisher, systemd is usually the clearer choice than leaving a command in an operator’s shell. A terminal multiplexer such as tmux can keep an operator-managed session available after disconnect, which may suit temporary work that you expect to reattach to and inspect. It does not, by itself, provide the same boot startup, service status and failure-restart lifecycle as a system unit. Choose according to whether the task belongs to an operator session or should run as a managed service.

If your broader goal is a long-running channel rather than maintaining this particular Ubuntu publishing path, the operational choices differ; the cloud service overview for a 24/7 lofi channel discusses that distinction. It does not replace diagnosing which process currently owns your YouTube connection.

If NGINX relays, inspect its own state

When the outbound publisher is NGINX RTMP, closing SSH should not ordinarily be treated as a reason for a system-managed NGINX process to exit. Check the service and its logs around the time you reproduced the issue:

systemctl status nginx
journalctl -u nginx --since "30 minutes ago"

Use a time window that covers your test. systemctl status reports the service manager’s view, while the journal can show exits, configuration errors and startup messages. If NGINX is active, that says the service process is running; it does not prove that an RTMP push is connected or that YouTube is receiving usable video.

Before changing configuration, validate it with sudo nginx -t. Correct any reported syntax or module errors before reloading. Then inspect the configured RTMP application, destination and logs for evidence of a push attempt or disconnection. The F5 NGINX RTMP module guide describes a procedure for NGINX Plus; do not assume that package instructions apply to an Ubuntu NGINX Open Source installation. The upstream RTMP module is third-party, and available module packaging varies.

If NGINX is only receiving a local feed, check both ends: the encoder that sends into NGINX and the relay that sends to YouTube. A failure in either connection can interrupt the broadcast while the NGINX service remains active. Record timestamps and correlate the relevant logs before changing a push directive or restarting the service, since a restart can erase useful evidence or briefly interrupt a healthy path.

Give an interactive FFmpeg publisher a systemd unit

If you have established that FFmpeg itself publishes directly to YouTube and it was started from your shell, move that command into a service. The unit should run FFmpeg directly, not through a helper that backgrounds itself. Use the executable path from your installed system, the real input and encoding arguments, and the endpoint that YouTube currently provides for your stream.

Here is a schematic unit, not a command ready to paste unchanged:

[Unit]
Description=YouTube RTMP publisher
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=streamer
WorkingDirectory=/srv/stream
ExecStart=/usr/bin/ffmpeg [your input and encoding options] -f flv [your YouTube ingest URL]
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Replace both bracketed placeholders and check the actual FFmpeg path with command -v ffmpeg. The options before -f flv must describe a readable input and appropriate encoding or stream-copy behaviour; the final URL must match your YouTube ingest endpoint and include the stream key in the format your encoder expects. The example’s RestartSec is an illustrative delay, not a universal setting. Do not copy a placeholder unit as if it were a valid publisher.

Keep secrets out of source control, public logs and a unit file readable by other users. You can put sensitive values in a restricted environment file owned and readable only by the service account, or use a systemd credential mechanism supported by the Ubuntu and systemd versions in use. Check how your FFmpeg build accepts the value, and avoid printing a full command containing the key when collecting diagnostics. If you suspect the key has been exposed, replace it through YouTube’s current stream settings rather than continuing to use a compromised secret.

Install the unit under /etc/systemd/system/youtube-publisher.service, then have systemd load it and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now youtube-publisher.service
systemctl status youtube-publisher.service
journalctl -u youtube-publisher.service

The enable --now operation both configures startup at boot and requests an immediate start. If it fails, the journal should help distinguish a missing file, permissions problem, invalid option, unreachable endpoint or encoder error. Fix the reported cause; repeatedly restarting a command with a bad input or wrong key does not make it valid. For stream-specific recovery and reconnect errors, see the FFmpeg YouTube reconnect troubleshooting guide.

Set the service account, paths and access

A service runs with the identity and environment defined for it, not the interactive shell’s convenient defaults. User=streamer means that the account must exist and be able to read the source file, any referenced configuration and any needed certificates. If the input is a device or mounted storage, check that account’s access as well. Avoid running the publisher as root merely to make a permissions error disappear.

Use absolute paths for the executable, input files and configuration. A relative path that worked in your home directory may fail when systemd starts the unit with a different working directory. WorkingDirectory=/srv/stream sets a predictable location for relative file access, but explicit absolute paths in ExecStart are easier to audit. Confirm directory traversal permissions as well as file read permissions; a readable file inside an inaccessible directory still cannot be opened by the service account.

Systemd does not interpret ExecStart as a normal interactive shell command. Shell expansions, pipes and redirections do not automatically work as they do at a prompt. Use FFmpeg’s own arguments directly, or deliberately configure a suitable wrapper only if you have a reason to maintain one. A wrapper adds another process and another place for quoting, environment and exit-status errors to hide.

After changing a unit file, run sudo systemctl daemon-reload before restarting the unit. Check the resulting systemctl status and journal output rather than assuming the edit took effect. This is also the point to verify that the service is running under the intended account and that the input path resolves as expected.

Choose restart behaviour and enable it deliberately

Restart=on-failure is a reasonable starting point for a long-running publisher: systemd requests another start when the process exits unsuccessfully, but an intentional systemctl stop is not treated as an unexpected failure to undo. Ubuntu’s systemd service manual recommends this mode for long-running services. That recommendation does not mean every stream should use identical restart settings: consider whether the process exits cleanly at the end of its input, whether the source is expected to finish, and how an operator should stop it.

A restart is recovery from process exit, not a repair for a bad source, codec, key or ingest URL. If FFmpeg repeatedly fails because it cannot open a file, a restart policy can make the same failure recur and may run into systemd’s start-rate limits. Diagnose the journal and correct the underlying problem rather than trying to create an endless restart loop. Select any delay and rate-limit behaviour with the actual failure mode in mind.

For a server-owned job that should return after a planned reboot, enable the unit at boot as shown above. For a one-off stream that should stop after its job is done, enabling it permanently may be the wrong choice. Decide explicitly whether it should start at boot, and whether it should restart after an encoder error, an intentional stop or a normal end of input. Those are different operational requirements, not one universal policy.

RTMPS is RTMP carried over TLS/SSL; changing the transport does not make a shell-launched process persistent. If you choose RTMPS, use the endpoint shown in YouTube Live Control Room and confirm your encoder supports it. YouTube explains the protocol in its RTMPS help page. Treat endpoint selection and process supervision as separate configuration tasks.

Verify service state and YouTube health separately

After starting the unit, look first at systemd, then the publisher’s own output, then YouTube’s status. systemctl is-active youtube-publisher.service or systemctl status youtube-publisher.service answers whether systemd considers the unit active. journalctl -u youtube-publisher.service -f lets you watch fresh messages while testing. Neither proves that the destination has a healthy stream: a process can remain alive while its input stalls, the network path breaks, or the ingest rejects the signal.

Open YouTube Live Control Room and check the current stream status and health indicators while the publisher is running. Confirm that the key belongs to the intended stream, the URL is the current one, the input produces data continuously, and the service account can read it. Keep the stream key private in screenshots and support requests. If you use a relay, review NGINX’s logs and the relevant RTMP connection evidence as well as the encoder’s output.

For a useful reproduction, note the time you disconnect SSH, reconnect, and compare the recorded PID, systemd state, recent journal entries and YouTube health. If the PID survives and YouTube shows healthy ingest, the SSH logout was not the cause of a stream failure. If the process is absent, identify whether it was shell-owned, failed under systemd or exited normally. If it is active but YouTube reports no data or a problem, follow the ingest evidence rather than changing SSH or NGINX blindly.

A concise record helps when the next failure happens overnight: which process published, which service owned it, what its last log message said, whether the source was still available, and what YouTube showed. For channel-specific planning beyond service recovery, the guide to a 24/7 devotional video channel on YouTube in India covers a different set of operating decisions. It should not be read as evidence that any particular process is healthy.

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

Will my FFmpeg stream stop every time I close SSH?

Not necessarily. If FFmpeg is a child of an interactive shell, logout can affect it; if a service manager owns it, SSH closing should not itself stop that service. Check the process and its unit rather than assuming either outcome.

Does systemctl active mean YouTube is receiving video?

No. It means systemd considers the service active, not that the source is producing data or YouTube’s ingest is healthy. Check publisher logs and the current status in YouTube Live Control Room.

Should I restart NGINX when the stream stops?

Not as a first step. Inspect systemctl status nginx, its journal and the validated configuration, then determine whether NGINX is actually the outbound publisher. A restart can interrupt service and may not fix a problem in FFmpeg, the source or YouTube ingest.

Should I use RTMP or RTMPS to keep the stream alive?

Transport choice does not decide whether a process survives SSH logout. Use the endpoint YouTube currently gives you and an encoder that supports it; use a persistent service to manage a publisher that should run beyond your login session.

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 ↗