Owncast is an open source, self-hosted live streaming server with built-in chat. You send it an RTMP stream from OBS Studio, it transcodes the video with FFmpeg into HLS, and viewers watch it in a web page you control, without Twitch or YouTube in between.

In this tutorial you will install Owncast on Ubuntu 24.04, run it as an unprivileged systemd service, put it behind Nginx with a Let's Encrypt certificate, and send your first stream from OBS Studio.

Prerequisites

To follow this tutorial, you will need:

  • A server running Ubuntu 24.04, for example a CubePath VPS, with at least 2 vCPUs and 2 GB of RAM. Transcoding is CPU intensive: plan roughly one extra vCPU for every additional video quality you offer.
  • A non-root user with sudo privileges and UFW enabled.
  • A domain name with an A record pointing to your server's IP address. This tutorial uses your_domain as a placeholder.
  • OBS Studio installed on the computer you will stream from.

Step 1 - Installing FFmpeg and Nginx

Owncast uses FFmpeg to transcode the incoming stream, and Nginx will terminate HTTPS in front of it. Both are in the Ubuntu repositories:

sudo apt update
sudo apt install -y ffmpeg nginx unzip

Confirm that FFmpeg is available:

ffmpeg -version | head -n 1
ffmpeg version 6.1.1-3ubuntu5 Copyright (c) 2000-2023 the FFmpeg developers

Step 2 - Downloading Owncast

Owncast ships as a single binary. Create a dedicated system user that owns the installation, so the service never runs as root:

sudo useradd --system --create-home --home-dir /opt/owncast --shell /usr/sbin/nologin owncast

Look up the download URL of the latest release from the GitHub API and download it:

cd /tmp
OWNCAST_URL=$(curl -s https://api.github.com/repos/owncast/owncast/releases/latest | grep -o 'https://[^"]*linux-64bit.zip')
echo "$OWNCAST_URL"
curl -LO "$OWNCAST_URL"

Extract the archive into /opt/owncast and give ownership to the owncast user:

sudo unzip -o /tmp/owncast-*-linux-64bit.zip -d /opt/owncast
sudo chown -R owncast:owncast /opt/owncast

Check that the binary is in place, owned by owncast and executable (-rwxr-xr-x):

ls -l /opt/owncast/owncast

Step 3 - Running Owncast as a systemd service

Owncast listens on port 8080 for the web interface and on port 1935 for RTMP. Because Nginx will serve the web interface publicly, bind the web server to 127.0.0.1 so port 8080 is never exposed directly.

Create the unit file:

sudo nano /etc/systemd/system/owncast.service

Add the following content:

[Unit]
Description=Owncast live streaming server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=owncast
Group=owncast
WorkingDirectory=/opt/owncast
ExecStart=/opt/owncast/owncast -webserverip 127.0.0.1 -webserverport 8080 -rtmpport 1935
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Owncast stores its database and HLS segments in data/ inside the working directory, which is why WorkingDirectory points to /opt/owncast.

Reload systemd and start the service:

sudo systemctl daemon-reload
sudo systemctl enable --now owncast

Check that it is running and listening on the expected addresses:

sudo systemctl status owncast --no-pager
sudo ss -tlnp | grep owncast
LISTEN 0 4096   127.0.0.1:8080   0.0.0.0:*  users:(("owncast",pid=2211,fd=9))
LISTEN 0 4096           *:1935         *:*  users:(("owncast",pid=2211,fd=8))

You can also query the status API locally:

curl -s http://127.0.0.1:8080/api/status

The JSON response includes "online":false because nothing is streaming yet.

Step 4 - Configuring Nginx and HTTPS

Create an Nginx server block that proxies requests to Owncast. The Upgrade and Connection headers are required for the chat, which uses WebSockets.

sudo nano /etc/nginx/sites-available/owncast
server {
    listen 80;
    listen [::]:80;
    server_name your_domain;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Enable the site, test the configuration and reload Nginx:

sudo ln -s /etc/nginx/sites-available/owncast /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Open the firewall for HTTP, HTTPS and RTMP. Port 8080 stays closed:

sudo ufw allow 'Nginx Full'
sudo ufw allow 1935/tcp
sudo ufw status

Now request a Let's Encrypt certificate. Certbot's Nginx plugin adds the listen 443 ssl block and the HTTP to HTTPS redirect for you:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your_domain

When Certbot finishes, open https://your_domain in a browser. You should see the Owncast page with an offline message.

Step 5 - Securing the admin panel

The admin panel is at https://your_domain/admin. On a fresh install, the username is admin and both the admin password and the default stream key are abc123. Change them before you do anything else:

  1. Log in to https://your_domain/admin with admin / abc123.
  2. Go to Configuration > Server Setup and set a strong admin password.
  3. In the Stream Keys section of the same page, delete the default key and create a new, long random one. You can generate one on the server with openssl rand -hex 24.
  4. Go to Configuration > General, set the Server URL to https://your_domain, and fill in the server name, description and logo.

Step 6 - Streaming from OBS Studio

In OBS Studio, open Settings > Stream and set:

  • Service: Custom...
  • Server: rtmp://your_domain/live
  • Stream Key: the key you created in the previous step

Then open Settings > Output, switch Output Mode to Advanced, and use settings that Owncast can transcode efficiently:

SettingRecommended value
Encoderx264, or NVENC H.264 if you have an NVIDIA GPU
Rate controlCBR
Bitrate2500-6000 Kbps
Keyframe interval2 s
Resolution1280x720 or 1920x1080
FPS30

Click Start Streaming. Within a few seconds the service log should show the incoming stream:

sudo journalctl -u owncast -f

Reload https://your_domain: the player starts after a short buffer and curl -s http://127.0.0.1:8080/api/status now reports "online":true.

Step 7 - Tuning video quality and latency

In the admin panel, open Video to control the qualities (output variants) that Owncast produces. Each variant is a separate FFmpeg encode, so it costs CPU.

A sensible starting point for a 2 to 4 vCPU server is two variants:

VariantResolutionVideo bitrateFramerate
High1280x7203000 Kbps30
Low854x480800 Kbps30

If the CPU cannot keep up, enable video passthrough on the high variant. Owncast then relays the video from OBS without re-encoding it, at the cost of sending your OBS bitrate to every viewer on that quality.

The latency setting on the same page trades delay for stability: lower latency means smaller buffers and more rebuffering on poor connections. Leave the default until you have measured your viewers' experience.

Watch CPU usage while you stream to check that the server has headroom:

top -o %CPU

If ffmpeg processes stay close to 100% of all cores, remove a variant or lower its resolution.

Step 8 - Offloading video to S3-compatible storage (optional)

By default, Owncast serves the HLS video segments itself, so every viewer's bandwidth goes through your server. For larger audiences, Owncast can upload the segments to S3-compatible object storage (AWS S3, Wasabi, Backblaze B2, MinIO) and let viewers download them from there or from a CDN in front of the bucket.

In the admin panel, go to Configuration > Storage (called S3 in some versions), enable it, and fill in the endpoint, access key, secret, bucket and region of your provider. For MinIO and some other providers, enable the path-style option. Start a test stream and confirm that new objects appear in the bucket.

Troubleshooting

OBS says "Failed to connect to server". Check that Owncast is listening on RTMP and that the port is open:

sudo ss -tlnp | grep 1935
sudo ufw status | grep 1935

If both look correct, check the key: a wrong stream key is logged by Owncast and the connection is rejected.

sudo journalctl -u owncast -n 50 --no-pager

The page loads but chat does not connect. The WebSocket headers are missing from the Nginx configuration. Compare your server block with the one in Step 4, then run sudo nginx -t && sudo systemctl reload nginx.

Viewers see constant buffering. The server is usually short on CPU or upload bandwidth. Check top during a stream, reduce the number of variants, or enable passthrough as described in Step 7.

The service does not start after an upgrade. Look at the last lines of the journal. Permission errors mean files in /opt/owncast are not owned by the owncast user: run sudo chown -R owncast:owncast /opt/owncast and restart the service.

Conclusion

You now have Owncast running as a systemd service on Ubuntu 24.04, served over HTTPS by Nginx, and receiving streams from OBS Studio. Your stream, chat and viewer data stay on infrastructure you control.

From here you can customize the page appearance and chat rules in the admin panel, put a CDN in front of the S3 bucket to handle large audiences, and back up /opt/owncast/data regularly so you keep your configuration and chat history.