You want to stream. Not to Twitch — to your server. But when you start looking at self-hosted options, you’ll find three very different paths, and picking the wrong one will either bury you in complexity or leave you wishing you’d had chat.
Let’s sort this out.
The Three Lanes
Owncast is Twitch in a box. Single binary, live streaming, built-in chat, viewer directory, integrations. You point your OBS at it and go live. It’s the “I want all the features” play.
PeerTube is YouTube on a budget, but federated. It’s a full video platform with upload/VOD storage, transcoding pipelines, and federation with other PeerTube instances via ActivityPub. It can stream live, but that’s an add-on. This is for “I’m building a video archive” energy.
nginx-rtmp is the “I’ll do it myself” module. It’s RTMP ingest + HLS output with no UI, no auth, no chat. You configure it and connect OBS directly. This is the minimal pipeline — and sometimes minimal is exactly what you need.
The question isn’t “which is best.” It’s “what are you actually doing?”
Owncast: The Twitch Replacement
Owncast is built for one thing: live streaming with an audience. You get RTMP ingest (point OBS at it), HLS playback, chat, follower notifications, webhooks, and a nice web UI out of the box.
Install and Run
curl -s https://owncast.online/install.sh | bash./owncastThat’s it. Head to http://localhost:8080/admin, set a password, and you’re done. No database, no dependencies. The whole thing is a single binary.
Want it in Docker?
services: owncast: image: owncast/owncast:latest ports: - "1935:1935" # RTMP ingest - "8080:8080" # HTTP volumes: - owncast-data:/home/owncast/data environment: TZ: UTC
volumes: owncast-data:Connect OBS
In OBS, go to Settings → Stream:
- Service: Custom
- Server:
rtmp://your-domain.com:1935/live - Stream key: Find this in the Owncast admin dashboard (Settings → RTMP → Stream key)
Hit “Start Streaming” and you’re live. Viewers go to https://your-domain.com and they see your stream + chat.
What Owncast Does Well
Chat is first-class. Built-in moderator tools, emotes, user registration optional. This matters for live events.
Audience discovery. Owncast has a public directory where you can list your stream. It’s not huge, but people find streams there.
Webhooks. Want to ring a Discord bell when you go live? Post to a webhook. Shout-outs, follower notifications, raid integrations — all there.
Single server, single binary. No Postgres. No Redis. No object storage dance. Deploy, run, done.
The Catch
No federation. Your Owncast instance is an island. You can’t discover or interact with other Owncast instances.
Transcoding is your problem. Owncast does not transcode. Whatever bitrate you send, viewers receive. Send 6 Mbps H.264 at 1080p, viewers need 6 Mbps. If you want bitrate ladders (720p, 480p, 360p), you transcode on your hardware before ingest or use an external service.
Storage is local disk. VOD recordings (if enabled) go to your server’s storage. Scale to 100 hours of archives and you’re buying disks.
Audience is small. It’s a fantastic piece of software, but most people have never heard of it. You’ll drive your own traffic.
PeerTube: Federated Video Platform
PeerTube is not a live streaming tool first. It’s a YouTube alternative — upload videos, host them, transcode them, and federate them across a network of PeerTube instances that can see and share each other’s content.
Live streaming is an optional feature on top of that architecture.
Install: Prepare Yourself
PeerTube requires:
- PostgreSQL 13+
- Redis 6+
- Node.js 20+ (LTS)
- ffmpeg + ffprobe
- 50+ GB disk minimum (per 100 hours of video at transcoded quality)
- Object storage (S3 or S3-compatible) for serious deployments
This is not a one-binary situation.
# Create a PeerTube usersudo useradd -m -d /home/peertube peertube
# Install Node.js and ffmpegcurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -sudo apt install nodejs ffmpeg postgresql redis-server
# Clone PeerTubecd /home/peertubegit clone https://github.com/Chocobozzz/PeerTube.git peertubecd peertubenpm installnpm run build
# Configurecp server/config/production.yaml.example server/config/production.yaml# Edit production.yaml — set hostname, database, Redis, storage pathsOr use Docker Compose — the official docker-compose.yml is 200+ lines with Postgres, Redis, and a PeerTube service.
Federation: The Secret Sauce
Once your PeerTube instance is live, it announces itself to the fediverse. Other PeerTube instances can see your videos, comment on them (via ActivityPub), and vice versa. You can browse the Peertube Index and discover other instances.
This is powerful for discoverability — but it requires serious uptime, HTTPS, and a stable domain. Spin up a PeerTube instance and abandon it after 6 months, and you’ve left dead links and broken video references across the fediverse.
Live Streaming (Optional Feature)
PeerTube live is a built-in feature (introduced in v3.0). You enable it in the admin settings, configure RTMP ingest, and streamers on your instance can go live. Live streams generate VOD archives automatically.
But here’s the thing: the live experience is minimal. There’s a chat plugin (peertube-plugin-chat), but the UI is barebones compared to Owncast. If live streaming is your primary goal, PeerTube will feel over-engineered.
Storage Reality
PeerTube transcodes uploads into multiple bitrates (1080p, 720p, 480p, etc.). Storage adds up fast. Most deployments push all of that to S3 or Wasabi (cheap S3-compatible storage). Local disk becomes the staging area only.
What PeerTube Does Well
Discoverability via federation. Your videos appear on other instances. People find you through the fediverse.
VOD-first architecture. Transcoding pipeline, automatic quality ladders, storage abstraction. If you’re building a video library, this is the platform.
Community. An active development community, plugin ecosystem, and federation network.
Open source ethics. It’s built by humans who believe in decentralization.
The Catch
Operational overhead. Postgres, Redis, ffmpeg workers, object storage orchestration. You’re running a platform, not a service.
Overkill for live-only. If you just want to stream, you’ve installed 90% of features you won’t use.
Bandwidth costs. Transcoding and serving multiple bitrates to multiple viewers multiplies bandwidth. Cheap S3 storage doesn’t mean cheap egress.
Moderation at scale. More federation = more abuse vectors. You’ll need instance rules, moderation tools, and federation policies.
nginx-rtmp: Roll Your Own
nginx-rtmp is a module that turns nginx into an RTMP server. RTMP ingest, HLS output, no UI, no database, no auth. It’s a plumbing tool.
This is for people who want to:
- Emit HLS from a standard web server
- Build their own front-end
- Skip database complexity entirely
- Minimize CPU/memory overhead
Install the Module
Most distros don’t package nginx with RTMP. You’ll compile it yourself or use a pre-built image:
# Debian/Ubuntu — build from sourcesudo apt install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev
# Download nginx and rtmp modulecd /tmpwget http://nginx.org/download/nginx-1.27.0.tar.gzwget https://github.com/arut/nginx-rtmp-module/archive/master.zip
tar xzf nginx-1.27.0.tar.gzunzip master.zip
cd nginx-1.27.0./configure \ --prefix=/etc/nginx \ --sbin-path=/usr/sbin/nginx \ --modules-path=/usr/lib64/nginx/modules \ --with-http_ssl_module \ --with-http_v2_module \ --add-module=../nginx-rtmp-module-master
make && sudo make installOr use a Docker image with RTMP pre-built — tiangolo/nginx-rtmp or similar.
Configure nginx-rtmp
Here’s the minimal config:
rtmp { server { listen 1935; chunk_size 4096;
application live { live on; record off;
# Push to HLS push rtmp://localhost/hls; }
application hls { live on;
# HLS output hls on; hls_path /tmp/hls; hls_fragment 3; hls_playlist_length 60;
# Bitrate: no transcoding, direct from source } }}
http { server { listen 8080;
location /hls { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } alias /tmp/hls; expires -1; } }}Point OBS at rtmp://your-server:1935/live and it will:
- Ingest RTMP on port 1935
- Push to the local
hlsapplication - Generate HLS playlists in
/tmp/hls - Serve them over HTTP on port 8080
Play the Stream
<video controls width="800" height="600"> <source src="http://your-server:8080/hls/stream.m3u8" type="application/vnd.apple.mpegurl"></video>Done. No transcoding, no chat, no UI. Just RTMP in, HLS out.
What nginx-rtmp Does Well
Minimal footprint. A few MB of memory, no database, no external services.
Standards-based. RTMP is industry standard for ingest. HLS is universal playback. Stack them with anything.
CPU efficient. No transcoding = low CPU. Direct bitrate passthrough.
DIY-friendly. You control the entire pipeline. Add your own auth, chat, overlays via a custom front-end.
The Catch
No UI. Viewers see a bare HTML5 video player. No chat, no follower buttons, no integrations.
No transcoding. Your viewers need the bandwidth you send. 6 Mbps source = 6 Mbps for all viewers. No adaptive bitrate.
No redundancy. Single nginx process dies, stream dies. No failover, no clustering.
DIY hosting. You’re building a streaming platform from scratch. Chat, moderation, recording, VOD archive — you write that layer.
No federation or discovery. Your stream is completely private unless you tell people about it.
The Real Differences
Live vs VOD
- Owncast: Live-first, optional VOD recording
- PeerTube: VOD-first, optional live
- nginx-rtmp: Live pass-through, you handle VOD
Chat and Community
- Owncast: Integrated, moderated, first-class
- PeerTube: Plugin-based, aimed at commenting on VOD
- nginx-rtmp: You build it or skip it
Federation
- Owncast: None. You list in their directory.
- PeerTube: ActivityPub federation to other instances and the fediverse
- nginx-rtmp: None
Transcoding
- Owncast: Doesn’t transcode. You send the bitrate viewers get.
- PeerTube: Full transcoding ladder. Multiple quality tiers.
- nginx-rtmp: Direct passthrough, no transcoding
Operational Burden
- Owncast: Single binary, no database
- PeerTube: Postgres, Redis, ffmpeg workers, object storage
- nginx-rtmp: nginx config, basic HTTP server
Audience Discovery
- Owncast: Owncast directory (small but real)
- PeerTube: Fediverse + PeerTube Index (larger, but requires federation stability)
- nginx-rtmp: You drive traffic manually
The Decision Matrix
You should use Owncast if:
- You’re streaming live events (games, talks, classes)
- You want chat and audience interaction
- You don’t want to run a Postgres database
- You’re the only streamer (single channel)
You should use PeerTube if:
- You’re building a video archive (VOD library)
- You want discoverability via federation
- You’re willing to run the full stack
- You need multiple quality tiers (transcoding)
- You want to be part of the fediverse
You should use nginx-rtmp if:
- You want the absolute minimum
- You’re embedding a stream in a custom app
- You’re happy without chat or fancy features
- You want to transcode/process elsewhere in the pipeline
- You’re building infrastructure, not a consumer platform
The Honest Take
Most people think they want to build a streaming platform and actually just want to go live and have their friends watch. Owncast is the answer. Install it on a $5 VPS, point OBS at it, and you’re done.
If you’re serious about building a video library and want the fediverse to help distribute it, PeerTube is the long-term play. But you’re signing up for Postgres, Redis, object storage, and federation moderation.
nginx-rtmp is for people who know what they’re building. You’re not learning streaming; you’re already competent enough to add the parts you need.
Start with Owncast. You can always upgrade to PeerTube later if you need federation. Or drop down to nginx-rtmp if you want to strip away features. But honestly? Owncast is where most people should stop.