Skip to main content

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 HTTPSsetup
Please 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:

Step
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 nginx

CreateAuto 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.sh

Create systemd services (auto start) & restart)

done
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 cam3

Verify cam services is running

sudo systemctl status camX

You 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 files
Uses timestamps (program_date_time) for easier tracking