Alpine Linux’s minimal footprint makes it an excellent choice for video streaming servers. This guide will help you build a professional video streaming infrastructure capable of handling live broadcasts, video-on-demand, and adaptive bitrate streaming.
Table of Contents
- Prerequisites
- Understanding Video Streaming
- Installing NGINX with RTMP
- Configuring RTMP Streaming
- Setting Up HLS Streaming
- Implementing DASH Streaming
- Video Transcoding Setup
- Live Streaming Configuration
- Video on Demand (VOD)
- Adaptive Bitrate Streaming
- Security and Authentication
- Performance Optimization
- Monitoring and Analytics
- CDN Integration
- Troubleshooting
- Best Practices
- Conclusion
Prerequisites
Before setting up video streaming, ensure you have:
- Alpine Linux with at least 4GB RAM (8GB+ recommended)
- High-speed network connection (1Gbps+ for production)
- Sufficient storage for video files
- Basic understanding of video codecs and streaming protocols
- Domain name for public streaming (optional)
- SSL certificate for HTTPS streaming
Understanding Video Streaming
Streaming Protocols
# Check system resources
free -h
df -h
cat /proc/cpuinfo | grep "model name" | head -1
Key protocols:
- RTMP: Real-Time Messaging Protocol (ingestion)
- HLS: HTTP Live Streaming (delivery)
- DASH: Dynamic Adaptive Streaming over HTTP
- WebRTC: Real-time communication
Installing NGINX with RTMP
Step 1: Build NGINX with RTMP Module
# Install build dependencies
apk add build-base pcre-dev openssl-dev zlib-dev linux-headers
# Create build directory
mkdir -p /tmp/nginx-build
cd /tmp/nginx-build
# Download NGINX source
NGINX_VERSION="1.24.0"
wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar -xzf nginx-${NGINX_VERSION}.tar.gz
# Download RTMP module
git clone https://github.com/arut/nginx-rtmp-module.git
# Configure and build NGINX
cd nginx-${NGINX_VERSION}
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_geoip_module=dynamic \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-stream_realip_module \
--with-stream_geoip_module=dynamic \
--with-http_slice_module \
--with-http_v2_module \
--add-module=../nginx-rtmp-module
# Compile and install
make -j$(nproc)
make install
Step 2: Create NGINX Service
# Create nginx user
adduser -D -s /sbin/nologin nginx
# Create directories
mkdir -p /var/cache/nginx/{client_temp,proxy_temp,fastcgi_temp,uwsgi_temp,scgi_temp}
mkdir -p /var/log/nginx
mkdir -p /etc/nginx/conf.d
# Set permissions
chown -R nginx:nginx /var/cache/nginx
chown -R nginx:nginx /var/log/nginx
# Create init script
cat > /etc/init.d/nginx << 'EOF'
#!/sbin/openrc-run
description="Nginx HTTP and reverse proxy server"
command="/usr/sbin/nginx"
command_args=""
pidfile="/var/run/nginx.pid"
extra_started_commands="reload"
depend() {
need net
use dns logger netmount
}
start_pre() {
checkpath --directory --owner nginx:nginx --mode 0755 /var/log/nginx
/usr/sbin/nginx -t -q
}
reload() {
ebegin "Reloading nginx"
/usr/sbin/nginx -s reload
eend $?
}
EOF
chmod +x /etc/init.d/nginx
Configuring RTMP Streaming
Step 1: Basic RTMP Configuration
# Create NGINX configuration with RTMP
cat > /etc/nginx/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
# RTMP configuration
rtmp {
server {
listen 1935;
chunk_size 4096;
# Live streaming application
application live {
live on;
# Record all streams
record all;
record_path /var/recordings;
record_unique on;
record_suffix .flv;
# Allow publishing from localhost only
allow publish 127.0.0.1;
deny publish all;
# Allow playback from anywhere
allow play all;
# Push to HLS
exec_push /usr/bin/ffmpeg -i rtmp://localhost/live/$name
-c:v libx264 -c:a aac -b:v 2500k -b:a 128k -f flv
rtmp://localhost/hls/$name 2>>/var/log/nginx/ffmpeg-$name.log;
}
# HLS application
application hls {
live on;
hls on;
hls_path /var/www/hls;
hls_fragment 3s;
hls_playlist_length 60s;
# Disable consuming the stream from RTMP
deny play all;
}
}
}
# HTTP configuration
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
# Include additional configurations
include /etc/nginx/conf.d/*.conf;
}
EOF
Step 2: Streaming Server Configuration
# Create streaming server configuration
cat > /etc/nginx/conf.d/streaming.conf << 'EOF'
server {
listen 80;
server_name stream.example.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name stream.example.com;
# SSL configuration
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# RTMP statistics
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /etc/nginx/html;
}
# HLS streaming
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /var/www;
# CORS headers
add_header Access-Control-Allow-Origin *;
add_header Cache-Control no-cache;
# Disable cache
expires -1;
}
# DASH streaming
location /dash {
root /var/www;
types {
application/dash+xml mpd;
video/mp4 mp4;
}
add_header Access-Control-Allow-Origin *;
add_header Cache-Control no-cache;
}
# Video player
location / {
root /var/www/html;
index index.html;
}
# Control interface
location /control {
rtmp_control all;
}
}
EOF
Setting Up HLS Streaming
Step 1: HLS Configuration
# Create HLS directories
mkdir -p /var/www/hls
chown -R nginx:nginx /var/www/hls
# Enhanced HLS configuration
cat > /etc/nginx/conf.d/hls-advanced.conf << 'EOF'
# Advanced HLS configuration
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /var/www;
# CORS configuration
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control no-cache;
# Client caching
location ~ \.ts$ {
add_header Cache-Control "public, max-age=3600";
}
location ~ \.m3u8$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma no-cache;
add_header Expires 0;
}
}
EOF
Step 2: HLS Player Setup
# Create web player
mkdir -p /var/www/html
cat > /var/www/html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Live Stream Player</title>
<link href="https://vjs.zencdn.net/7.20.3/video-js.css" rel="stylesheet">
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
background: #1a1a1a;
color: #fff;
}
.video-container {
max-width: 1280px;
margin: 0 auto;
}
.video-js {
width: 100%;
height: auto;
aspect-ratio: 16/9;
}
.stream-info {
margin-top: 20px;
padding: 15px;
background: #2a2a2a;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="video-container">
<video id="player" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto">
<source src="/hls/stream.m3u8" type="application/x-mpegURL">
</video>
<div class="stream-info">
<h3>Stream Information</h3>
<div id="stats"></div>
</div>
</div>
<script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
<script>
var player = videojs('player', {
autoplay: true,
controls: true,
fluid: true,
liveui: true
});
// Update statistics
function updateStats() {
fetch('/stat')
.then(response => response.text())
.then(data => {
// Parse and display statistics
document.getElementById('stats').innerHTML = 'Connected';
});
}
setInterval(updateStats, 5000);
</script>
</body>
</html>
EOF
Implementing DASH Streaming
Step 1: DASH Configuration
# Create DASH directory
mkdir -p /var/www/dash
chown -R nginx:nginx /var/www/dash
# Update RTMP configuration for DASH
cat >> /etc/nginx/nginx.conf << 'EOF'
# DASH application
application dash {
live on;
dash on;
dash_path /var/www/dash;
dash_fragment 3s;
dash_playlist_length 60s;
# Disable consuming the stream from RTMP
deny play all;
}
EOF
Step 2: DASH Transcoding
# Create DASH transcoding script
cat > /usr/local/bin/transcode-dash.sh << 'EOF'
#!/bin/sh
# Transcode to DASH with multiple bitrates
INPUT="$1"
OUTPUT_DIR="/var/www/dash"
STREAM_NAME=$(basename "$INPUT" .flv)
# Create output directory
mkdir -p "$OUTPUT_DIR/$STREAM_NAME"
# Transcode to multiple bitrates
ffmpeg -re -i "$INPUT" \
-map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 \
-c:v libx264 -crf 22 -c:a aac -ar 48000 \
-filter:v:0 scale=w=480:h=270 -maxrate:v:0 600k -b:a:0 64k \
-filter:v:1 scale=w=640:h=360 -maxrate:v:1 900k -b:a:1 128k \
-filter:v:2 scale=w=1280:h=720 -maxrate:v:2 2500k -b:a:2 192k \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
-seg_duration 3 -use_template 1 -use_timeline 1 \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
-f dash "$OUTPUT_DIR/$STREAM_NAME/manifest.mpd"
EOF
chmod +x /usr/local/bin/transcode-dash.sh
Video Transcoding Setup
Step 1: Install FFmpeg
# Install FFmpeg with full codec support
apk add ffmpeg ffmpeg-libs ffmpeg-dev
# Install additional codecs
apk add x264-libs x265 libvpx libvorbis opus-dev
Step 2: Transcoding Profiles
# Create transcoding profiles script
cat > /usr/local/bin/transcode-profiles.sh << 'EOF'
#!/bin/sh
# Video transcoding profiles
INPUT="$1"
OUTPUT_BASE="/var/www/vod"
STREAM_KEY=$(basename "$INPUT" | cut -d. -f1)
# Create output directory
mkdir -p "$OUTPUT_BASE/$STREAM_KEY"
# Source video information
eval $(ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,r_frame_rate \
-of default=noprint_wrappers=1 "$INPUT")
# Calculate frame rate
FPS=$(echo $r_frame_rate | bc -l | cut -d. -f1)
# Transcoding function
transcode_variant() {
local width=$1
local height=$2
local bitrate=$3
local audio_bitrate=$4
local profile=$5
echo "Transcoding ${width}x${height} @ ${bitrate}k..."
ffmpeg -i "$INPUT" \
-c:v libx264 -profile:v $profile \
-preset medium -crf 23 \
-maxrate ${bitrate}k -bufsize $((bitrate*2))k \
-vf "scale=${width}:${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:(ow-iw)/2:(oh-ih)/2" \
-c:a aac -b:a ${audio_bitrate}k -ar 44100 \
-movflags +faststart \
-y "$OUTPUT_BASE/$STREAM_KEY/${width}x${height}.mp4"
}
# Generate variants based on source resolution
if [ $width -ge 1920 ]; then
transcode_variant 1920 1080 5000 192 high
transcode_variant 1280 720 2500 128 main
transcode_variant 854 480 1200 96 main
transcode_variant 640 360 800 96 baseline
elif [ $width -ge 1280 ]; then
transcode_variant 1280 720 2500 128 main
transcode_variant 854 480 1200 96 main
transcode_variant 640 360 800 96 baseline
else
transcode_variant $width $height 1200 128 main
fi
# Generate HLS playlist
echo "Generating HLS playlist..."
ffmpeg -i "$INPUT" \
-c:v copy -c:a copy \
-f hls -hls_time 6 -hls_list_size 0 \
-hls_segment_filename "$OUTPUT_BASE/$STREAM_KEY/segment_%03d.ts" \
"$OUTPUT_BASE/$STREAM_KEY/playlist.m3u8"
echo "Transcoding complete for $STREAM_KEY"
EOF
chmod +x /usr/local/bin/transcode-profiles.sh
Live Streaming Configuration
Step 1: Stream Ingestion Setup
# Create stream authentication script
cat > /usr/local/bin/stream-auth.sh << 'EOF'
#!/bin/sh
# Stream authentication script
# Get stream key from URL
STREAM_KEY="$1"
# Validate stream key (implement your logic)
VALID_KEYS="/etc/nginx/stream_keys.txt"
if grep -q "^${STREAM_KEY}$" "$VALID_KEYS"; then
exit 0 # Allow
else
exit 1 # Deny
fi
EOF
chmod +x /usr/local/bin/stream-auth.sh
# Create stream keys file
touch /etc/nginx/stream_keys.txt
chmod 600 /etc/nginx/stream_keys.txt
Step 2: Advanced RTMP Configuration
# Update RTMP configuration with authentication
cat > /etc/nginx/rtmp-advanced.conf << 'EOF'
rtmp {
server {
listen 1935;
ping 30s;
notify_method get;
application live {
live on;
# Stream authentication
on_publish http://localhost/auth/publish;
on_publish_done http://localhost/auth/publish_done;
# Allow only authenticated publishers
allow publish all;
allow play all;
# Record streams
record all;
record_path /var/recordings;
record_unique on;
record_suffix .flv;
# Notify on events
on_play http://localhost/events/play;
on_play_done http://localhost/events/play_done;
# Multi-bitrate transcoding
exec_push /usr/local/bin/transcode-live.sh $name;
# Push to multiple outputs
# push rtmp://youtube.com/live/STREAM_KEY;
# push rtmp://twitch.tv/live/STREAM_KEY;
}
# Transcoded streams
application show {
live on;
# Different quality streams
# Original
on_publish http://localhost/auth/transcode;
# HLS output
hls on;
hls_path /var/www/hls;
hls_fragment 3s;
hls_playlist_length 60s;
# Variants
hls_variant _low BANDWIDTH=800000;
hls_variant _mid BANDWIDTH=1200000;
hls_variant _high BANDWIDTH=2500000;
}
}
}
EOF
Step 3: Live Transcoding Script
# Create live transcoding script
cat > /usr/local/bin/transcode-live.sh << 'EOF'
#!/bin/sh
# Live stream transcoding
STREAM_KEY="$1"
RTMP_URL="rtmp://localhost/live/$STREAM_KEY"
# Low quality
ffmpeg -i "$RTMP_URL" \
-c:v libx264 -preset veryfast -tune zerolatency \
-vf scale=640:360 -b:v 800k -maxrate 800k -bufsize 800k \
-c:a aac -b:a 96k -ar 44100 \
-f flv "rtmp://localhost/show/${STREAM_KEY}_low" \
2>/var/log/nginx/transcode-${STREAM_KEY}-low.log &
# Medium quality
ffmpeg -i "$RTMP_URL" \
-c:v libx264 -preset veryfast -tune zerolatency \
-vf scale=854:480 -b:v 1200k -maxrate 1200k -bufsize 1200k \
-c:a aac -b:a 128k -ar 44100 \
-f flv "rtmp://localhost/show/${STREAM_KEY}_mid" \
2>/var/log/nginx/transcode-${STREAM_KEY}-mid.log &
# High quality
ffmpeg -i "$RTMP_URL" \
-c:v copy \
-c:a copy \
-f flv "rtmp://localhost/show/${STREAM_KEY}_high" \
2>/var/log/nginx/transcode-${STREAM_KEY}-high.log &
# Save PIDs for cleanup
echo $! > /var/run/transcode-${STREAM_KEY}.pid
EOF
chmod +x /usr/local/bin/transcode-live.sh
Video on Demand (VOD)
Step 1: VOD Configuration
# Create VOD directory structure
mkdir -p /var/www/vod/{videos,thumbnails,metadata}
chown -R nginx:nginx /var/www/vod
# VOD server configuration
cat > /etc/nginx/conf.d/vod.conf << 'EOF'
server {
listen 8080;
server_name vod.example.com;
root /var/www/vod;
# MP4 streaming
location ~ \.mp4$ {
mp4;
mp4_buffer_size 1m;
mp4_max_buffer_size 5m;
# Enable seeking
add_header Accept-Ranges bytes;
# CORS
add_header Access-Control-Allow-Origin *;
# Cache control
expires 7d;
add_header Cache-Control "public, immutable";
}
# Thumbnail serving
location /thumbnails {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Video metadata API
location /api/videos {
default_type application/json;
content_by_lua_block {
-- Implement video listing logic
}
}
# Pseudo-streaming for FLV
location ~ \.flv$ {
flv;
}
}
EOF
Step 2: VOD Processing Pipeline
# Create VOD processing script
cat > /usr/local/bin/process-vod.sh << 'EOF'
#!/bin/sh
# VOD processing pipeline
INPUT_FILE="$1"
VIDEO_ID=$(uuidgen)
VOD_DIR="/var/www/vod"
# Directories
VIDEO_DIR="$VOD_DIR/videos/$VIDEO_ID"
THUMB_DIR="$VOD_DIR/thumbnails/$VIDEO_ID"
META_FILE="$VOD_DIR/metadata/$VIDEO_ID.json"
mkdir -p "$VIDEO_DIR" "$THUMB_DIR"
# Get video information
DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$INPUT_FILE")
WIDTH=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 "$INPUT_FILE")
HEIGHT=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 "$INPUT_FILE")
# Generate thumbnails
for i in 1 2 3 4 5; do
TIME=$(echo "$DURATION * $i / 6" | bc)
ffmpeg -ss "$TIME" -i "$INPUT_FILE" -vframes 1 -vf scale=320:180 "$THUMB_DIR/thumb_$i.jpg" -y
done
# Create preview gif
ffmpeg -i "$INPUT_FILE" -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -t 5 "$THUMB_DIR/preview.gif"
# Transcode video
/usr/local/bin/transcode-profiles.sh "$INPUT_FILE"
# Generate metadata
cat > "$META_FILE" << EOJ
{
"id": "$VIDEO_ID",
"filename": "$(basename "$INPUT_FILE")",
"duration": $DURATION,
"width": $WIDTH,
"height": $HEIGHT,
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"variants": [
{"quality": "1080p", "url": "/videos/$VIDEO_ID/1920x1080.mp4"},
{"quality": "720p", "url": "/videos/$VIDEO_ID/1280x720.mp4"},
{"quality": "480p", "url": "/videos/$VIDEO_ID/854x480.mp4"},
{"quality": "360p", "url": "/videos/$VIDEO_ID/640x360.mp4"}
],
"thumbnails": [
"/thumbnails/$VIDEO_ID/thumb_1.jpg",
"/thumbnails/$VIDEO_ID/thumb_2.jpg",
"/thumbnails/$VIDEO_ID/thumb_3.jpg",
"/thumbnails/$VIDEO_ID/thumb_4.jpg",
"/thumbnails/$VIDEO_ID/thumb_5.jpg"
],
"preview": "/thumbnails/$VIDEO_ID/preview.gif"
}
EOJ
echo "VOD processing complete: $VIDEO_ID"
EOF
chmod +x /usr/local/bin/process-vod.sh
Adaptive Bitrate Streaming
Step 1: ABR Implementation
# Create ABR manifest generator
cat > /usr/local/bin/generate-abr-manifest.sh << 'EOF'
#!/bin/sh
# Generate adaptive bitrate manifest
VIDEO_ID="$1"
BASE_URL="https://stream.example.com"
VOD_DIR="/var/www/vod/videos/$VIDEO_ID"
# Create master playlist
cat > "$VOD_DIR/master.m3u8" << EOM
#EXTM3U
#EXT-X-VERSION:3
# 1080p
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
${BASE_URL}/vod/videos/${VIDEO_ID}/1080p/playlist.m3u8
# 720p
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
${BASE_URL}/vod/videos/${VIDEO_ID}/720p/playlist.m3u8
# 480p
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=854x480,CODECS="avc1.64001e,mp4a.40.2"
${BASE_URL}/vod/videos/${VIDEO_ID}/480p/playlist.m3u8
# 360p
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2"
${BASE_URL}/vod/videos/${VIDEO_ID}/360p/playlist.m3u8
EOM
# Generate HLS segments for each quality
for quality in 1080p 720p 480p 360p; do
mkdir -p "$VOD_DIR/$quality"
case $quality in
1080p) res="1920x1080" ;;
720p) res="1280x720" ;;
480p) res="854x480" ;;
360p) res="640x360" ;;
esac
if [ -f "$VOD_DIR/${res}.mp4" ]; then
ffmpeg -i "$VOD_DIR/${res}.mp4" \
-c copy -f hls \
-hls_time 6 \
-hls_list_size 0 \
-hls_segment_filename "$VOD_DIR/$quality/segment_%03d.ts" \
"$VOD_DIR/$quality/playlist.m3u8"
fi
done
echo "ABR manifest generated for $VIDEO_ID"
EOF
chmod +x /usr/local/bin/generate-abr-manifest.sh
Step 2: Bandwidth Detection
# Create bandwidth detection endpoint
cat > /var/www/html/bandwidth-test.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Bandwidth Test</title>
</head>
<body>
<script>
function measureBandwidth() {
const testSize = 1048576; // 1MB
const testUrl = '/test-file.bin';
const startTime = Date.now();
fetch(testUrl)
.then(response => response.blob())
.then(blob => {
const endTime = Date.now();
const duration = (endTime - startTime) / 1000; // seconds
const bitsLoaded = blob.size * 8;
const bandwidth = bitsLoaded / duration;
console.log(`Bandwidth: ${(bandwidth / 1000000).toFixed(2)} Mbps`);
// Store bandwidth for player
localStorage.setItem('estimatedBandwidth', bandwidth);
});
}
// Test on load
measureBandwidth();
// Periodic testing
setInterval(measureBandwidth, 30000);
</script>
</body>
</html>
EOF
# Generate test file
dd if=/dev/urandom of=/var/www/html/test-file.bin bs=1M count=1
Security and Authentication
Step 1: Stream Key Management
# Create stream key management script
cat > /usr/local/bin/manage-stream-keys.sh << 'EOF'
#!/bin/sh
# Stream key management
KEYS_FILE="/etc/nginx/stream_keys.txt"
KEYS_DB="/var/lib/streaming/keys.db"
case "$1" in
add)
KEY=$(openssl rand -hex 16)
USER="$2"
echo "$KEY # $USER - Added $(date)" >> "$KEYS_FILE"
echo "Stream key for $USER: $KEY"
;;
remove)
KEY="$2"
sed -i "/^$KEY/d" "$KEYS_FILE"
echo "Removed key: $KEY"
;;
list)
cat "$KEYS_FILE"
;;
*)
echo "Usage: $0 {add|remove|list} [user|key]"
exit 1
;;
esac
# Reload NGINX
nginx -s reload
EOF
chmod +x /usr/local/bin/manage-stream-keys.sh
Step 2: Secure Token Authentication
# Create secure token module configuration
cat > /etc/nginx/conf.d/secure-token.conf << 'EOF'
# Secure token for VOD
location /secure/ {
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri SECRET_KEY";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
# Serve video
rewrite ^/secure/(.*)$ /vod/$1 last;
}
# Token generation endpoint
location /api/generate-token {
content_by_lua_block {
local expires = ngx.time() + 3600 -- 1 hour
local uri = ngx.var.arg_uri
local secret = "SECRET_KEY"
local string_to_hash = expires .. uri .. secret
local md5 = ngx.md5(string_to_hash)
local token_url = "/secure" .. uri .. "?md5=" .. md5 .. "&expires=" .. expires
ngx.header.content_type = "application/json"
ngx.say('{"url":"' .. token_url .. '","expires":' .. expires .. '}')
}
}
EOF
Performance Optimization
Step 1: NGINX Optimization
# Update NGINX configuration for performance
cat > /etc/nginx/nginx-optimized.conf << 'EOF'
user nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
# Basic settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Buffer sizes
client_body_buffer_size 128k;
client_max_body_size 0;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
output_buffers 32 32k;
postpone_output 1460;
# Timeouts
client_header_timeout 10s;
client_body_timeout 10s;
send_timeout 10s;
keepalive_timeout 65s;
keepalive_requests 100;
# File cache
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
# Rate limiting
limit_req_zone $binary_remote_addr zone=streaming:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
}
EOF
Step 2: System Tuning
# Create system tuning script
cat > /usr/local/bin/tune-streaming-server.sh << 'EOF'
#!/bin/sh
# System tuning for streaming
# Network tuning
cat >> /etc/sysctl.conf << EOC
# Network performance tuning
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_slow_start_after_idle = 0
# Connection handling
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
EOC
sysctl -p
# File system tuning
echo "fs.file-max = 1000000" >> /etc/sysctl.conf
# Set ulimits
cat >> /etc/security/limits.conf << EOL
nginx soft nofile 65535
nginx hard nofile 65535
nginx soft nproc 65535
nginx hard nproc 65535
EOL
echo "System tuning applied"
EOF
chmod +x /usr/local/bin/tune-streaming-server.sh
Monitoring and Analytics
Step 1: Streaming Analytics
# Create analytics collection script
cat > /usr/local/bin/collect-streaming-stats.sh << 'EOF'
#!/bin/sh
# Collect streaming statistics
STATS_DIR="/var/log/streaming/stats"
DATE=$(date +%Y%m%d_%H%M%S)
STATS_FILE="$STATS_DIR/stats_$DATE.json"
mkdir -p "$STATS_DIR"
# Get RTMP statistics
curl -s http://localhost/stat | \
xmlstarlet sel -t -m "//application/live/stream" \
-o '{"name":"' -v "name" -o '",' \
-o '"time":"' -v "time" -o '",' \
-o '"bw_in":"' -v "bw_in" -o '",' \
-o '"bytes_in":"' -v "bytes_in" -o '",' \
-o '"bw_out":"' -v "bw_out" -o '",' \
-o '"bytes_out":"' -v "bytes_out" -o '",' \
-o '"clients":"' -v "nclients" -o '"}' -n \
> "$STATS_FILE"
# Parse NGINX access logs
awk '$9 ~ /^2/ {print $1}' /var/log/nginx/access.log | \
sort | uniq -c | sort -rn | head -20 > "$STATS_DIR/top_ips_$DATE.txt"
# Bandwidth usage
vnstat -i eth0 --json > "$STATS_DIR/bandwidth_$DATE.json"
# Clean old stats
find "$STATS_DIR" -name "*.json" -mtime +7 -delete
find "$STATS_DIR" -name "*.txt" -mtime +7 -delete
EOF
chmod +x /usr/local/bin/collect-streaming-stats.sh
# Add to cron
echo "*/5 * * * * /usr/local/bin/collect-streaming-stats.sh" >> /etc/crontabs/root
Step 2: Monitoring Dashboard
# Create monitoring dashboard
cat > /var/www/html/dashboard.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Streaming Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-value {
font-size: 2em;
font-weight: bold;
color: #333;
}
.stat-label {
color: #666;
margin-top: 5px;
}
.chart-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>Streaming Server Dashboard</h1>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="active-streams">0</div>
<div class="stat-label">Active Streams</div>
</div>
<div class="stat-card">
<div class="stat-value" id="total-viewers">0</div>
<div class="stat-label">Total Viewers</div>
</div>
<div class="stat-card">
<div class="stat-value" id="bandwidth-usage">0 Mbps</div>
<div class="stat-label">Bandwidth Usage</div>
</div>
<div class="stat-card">
<div class="stat-value" id="server-uptime">0h</div>
<div class="stat-label">Server Uptime</div>
</div>
</div>
<div class="chart-container">
<canvas id="viewersChart"></canvas>
</div>
<div class="chart-container">
<canvas id="bandwidthChart"></canvas>
</div>
</div>
<script>
// Initialize charts
const viewersCtx = document.getElementById('viewersChart').getContext('2d');
const viewersChart = new Chart(viewersCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Concurrent Viewers',
data: [],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Viewers Over Time'
}
}
}
});
// Update function
function updateStats() {
fetch('/api/stats')
.then(response => response.json())
.then(data => {
document.getElementById('active-streams').textContent = data.streams;
document.getElementById('total-viewers').textContent = data.viewers;
document.getElementById('bandwidth-usage').textContent = data.bandwidth + ' Mbps';
document.getElementById('server-uptime').textContent = data.uptime;
// Update charts
const now = new Date().toLocaleTimeString();
viewersChart.data.labels.push(now);
viewersChart.data.datasets[0].data.push(data.viewers);
// Keep last 20 data points
if (viewersChart.data.labels.length > 20) {
viewersChart.data.labels.shift();
viewersChart.data.datasets[0].data.shift();
}
viewersChart.update();
});
}
// Update every 5 seconds
setInterval(updateStats, 5000);
updateStats();
</script>
</body>
</html>
EOF
CDN Integration
Step 1: CDN Push Configuration
# Create CDN push script
cat > /usr/local/bin/push-to-cdn.sh << 'EOF'
#!/bin/sh
# Push streams to CDN
STREAM_KEY="$1"
CDN_ENDPOINTS="/etc/nginx/cdn_endpoints.conf"
# Read CDN endpoints
while IFS='=' read -r name url; do
if [ -n "$name" ] && [ -n "$url" ]; then
echo "Pushing to $name"
ffmpeg -re -i "rtmp://localhost/live/$STREAM_KEY" \
-c copy -f flv "$url/$STREAM_KEY" \
2>/var/log/nginx/cdn-push-${name}-${STREAM_KEY}.log &
# Save PID for cleanup
echo $! > "/var/run/cdn-push-${name}-${STREAM_KEY}.pid"
fi
done < "$CDN_ENDPOINTS"
EOF
chmod +x /usr/local/bin/push-to-cdn.sh
# Create CDN endpoints configuration
cat > /etc/nginx/cdn_endpoints.conf << 'EOF'
# CDN Endpoints
primary=rtmp://cdn1.example.com/live
backup=rtmp://cdn2.example.com/live
# youtube=rtmp://a.rtmp.youtube.com/live2
# twitch=rtmp://live.twitch.tv/app
EOF
Step 2: CDN Origin Configuration
# Configure as CDN origin
cat > /etc/nginx/conf.d/cdn-origin.conf << 'EOF'
# CDN Origin Configuration
server {
listen 80;
server_name origin.example.com;
# HLS origin
location /hls {
root /var/www;
# Allow CDN servers only
allow 10.0.0.0/8;
allow 172.16.0.0/12;
deny all;
# Cache headers for CDN
add_header Cache-Control "public, max-age=3600";
add_header X-Content-Type-Options nosniff;
# CORS for CDN
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
}
# Purge cache endpoint
location /purge {
allow 127.0.0.1;
deny all;
# Implement cache purge logic
}
}
EOF
Troubleshooting
Common Issues
- Stream buffering issues:
# Check NGINX error logs
tail -f /var/log/nginx/error.log
# Monitor RTMP connections
netstat -an | grep 1935
# Check FFmpeg logs
tail -f /var/log/nginx/ffmpeg-*.log
- High CPU usage:
# Monitor FFmpeg processes
htop -p $(pgrep ffmpeg | tr '\n' ',')
# Optimize encoding settings
# Use hardware acceleration if available
ffmpeg -hwaccels
- Stream authentication fails:
# Check auth script
sh -x /usr/local/bin/stream-auth.sh test-key
# Verify stream keys file
cat /etc/nginx/stream_keys.txt
# Test RTMP publish
ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost/live/test-key
Debug Configuration
# Enable debug logging
cat >> /etc/nginx/nginx.conf << 'EOF'
error_log /var/log/nginx/debug.log debug;
rtmp {
access_log /var/log/nginx/rtmp_access.log;
}
EOF
# Monitor RTMP statistics
watch -n 1 'curl -s http://localhost/stat | grep -E "(nclients|time|bytes)"'
Best Practices
Production Checklist
# Create production setup script
cat > /usr/local/bin/production-setup.sh << 'EOF'
#!/bin/sh
# Production streaming server setup
echo "=== Production Setup Checklist ==="
# 1. Security
echo "[1] Configuring firewall..."
iptables -A INPUT -p tcp --dport 1935 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 1935 -j DROP
# 2. SSL certificates
echo "[2] Checking SSL certificates..."
certbot certificates
# 3. Monitoring
echo "[3] Setting up monitoring..."
apk add monit
cat > /etc/monit/conf.d/streaming << EOM
check process nginx with pidfile /var/run/nginx.pid
start program = "/etc/init.d/nginx start"
stop program = "/etc/init.d/nginx stop"
if failed host localhost port 80 then restart
if cpu > 80% for 5 cycles then alert
if memory > 80% then alert
EOM
# 4. Backup configuration
echo "[4] Backing up configuration..."
tar -czf /backup/streaming-config-$(date +%Y%m%d).tar.gz \
/etc/nginx \
/usr/local/bin \
/var/www/html
# 5. Performance testing
echo "[5] Running performance test..."
ab -n 1000 -c 100 http://localhost/hls/test.m3u8
echo "=== Setup Complete ==="
EOF
chmod +x /usr/local/bin/production-setup.sh
Maintenance Tasks
# Create maintenance script
cat > /etc/periodic/daily/streaming-maintenance << 'EOF'
#!/bin/sh
# Daily streaming server maintenance
# Clean old recordings
find /var/recordings -name "*.flv" -mtime +7 -delete
# Clean old logs
find /var/log/nginx -name "*.log" -mtime +30 -delete
# Rotate logs
/usr/sbin/logrotate /etc/logrotate.d/nginx
# Clean HLS segments
find /var/www/hls -name "*.ts" -mmin +60 -delete
# Update stream statistics
/usr/local/bin/collect-streaming-stats.sh
# Check disk space
df -h /var/www | mail -s "Streaming Server Disk Usage" [email protected]
EOF
chmod +x /etc/periodic/daily/streaming-maintenance
Conclusion
You’ve successfully configured a comprehensive video streaming solution on Alpine Linux. This setup supports multiple streaming protocols, adaptive bitrate streaming, live broadcasting, and video-on-demand services. The lightweight nature of Alpine Linux combined with NGINX’s efficiency provides a robust platform for streaming media.
Remember to regularly monitor your streams, optimize encoding settings based on your audience, and keep your security configurations updated. With proper maintenance and monitoring, this streaming server can handle professional broadcasting requirements while maintaining excellent performance.