Build a Bulletproof 24/7 YouTube Live Stream on Linux VPS: The Complete 2026 Guide

Published on

Stop relying on expensive SaaS platforms. Learn to build a production-grade 24/7 stream on a $10/month VPS using FFmpeg, systemd, and bash scripting. Auto-reconnection logic, bitrate optimization, and monitoring included. No PC required.

Written by Jochem Wassenaar – CEO of Space-Node – 15+ years combined experience in game server hosting, VPS infrastructure, and 24/7 streaming solutions. Learn more

Quick Setup (TL;DR):

# Install FFmpeg on Ubuntu VPS
sudo apt update && sudo apt install ffmpeg -y

# Create stream script
nano stream.sh
# (Paste script from "The Resilient Loop" section below)

# Make executable and run
chmod +x stream.sh
./stream.sh

This guide replaces: Upstream.so ($50/mo), Gyre ($75/mo), Live247 ($30/mo) with a $10/month VPS that runs 24/7 forever.


Why Self-Host Your 24/7 Stream?

The SaaS Cost Trap

Cloud Streaming Services (Annual Cost):
┌────────────────────────────────────┐
│ Upstream.so:      $600/year       │
│ Gyre.pro:         $900/year       │
│ Live247:          $360/year       │
└────────────────────────────────────┘

DIY Linux VPS (Annual Cost):
┌────────────────────────────────────┐
│ VPS (2 vCPU, 4GB): $120/year      │
│ Domain (optional):  $12/year      │
│ Total:             $132/year      │
│                                    │
│ Savings:           $228-768/year  │
└────────────────────────────────────┘

More importantly: You own the infrastructure. No vendor lock-in, no feature restrictions, no "upgrade to pro" walls.

What You Can Stream 24/7

| Content Type | Use Case | Bandwidth | |--------------|----------|-----------| | LoFi Radio | Music playlist + static background | 2-4 Mbps | | Podcast Archive | Audio + album art | 1-3 Mbps | | Gaming Montage Loop | Highlight reel on repeat | 4-6 Mbps | | Security Camera | Wildlife cam, city view | 2-5 Mbps | | Server Status | Minecraft server stats (see our Minecraft guide) | 1-3 Mbps | | Educational Content | Tutorials on loop | 4-6 Mbps |


Prerequisites: VPS Selection

Minimum Requirements

| Spec | Requirement | Why | |------|-------------|-----| | CPU | 2 vCPUs (2.5+ GHz) | FFmpeg encoding uses ~60% of one core | | RAM | 2GB minimum, 4GB recommended | FFmpeg buffer + OS overhead | | Storage | 20GB (50GB+ for large libraries) | OS + video files | | Bandwidth | Unmetered or 2TB/month | 1080p @ 4Mbps = ~1.3TB/month | | Network | 100 Mbps upload minimum | Smooth 1080p delivery |

Recommended VPS Providers

| Provider | Plan | CPU | RAM | Price | Best For | |----------|------|-----|-----|-------|----------| | Space-Node | VPS-2 | 2 vCPU Ryzen | 4GB | $10/mo | Best price/performance | | Hetzner Cloud | CX21 | 2 vCPU AMD | 4GB | €4.51/mo | Europe, cheap bandwidth | | DigitalOcean | Basic Droplet | 2 vCPU | 2GB | $12/mo | Beginner-friendly UI | | Vultr | High Frequency | 2 vCPU | 4GB | $12/mo | Good uptime SLA |

Warning: Avoid shared hosting (cPanel/Plesk). You need root SSH access to install FFmpeg.


Initial VPS Setup

Step 1: OS Installation

Recommended OS: Ubuntu 24.04 LTS (Long Term Support until 2029)

Why Ubuntu?

  • ✅ FFmpeg in default repos (no manual compilation)
  • ✅ 5 years of security updates
  • ✅ Largest community support

Alternative: Debian 12 (more stable, slightly older packages)

Step 2: SSH Connection

# From your local terminal (Mac/Linux)
ssh root@<your-vps-ip>

# Windows: Use PuTTY or Windows Terminal

First-time setup:

# Update package lists
sudo apt update

# Upgrade existing packages
sudo apt upgrade -y

# Install essential tools
sudo apt install ffmpeg nano htop curl wget -y

Verify FFmpeg installation:

ffmpeg -version

# Should show:
# ffmpeg version 6.1.x-static

The Problem with Basic FFmpeg Commands

Why Most Tutorials Fail

Standard FFmpeg command you'll find on Reddit:

ffmpeg -re -stream_loop -1 -i video.mp4 \
  -c copy -f flv rtmp://a.rtmp.youtube.com/live2/YOUR_KEY

Problems:

  1. No auto-reconnect: If your network hiccups for 1 second, stream dies forever
  2. Wrong codec settings: -c copy fails if your video isn't already YouTube-compatible
  3. No error handling: Crashes on corrupt frames in source video
  4. No logging: When it breaks at 3 AM, you have no idea why

What happens in production:

Hour 1: Stream starts fine ✅
Hour 3: Network blip, FFmpeg exits ❌
Hour 4-24: Stream offline, you're asleep 😴
Morning: Viewers left angry comments 😡

The Resilient Loop: Production-Grade Script

Core Concept: The While True Loop

Bash while loop = automatic restart if FFmpeg crashes:

#!/bin/bash

while true; do
  ffmpeg [your command here]
  sleep 2  # Wait 2 seconds before retry
done

How it works:

  1. FFmpeg runs
  2. If FFmpeg exits (crash/disconnect), the loop continues
  3. sleep 2 prevents rapid-fire retries (which can get you IP-banned)
  4. Loop restarts FFmpeg
  5. Repeat forever

Complete Production Script

Create the script:

nano ~/stream.sh

Paste this (customize the variables):

#!/bin/bash

#############################################
# 24/7 YouTube Stream - Production Script
# Created: February 2026
# License: MIT
#############################################

# CONFIGURATION
VIDEO_SOURCE="/root/content/stream.mp4"
YOUTUBE_RTMP="rtmp://a.rtmp.youtube.com/live2"
STREAM_KEY="YOUR_STREAM_KEY_HERE"
BITRATE="4500k"
LOG_FILE="/var/log/stream.log"

# ENCODING SETTINGS
PRESET="veryfast"      # veryfast, fast, medium, slow (slower = better quality, more CPU)
GOP_SIZE="60"          # Keyframe every 60 frames (2 seconds at 30fps)
AUDIO_BITRATE="128k"
AUDIO_RATE="44100"

# RECONNECTION SETTINGS
RETRY_DELAY="2"        # Seconds to wait before retry
MAX_RETRIES="0"        # 0 = infinite retries

#############################################
# DO NOT EDIT BELOW THIS LINE
#############################################

echo "Starting 24/7 stream at $(date)" | tee -a "$LOG_FILE"

retry_count=0

while [ "$MAX_RETRIES" -eq 0 ] || [ "$retry_count" -lt "$MAX_RETRIES" ]; do
  echo "[$(date)] Stream attempt #$((retry_count + 1))" | tee -a "$LOG_FILE"
  
  ffmpeg -re -stream_loop -1 -i "$VIDEO_SOURCE" \
    -c:v libx264 \
    -preset "$PRESET" \
    -b:v "$BITRATE" \
    -maxrate "$BITRATE" \
    -bufsize $((${BITRATE%k} * 2))k \
    -pix_fmt yuv420p \
    -g "$GOP_SIZE" \
    -c:a aac \
    -b:a "$AUDIO_BITRATE" \
    -ar "$AUDIO_RATE" \
    -f flv \
    "$YOUTUBE_RTMP/$STREAM_KEY" 2>&1 | tee -a "$LOG_FILE"
  
  EXIT_CODE=$?
  echo "[$(date)] FFmpeg exited with code $EXIT_CODE" | tee -a "$LOG_FILE"
  
  retry_count=$((retry_count + 1))
  
  if [ "$EXIT_CODE" -eq 0 ]; then
    echo "[$(date)] Clean exit detected. Stopping." | tee -a "$LOG_FILE"
    break
  fi
  
  echo "[$(date)] Retrying in $RETRY_DELAY seconds..." | tee -a "$LOG_FILE"
  sleep "$RETRY_DELAY"
done

echo "[$(date)] Stream script terminated" | tee -a "$LOG_FILE"

Make it executable:

chmod +x ~/stream.sh

Script Breakdown: Critical Flags Explained

1. -re (Read at Native Frame Rate)

-re

What it does: Forces FFmpeg to send data at the video's actual playback speed (30fps).

Without -re:

FFmpeg reads the file as fast as possible:
500 fps → Floods YouTube's buffer → Stream crashes

With -re:

FFmpeg reads at 30 fps → Smooth delivery → Stable stream

Critical for: All 24/7 streams. Never omit this.


2. -bufsize (The Stability Secret)

-bufsize $((${BITRATE%k} * 2))k

What it does: Sets the encoder's buffer to 2x the target bitrate.

Why 2x?

  • Video bitrate fluctuates based on scene complexity
  • Fast motion (action scenes) need temporary bitrate spikes
  • Buffer absorbs spikes without violating average bitrate limit

Example:

Bitrate: 4500k
Buffer:  9000k (2x)

Scene complexity:
  - Calm scene:   2,000 kbps (under budget)
  - Action scene: 6,500 kbps (spike, buffer absorbs)
  - Average:      4,500 kbps (YouTube happy)

Without proper buffer: Blocky video during fast motion.


3. -ar 44100 (Prevent Audio Desync)

-ar 44100

What it does: Explicitly sets audio sample rate to 44.1 kHz.

Why needed?

  • Some source videos have weird sample rates (48 kHz, 32 kHz)
  • YouTube expects 44.1 kHz or 48 kHz
  • Mismatched rates cause audio drift after 12+ hours

Symptom without -ar:

Hour 1:  Audio synced ✅
Hour 12: Audio 3 seconds behind video ❌
Hour 24: Audio 10 seconds behind ❌❌❌

4. -g 60 (GOP Size = Keyframe Interval)

-g 60

What it does: Inserts a keyframe every 60 frames.

Math:

  • 60 frames ÷ 30 fps = Keyframe every 2 seconds

Why 2 seconds?

  • YouTube requires keyframes ≤ 4 seconds
  • More keyframes = easier for viewers to join mid-stream
  • Fewer keyframes = slightly better compression

Optimal: 2 seconds (60 frames at 30fps, 120 at 60fps)


Running the Stream

Test Run (Foreground)

Start the stream:

./stream.sh

You'll see:

Starting 24/7 stream at Thu Feb  6 14:32:01 UTC 2026
[Thu Feb  6 14:32:01 UTC 2026] Stream attempt #1
ffmpeg version 6.1 Copyright...
Input #0, mov,mp4...
  Duration: 00:15:32.48, start: 0.000000, bitrate: 5432 kb/s
  Stream #0:0: Video: h264, yuv420p, 1920x1080, 30 fps
  Stream #0:1: Audio: aac, 44100 Hz, stereo
Output #0, flv, to 'rtmp://a.rtmp.youtube.com/live2/xxxx':
  Stream #0:0: Video: h264, 1920x1080, 30 fps, 4500 kb/s
  Stream #0:1: Audio: aac, 44100 Hz, 128 kb/s
frame=  456 fps= 30 q=28.0 size=    2048kB time=00:00:15.20 bitrate=1103.2kbits/s speed=1.00x

Key indicators:

  • fps= 30 (matches source)
  • speed=1.00x (real-time encoding, not faster/slower)
  • bitrate= fluctuates around 4500k

Stop test: Press Ctrl+C


Production Run (Background with Screen)

Problem: If you close your SSH session, the stream stops.

Solution: Use screen (terminal multiplexer).

Install screen:

sudo apt install screen -y

Start a detached screen session:

screen -S stream -dm bash -c './stream.sh'

Explanation:

  • -S stream: Names the session "stream"
  • -dm: Detaches immediately
  • bash -c './stream.sh': Runs your script

Check if it's running:

screen -ls

# Output:
# There is a screen on:
#   12345.stream (Detached)

Attach to see live output:

screen -r stream

Detach again (leave it running): Press Ctrl+A, then D

Kill the stream:

screen -X -S stream quit

Alternative: Systemd Service (Auto-Start on Boot)

For production servers that need to survive reboots:

Create service file:

sudo nano /etc/systemd/system/youtube-stream.service

Paste:

[Unit]
Description=24/7 YouTube Live Stream
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/root
ExecStart=/root/stream.sh
Restart=always
RestartSec=10
StandardOutput=append:/var/log/stream.log
StandardError=append:/var/log/stream.log

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable youtube-stream.service
sudo systemctl start youtube-stream.service

Check status:

sudo systemctl status youtube-stream.service

# Should show: "active (running)"

View logs:

sudo journalctl -u youtube-stream.service -f

Now your stream auto-starts on server reboot!


Bitrate Optimization for Different Content

The Bitrate-Quality Tradeoff

| Resolution | Slow Motion (Sports) | Medium Motion (Talking) | High Motion (Gaming) | |------------|----------------------|-------------------------|----------------------| | 720p30 | 2,500 kbps | 1,500 kbps | 3,000 kbps | | 1080p30 | 4,500 kbps | 3,000 kbps | 6,000 kbps | | 1080p60 | 6,000 kbps | 4,500 kbps | 9,000 kbps |

VPS bandwidth calculator:

Bitrate × Hours × Days = Monthly bandwidth

Example (1080p30 @ 4.5 Mbps):
4.5 Mbps × 24 hours × 30 days = 3,240,000 Mb
= 3,240 Gb = 405 GB/month

Most VPS providers: Offer 1-5 TB/month (plenty for one stream).


Monitoring and Troubleshooting

Real-Time Monitoring

Install htop:

sudo apt install htop -y
htop

What to look for:

CPU:  [||||||||          ] 40%  ← FFmpeg using ~40% (normal)
MEM:  [||||||            ] 1.2GB/4GB  ← Healthy

If CPU hits 100%: Your preset is too slow. Change to ultrafast in script.

If RAM fills up: You have a memory leak. Restart the stream.


Common Errors and Fixes

Error 1: "Connection refused"

[tcp @ 0x...] Connection to tcp://a.rtmp.youtube.com:1935 failed

Cause: YouTube stream key is wrong or stream isn't enabled in YouTube Studio.

Fix:

  1. Go to YouTube Studio → Go Live → Stream Key
  2. Copy the key exactly
  3. Update STREAM_KEY in script

Error 2: "Frame rate too high"

[flv @ 0x...] Frame rate 120 too high, clamping to 60

Cause: Your source video is 120fps, but YouTube only accepts up to 60fps.

Fix: Add -r 30 to force 30fps output:

ffmpeg -re -stream_loop -1 -i "$VIDEO_SOURCE" \
  -r 30 \  # Add this line
  -c:v libx264 ...

Error 3: "Codec not supported"

Codec for stream 0 does not use global headers but container format requires global headers

Cause: Trying to use -c copy (copy codec) with incompatible source.

Fix: Always re-encode. Never use -c copy for 24/7 streams (it's unreliable).


Error 4: "Past duration too large"

[flv @ 0x...] Past duration 0.999944 too large

Cause: Audio/video timestamps drifting (common with -stream_loop).

Fix: Add -fflags +genpts to regenerate timestamps:

ffmpeg -re -fflags +genpts -stream_loop -1 -i "$VIDEO_SOURCE" ...

Advanced: Multiple Video Playlist

Problem: One Video Gets Boring

Solution: Rotate through a folder of videos.

Create playlist file:

nano ~/playlist.txt

Add videos (absolute paths):

file '/root/content/video1.mp4'
file '/root/content/video2.mp4'
file '/root/content/video3.mp4'

Update script to use playlist:

VIDEO_SOURCE="/root/playlist.txt"

# Change FFmpeg input:
ffmpeg -re -f concat -safe 0 -stream_loop -1 -i "$VIDEO_SOURCE" \
  ...rest of command...

Now FFmpeg loops through all 3 videos infinitely!


Bandwidth & Cost Optimization

Reducing Data Usage

1. Lower bitrate (quality tradeoff):

BITRATE="3000k"  # Instead of 4500k
# Saves: ~30% bandwidth, slight quality loss

2. Drop to 720p:

# Add scaling filter
-vf scale=1280:720 \

3. Use VP9 codec (better compression):

-c:v libvpx-vp9 \
-b:v 2500k  # VP9 achieves 1080p quality at lower bitrate

Warning: VP9 encoding uses 3x more CPU than H.264. Only use on powerful VPS (4+ vCPUs).


Monthly Cost Breakdown

VPS Costs ($10/month example):

Server rental:         $10.00
Domain (optional):      $1.00
Bandwidth (included):   $0.00
────────────────────────────
Total:                 $11.00/month

vs Cloud Streaming SaaS:

Upstream.so:           $50.00
Gyre.pro:              $75.00
Live247:               $30.00

Annual savings: $228-768


Security Best Practices

1. Never Hardcode Stream Keys in Scripts

Bad:

STREAM_KEY="abc-1234-defg-5678"  # Visible in script

Good (use environment variables):

# Store key in separate file
echo "export STREAM_KEY='abc-1234-defg-5678'" > ~/.stream_env
chmod 600 ~/.stream_env  # Only root can read

# Load in script
source ~/.stream_env

2. Enable UFW Firewall

# Allow SSH only
sudo ufw allow 22/tcp
sudo ufw enable

# Stream doesn't need inbound ports (only outbound to YouTube)

3. Automatic Security Updates

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades
# Select "Yes"

Performance Tuning

CPU Preset Impact

| Preset | CPU Usage | Quality | Encoding Speed | |--------|-----------|---------|----------------| | ultrafast | 20% | Low | 5x real-time | | veryfast | 40% | Good | 2x real-time | | fast | 60% | Better | 1.5x real-time | | medium | 80% | High | 1x real-time | | slow | 100% | Very High | 0.5x real-time |

For 24/7 streams: Use veryfast or fast (balance of quality + CPU headroom).

For low-end VPS: Use ultrafast (prevent CPU overload).


Integrating with Minecraft Server Hosting

Use case: Stream your Minecraft server status 24/7 to attract players.

Setup:

  1. Create a video showing your server IP, rules, and live player count
  2. Use FFmpeg to overlay real-time stats (via ImageMagick + cron script)
  3. Stream to YouTube 24/7

Cross-link: See our Folia vs Paper guide for server optimization, then stream your optimized server!

Benefits:

  • Players can find your server by searching YouTube
  • 24/7 presence = appears more "active"
  • Costs same $10 VPS (run Minecraft + stream on one server if under 20 players)

Troubleshooting: The 30-Second Checklist

Stream won't start?

# 1. Check FFmpeg installed
ffmpeg -version

# 2. Check video file exists
ls -lh /root/content/stream.mp4

# 3. Test YouTube stream key
curl -I rtmp://a.rtmp.youtube.com/live2
# Should return connection attempt (even if it fails, proves network works)

# 4. Check logs
tail -f /var/log/stream.log

# 5. Test with simple command first
ffmpeg -re -i /root/content/stream.mp4 -c copy -f flv rtmp://a.rtmp.youtube.com/live2/YOUR_KEY

Migration from Cloud Services

Leaving Upstream/Gyre?

Export your content:

  1. Download all videos from cloud service
  2. Upload to VPS using scp or rsync:
# From local machine
scp -r ./videos/ root@your-vps:/root/content/

Recreate playlists:

  • Upstream's playlists → Create playlist.txt (see Multiple Video section)
  • Scheduled streams → Use cron to start/stop script at specific times

Example cron (stream only 9 AM - 11 PM daily):

crontab -e

# Start at 9 AM
0 9 * * * screen -S stream -dm bash -c '/root/stream.sh'

# Stop at 11 PM
0 23 * * * screen -X -S stream quit

Scaling to Multiple Streams

Can one VPS run 5 streams?

Math:

  • 1 stream: ~40% CPU, 1.5GB RAM
  • 5 streams: ~200% CPU (need 4 vCPUs), 7.5GB RAM

Recommended VPS for 5 streams:

  • CPU: 6 vCPUs
  • RAM: 12GB
  • Cost: ~$40/month
  • Still cheaper than 5× cloud services ($150-250/month)

Setup:

# Create separate script for each stream
cp stream.sh stream1.sh
cp stream.sh stream2.sh
...

# Edit each with different VIDEO_SOURCE and STREAM_KEY

# Run in separate screens
screen -S stream1 -dm bash -c './stream1.sh'
screen -S stream2 -dm bash -c './stream2.sh'
...

Conclusion: Freedom from SaaS

What you've built: ✅ A production-grade 24/7 streaming server
✅ Auto-reconnection on network failures
✅ Systemd service for auto-start on boot
✅ Full control over quality and bitrate
✅ $40-60/month savings vs cloud services

Next steps:

  1. Experiment with different bitrates for your content type
  2. Add multiple videos to playlist for variety
  3. Integrate with your Minecraft server (overlay server IP)
  4. Monitor for 72 hours to ensure stability

Advanced topics (future guides):

  • Liquidsoap for dynamic radio stations
  • SRT protocol for IRL streaming
  • Multi-platform simultaneous streaming (YouTube + Twitch)

Need a powerful VPS for streaming? Space-Node offers Ryzen-based VPS with unmetered bandwidth starting at $10/month. Perfect for 24/7 streams.

Related Guides:

Jochem Wassenaar

About the Author

Jochem Wassenaar – CEO of Space-Node – Experts in game server hosting, VPS infrastructure, and 24/7 streaming solutions with 15+ years combined experience.

Since 2023
500+ servers hosted
4.8/5 avg rating

Our team specializes in Minecraft, FiveM, Rust, and 24/7 streaming infrastructure, operating enterprise-grade AMD Ryzen 9 hardware in Netherlands datacenters. We maintain GDPR compliance and ISO 27001-aligned security standards.

Read full author bio and credentials →

Start Streaming in Minutes

Join content creators worldwide who trust our streaming infrastructure. Setup is instant and support is always available.

Build a Bulletproof 24/7 YouTube Live Stream on Linux VPS: The Complete 2026 Guide