FFmpeg + NGINX HTTPS HLS Streaming Server Ubuntu 24.04
🧩 Step 1 — Update system &and install dependencies
sudo apt update && sudo apt upgrade -y
sudo apt install -y ffmpeg nginx python3-yaml openssl
Create🗂️ self-signedStep SSL2 cert— forDirectory HTTPSsetupPlease change domain with your own domain
sudo mkdir -p /etc/ssl/privateusr/local/ffmpeg-hls
/etc/ssl/certs
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/ssl/private/stream-selfsigned.key \
-out /etc/ssl/certs/stream-selfsigned.crt \
-subj "/CN=stream.local"Create web root for HLS
sudo mkdir -p /var/www/html/{cam1,cam2,cam3}cctv
sudo chmodchown -R 777www-data:www-data /var/www/html
Configure🧾 NGINXStep for3 HTTPS— +Camera CORSconfiguration file
Create file /usr/local/ffmpeg-hls/cams.yml
sudo- nanoname: /etc/nginx/sites-available/stream.confcam1
url: rtsp-url-here
- name: cam2
url: rtsp-url-here
- name: cam3
url: rtsp-url-here
# Add up to cam16 if needed...
Paste⚙️ this:
server4 {— listen 443 ssl;
server_name stream.local;
ssl_certificate /etc/ssl/certs/stream-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/stream-selfsigned.key;
root /var/www/html;
location / {
autoindex on;
add_header Access-Control-Allow-Origin *;
}
}Enable and restart:
sudo ln -s /etc/nginx/sites-available/stream.conf /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl restart nginxCreateAuto FFmpeg scriptslauncher forscript
Create eachfile camera;/usr/local/ffmpeg-hls/start-all-cams.sh
#!/bin/bash
CONFIG_FILE="/usr/local/ffmpeg-hls/cams.yml"
OUTPUT_ROOT="/var/www/html/cctv"
HLS_DURATION=10 # CamXseconds per segment
TOTAL_HISTORY_MIN=10 # total minutes to keep
SEGMENTS_TO_KEEP=$(( (TOTAL_HISTORY_MIN*60) / HLS_DURATION ))
mkdir -p "$OUTPUT_ROOT"
# 1️⃣ Generate list of active cams
python3 - Keep<<'PYCODE' 10> minutes/tmp/camlist.txt
import yaml, sys
data = yaml.safe_load(open("/usr/local/ffmpeg-hls/cams.yml"))
for cam in data:
print(f"{cam['name']}|{cam['url']}")
PYCODE
# 2️⃣ Build array of videoactive cam names
ACTIVE_CAMS=($(awk -F'|' '{print $1}' /tmp/camlist.txt))
# 3️⃣ Clean up old folders not in YAML
for dir in "$OUTPUT_ROOT"/*/; do
[ -d "$dir" ] || continue
CAM_NAME=$(basename "$dir")
if [[ ! " ${ACTIVE_CAMS[@]} " =~ " ${CAM_NAME} " ]]; then
echo "🧹 Removing old folder: $dir"
rm -rf "$dir"
fi
done
# 4️⃣ Start each active camera
while IFS="|" read -r NAME URL; do
[ -z "$NAME" ] && continue
mkdir -p "$OUTPUT_ROOT/$NAME"
echo "🎥 Starting $NAME ..."
(stable + low disk usage)
while true; do
ffmpeg -hide_banner -loglevel warning \
-rtsp_transport tcp \
-i #change-this-with-camera-rtsp-url"$URL" \
-fflags +genpts -use_wallclock_as_timestamps 1 \
-an -c:v copy \
-f hls \
-hls_time 10$HLS_DURATION \
-hls_list_size 60$SEGMENTS_TO_KEEP \
-hls_flags delete_segments+append_list+program_date_time \
-hls_segment_filename /var/www/html/camX/"$OUTPUT_ROOT/$NAME/segment_%05d.tsts" \
/var/www/html/camX/"$OUTPUT_ROOT/$NAME/index.m3u8m3u8"
echo "CamX⚠️ disconnected$NAME ordisconnected, FFmpeg crashed, restartingretrying in 5s..."
sleep 5
done
Change file permision
sudo chmod +x /usr/local/bin/cam*-stream.shCreate systemd services (auto start) &
restart)
sudo tee< /etc/systemd/system/camX.servicetmp/camlist.txt
<<'EOF'echo [Unit]"✅ Description=FFmpegAll HLSactive Streamcamera -streams Camerastarted!"
X
After=network.target
[Service]
ExecStart=/usr/local/bin/camX-stream.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFwait
Enable all systemd services
sudo systemctl daemon-reload
sudo systemctl enable --now cam1 cam2 cam3Verify cam services is running
sudo systemctl status camXYou should see them active. Then in browser:
https://<yourip-or-yourdomain>/cam1/index.m3u8
https://<yourip-or-yourdomain>/cam2/index.m3u8
https://<yourip-or-yourdomain>/cam3/index.m3u8*Notes
-hls_time 10 → each segment = 10 seconds-hls_list_size 60 → keeps ~10 minutes (60 × 10 s)delete_segments → automatically removes older .ts filesUses timestamps (program_date_time) for easier tracking