VPS OBS Streaming: From 403 to RTMP + AutoDJ
How Database Mart engineers guided a customer through SSL setup, Nginx RTMP configuration, OBS live streaming, WebDAV file upload, and Node.js AutoDJ deployment — all on a single VPS.
* A 7-day end-to-end build: SSL → RTMP → HLS streaming → WebDAV uploads → AutoDJ 24/7 broadcast — zero server reset required.
Nginx Not Listening on Port 443 — SSL from Zero
A customer reached out with a VPS running Nginx and a goal: build a WebTV and live streaming platform using Lovable.dev as the frontend framework. The immediate blocker was a 403 Forbidden error — his frontend app couldn't reach the VPS backend.
Engineer Alina identified the root cause quickly: Nginx was not configured to listen on port 443, and no SSL certificate existed. This meant the https:// connection his frontend required was simply not possible yet.
After the customer pointed his DNS A record to the VPS IP, engineer Robert issued and installed a free Let's Encrypt SSL certificate, configured port 443, and set up HTTP-to-HTTPS redirection on port 80.
Result: curl -I https://<SUBDOMAIN>.<DOMAIN>/ returned 200 OK. The SSL layer was live.
Configuring Nginx RTMP for OBS Live Streaming on a VPS
With SSL in place, the customer's real goal came into focus: he needed the VPS to receive an RTMP live stream from OBS, and serve it as HLS/DASH for playback in his Lovable.dev web app. Engineer Hiker took on the build.
Hiker installed the libnginx-mod-rtmp module and rewrote the /etc/nginx/nginx.conf from scratch to support:
- RTMP ingestion with a
liveapplication block - HLS and DASH segment generation under
/var/www/nginx-rtmp/hlsand/dash - HTTPS serving on port 443 with full CORS headers for cross-origin frontend access
- WebSocket upgrade support for real-time frontend communication
- The
/statRTMP status endpoint for live monitoring
Server:
rtmp://<VPS_IP>/liveStream Key:
<STREAM_KEY>⚠️ No trailing slash after
/live — OBS is strict about URL formatting.First OBS connection succeeded — but playback returned a 404. Investigation revealed that Nginx was creating a nested folder structure for HLS segments instead of a flat .m3u8 file. Hiker patched the configuration, cleared the cache, and verified the correct output:
# Correct HLS output after fix:
# https://<SUBDOMAIN>.<DOMAIN>/hls/<STREAM_KEY>.m3u8
# curl -I returns 200 OK ✓
# nginx.conf rtmp block (key excerpt):
rtmp {
server {
listen 1935;
application live {
live on;
hls on;
hls_path /var/www/nginx-rtmp/hls;
hls_fragment 2s;
dash on;
dash_path /var/www/nginx-rtmp/dash;
}
}
}
The /stat Always Shows 0 Publishers — A Multi-Core Nginx Bug
The customer's dashboard was showing 0 publishers even while OBS was actively streaming. The /stat endpoint appeared broken. Hiker identified the real cause — a classic Nginx RTMP multi-worker issue:
By default Nginx spreads load across multiple CPU worker processes. The nginx-rtmp-module does not share stream state between workers. OBS connected to Core A; the /stat request was answered by Core B — which had no knowledge of the active stream, so it reported zero publishers.
Fix: Set worker_processes 1; in nginx.conf to force all RTMP connections and stat queries into the same memory space. After restarting Nginx, the dashboard showed live publisher count, viewer count, and bandwidth data correctly.
Hiker also proactively created the /media/ directory with autoindex_format json; enabled — anticipating the customer's next requirement for the AutoDJ file library.
Enabling WebDAV PUT on VPS for Drag-and-Drop File Uploads
The customer's Lovable.dev frontend included a WebTV Management section with drag-and-drop file uploads, a playlist manager, and a scheduler. These features required the VPS to accept HTTP PUT requests for file uploads via WebDAV.
The initial error was clear: "dav_methods PUT must be enabled." Engineer Queta applied the following WebDAV directives to the /media Nginx location block:
# WebDAV config added to /media location: client_max_body_size 5G; dav_methods PUT DELETE MKCOL; create_full_put_path on; dav_access user:rw group:rw all:r; # Verified with: curl -X PUT -d "hello webdav test" \ https://<SUBDOMAIN>.<DOMAIN>/media/test.txt # → File created at /var/www/nginx-rtmp/media/test.txt ✓
The test upload succeeded. Large media files (up to 5 GB) could now be pushed directly from the web frontend to the VPS media directory, ready for playlist queuing and AutoDJ playback.
Deploying a Node.js AutoDJ Controller on VPS — 24/7 Streaming Without OBS
The customer's ultimate goal: 24/7 automated broadcast — playlists playing continuously on the stream even when his local OBS was offline. The Lovable.dev team confirmed that their app (running on Cloudflare Workers) cannot spawn ffmpeg processes directly, because Cloudflare's serverless environment has no OS-level access.
The solution: deploy a dedicated Node.js AutoDJ Controller on the VPS, exposing HTTP endpoints that the frontend could call to start/stop/query the FFmpeg stream pipeline.
Hiker coordinated the architecture with the customer, providing the VPS hostname and shared secret to the Lovable.dev developer. Once the developer built the service, the customer uploaded the files. Engineer Bruce completed the full deployment:
- Moved service files to
/opt/autodj-control/ - Installed the
autodj-control.servicesystemd unit file - Enabled and started the service:
systemctl enable --now autodj-control - Added the Nginx reverse proxy block:
location /autodj/ { proxy_pass http://127.0.0.1:8787/; } - Reloaded Nginx
# Nginx reverse proxy for AutoDJ controller:
location /autodj/ {
proxy_pass http://127.0.0.1:8787/;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
# Service accessible at:
# https://<SUBDOMAIN>.<DOMAIN>/autodj/
# systemd service check:
systemctl status autodj-control.service
# → Active: active (running) ✓
A minor setback: after the customer updated the app's automation logic, the AutoDJ worker stopped. Robert investigated and found FFmpeg was missing from the system. After installing ffmpeg v6.1.1 and verifying the execution path in /opt/autodj-control/server.js, the service restarted cleanly.
RESOLVED AUTODJ LIVE NO RESET
Geek Tips — Replicate This Stack on Your VPS
If your /stat page always shows zero active streams even while OBS is connected, you're almost certainly hitting the nginx-rtmp multi-worker bug. The fix is simple:
- Open
/etc/nginx/nginx.conf - Set
worker_processes 1;at the top level - Run
nginx -t && systemctl reload nginx - Reconnect OBS and refresh
/stat
💡 The nginx-rtmp-module doesn't share state between worker processes. Forcing a single worker puts OBS connections and stat queries in the same memory space.
OBS is unusually strict about URL format. A trailing slash on the server URL will silently break the connection even if everything else is correct.
- ✅ Correct:
rtmp://your-server-ip/live - ❌ Broken:
rtmp://your-server-ip/live/
Also verify the application name in your rtmp {} block exactly matches what you put in the OBS server URL (e.g. /live).
🔍 Check tail -f /var/log/nginx/error.log while connecting OBS to see real-time rejection reasons.
Two common causes:
- Nested path mode enabled: Nginx is writing segments to a subfolder. Your frontend expects a flat
.m3u8at/hls/<stream-key>.m3u8. Fix: removehls_nested on;if present. - Wrong HLS path permissions: The
hls_pathdirectory must be writable by the Nginx worker user (www-dataon Ubuntu).
mkdir -p /var/www/nginx-rtmp/hls chown -R www-data:www-data /var/www/nginx-rtmp # Then test: curl -I https://your-domain/hls/your-stream-key.m3u8
If your frontend is hosted on Cloudflare Workers, Vercel, or any serverless platform, you cannot run FFmpeg or any long-lived process directly. These environments run in sandboxed V8 isolates with no OS access.
The correct architecture for a VPS-based AutoDJ:
- Deploy a small Node.js or Python service on your VPS (
server.jswith Express) - Expose
POST /autodj/start,POST /autodj/stop,GET /autodj/statusendpoints - Run it as a
systemdservice for 24/7 uptime - Proxy it through Nginx:
location /autodj/ { proxy_pass http://127.0.0.1:8787/; } - Protect the endpoint with a shared secret header
📡 This is exactly the architecture Lovable.dev calls "Option A" — and it works perfectly on any DatabaseMart VPS with Ubuntu.
"From a blank VPS to a 24/7 live streaming platform —
SSL, RTMP, HLS, WebDAV, and AutoDJ.
We don't just set up servers.
We help you ship what you're building."
