Setting up Owncast on a Debian VM using Linode

I set up Owncast on a fresh Debian Linode VM. These are my notes.

Sometime in 2017 I decided that I didn't want a Facebook account any more.1. I had also planned on closing my twitter account, but I drug my feet, and by the time 2019 happened, I had started checking it regularly again. I'm mostly over that now2, which is why I set up Fediverse Monster. And, like anything else, once I got a taste of decentralized media, I wanted more.

I sometimes pretend to be a video game streamer on Twitch. I used Twitch because everyone uses Twitch. I just never used Twitch a lot because Twitch comes with its own baggage that I'm not really interested in. Once I found out about Owncast, though, I started to think that I could host my own streaming site. So I went to Linode, created a new VM and got it installed. The documentation was lacking in a few places, and seems to really want you to curl some script and pipe it into bash, which always makes me nervous, so I did it by hand, and then I wrote up my notes so that I can remember what I did six months from now.

Disclaimer - this is just a rough guide to the steps I took to set up an Owncast server. They work for me™, but they may not work for you. This also assumes that you already have a domain that you want to use, or you know how to get one and can do basic setup. It also assumes that you have basic sysadmin skills and know how to move around a command line and edit config files.

Set up a new VM

I went with Linode because that's who I'm familiar with. I chose a low-end VM because I don't really expect that my stream is going to popular enough that I'm going to need anything too outrageous to start with. I chose Debian 11, set my root password and installed all updates. Once that was done, I added a non-root account

# useradd someusername

I updated /etc/hostname to give it the name of my server, backlog.tv, and rebooted.

Installing prerequisite software

Once the server was rebooted, I installed a firewall (ufw) a web server (nginx), and ffmpeg

# apt install ufw ffmpeg nginx

Downloading Owncast

We need to figure out a place for Owncast to live. We need to put it in a place that's accessible by the user account it's going to run as (more on this later), and also where nginx can get to it for serving up a reverse proxy (also more on this later). I put it in a subfolder of the /var/www folder and gave it appropriate permissions

mkdir /var/www/backlogtv

Downloading Owncast is straightforward. Go to the Owncast Github page and download the latest release for your particular use case. Mine is the Linux 64bit release. To save some time, you might want to put it in the directory where it's going to live. I used wget.

cd /var/www/backlogtv
wget https://github.com/owncast/owncast/releases/download/v0.0.13/owncast-0.0.13-linux-64bit.zip

Unzip the archive and you'll have a few files and directories to play with. You probably want to delete the .zip archive or move it somehwere else if you want to keep it for some reason.

cd /var/www/backlogtv
unzip owncast-0.0.13-linux-64bit.zip
rm owncast-0.0.13-linux-64bit.zip

Make the www-data user the owner of this folder and everything in it3,4

chown -R www-data:www-data /var/www/backlogtv/*

Testing

Since I ultimately want to run Owncast as the www-data user, I can use su to switch from root to www-data. Since the www-data account is special, it doesn't have a shell, so you normally can't use it to log in, that's easily remedied with the -s $SHELL argument

#su www-data -s $SHELL
$cd /var/www/backlogtv
$./owncast

If all goes well, you'll see some console output and visiting http://backlog.tv:8080 should show the default Owncast page. If you do, it's time to move on to the next step.

Setting up nginx

Having Owncast running on port 8080 is okay, but it's not ideal, and we want users to be able to find us by going to https://backlog.tv5, and to do that, we're going to need to set up a reverse proxy, but to do that, we're going to need an SSL certificate6.

This process is going to vary depending on who you get the certificate from. I used Sectigo from Namecheap because they were cheap, but you use whichever vendor you're comfortable with.

Generate the private key and certificate signing request, substituting your domain for yourdomain. Certificates can only be issued for a maximum of about a year. So we'll have to do this again in a year or so.

openssl req -new -newkey rsa:4096 -days 365 -nodes -keyout yourdomain.key -out yourdomain.csr

Fill out the required information, keep yourdomain.key private and submit yourdomain.csr to your certificate vendor. Complete the steps and you will get back some files. In my case, I got a ca-bundle file and a crt file. These are text files, so you can open them up in your favorite text editor, copy the contents, and paste them into your terminal window if you wanted to. I wanted to.

Now we have a bunch of certificate files: yourdomain.key, yourdomain.csr, yourdomain.ca-bundle, and yourdomain.crt. We need to concatenate the ca-bundle and the crt file into one6

cat yourdomain.crt yourdomain.ca-bundle >> yourdomain-chain.crt

Then you copy yourdomain-chain.crt to /etc/ssl/certs and yourdomain.key to /etc/ssl/private.

Before we get started on the actual nginx config, it's worth noting that nginx is very powerful software. You can do a lot with it, and it's also really easy to screw something up in just the right way where it looks like everything is working, but it's broken in subtle ways. There are entire books written on nginx, and a lot of tutorials on the web that all seem to contradict each other. Reading enough documentation to synthesize some semblance of understanding of the basics of how nginx works is your homework.

The first thing that we need to do is to remove the symbolic link to the default website. We don't need it.

#rm /etc/nginx/sites-enabled/default

Next we need to create a configuration file that tells nginx how our website works. You can call it whatever you want, but I find that using the domain name makes things easier

/etc/nginx/sites-available/backlog.tv
map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

This block defines some variables that we need for the reverse proxy7

server {
        listen 80;
        server_name backlog.tv www.backlog.tv;
        return 301 https://$host$request_uri;
}

This block sets up a web server listening on port 80 that redirects all requests to port 443 (it uses HTTP 301 - Moved Permanently to tell the browser to try to fetch the resource using HTTPS instead)

server {
        # SSL configuration
        #
        listen 443 ssl;
        # listen [::]:443 ssl default_server;
        ssl_certificate /etc/ssl/certs/yourdomain-chain.crt;
        ssl_certificate_key /etc/ssl/private/yourdomain.key;

This block sets up SSL. It's important to point ssl_certificate and ssl_certificate_key to where your certificate and key files live

   root /var/www/backlogtv/webroot;

This tells nginx where the webroot folder is. This came bundled with the Owncast download that we unzipped earlier. You can put this wherever you want, but the folder has to be accessible to the www-data user.

        # Add index.php to the list if you are using PHP
        index index.html index.htm;
        server_name backlog.tv www.backlog.tv;

        autoindex on;

This tells nginx what files to treat as index files (or the default files) if they go to a subdirectory. It also has the name(s) of the server that people will type into their web browser (and some people continue typing 'www' in front of everything, so I added that, too), and it tells nginx to present a list of files if the user gets to a directory without an index file in it. I should probably rearrange these directives some day.

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                # try_files $uri $uri/ =404;

This is all commented out. I'm noting this because I copied the default site config and modified it. I spent way too long trying to get it working and it turned out that this block got in my way. This is my reminder to comment out or delete the 'try_files' line next time

   proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_pass http://127.0.0.1:8080;

       }
        
}

This is where the magic of the reverse proxy happens. The important thing is to pay attention to the proxy_pass directive. That's where nginx is going to forward requests. If you have Owncast listening on some port other than 8080, you'll want to change that

Now we tell nginx that our website is ready to be hosted by making a symbolic link in the sites-enabled directory to the configuration file in the sites-available directory.

# ln -s /etc/nginx/sites-available/yoursite /etc/nginx/sites-enabled/yoursite

Then we run service nginx start to start the server (or service nginx restart if it's already running). If there are no errors, then the web server is running and we can test it by visiting https://YourDomain. If you get an error that there's a bad gateway, then either your reverse proxy isn't set up right, or the Owncast executable isn't running.

Setting up the service

To make connecting easier, it makes some sense to set up Owncast to run as a service that's automatically started when the server boots up. To do this, we can create a systemd unit file8

/etc/systemd/system/owncast.service
[Unit]
Description=Owncast

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/yoursite/
ExecStart=/var/www/yoursite/owncast
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

The important bits are that you want WorkingDirectory to be the directory where Owncast is located, ExecStart is the full path to the owncast executable file, and User is the user that we want to run the program as. In this case it's the www-user user.

Now we can service owncast start and see if Owncast actually starts. If it does, we can enable the service by using systemctl enable owncast, and we can reboot the server to see if everything starts up properly. If it did, we're done (we're not done).

Next Steps

Owncast is installed and running, but there's still more to do that are beyond the scope of this document, including getting your instance configured. At minimum I'd recommend the following.

  • Change the stream key/admin password
  • Figure out a backup strategy
  • Configure ufw to allow traffic to ports 22, 80, 443, 1935, and deny everything else. Move some of the ports around if you want (maybe don't move port 80 and 443, though). Bonus points to only allow SSH logins from certain IP addresses.
  • Enable SSH key-based authentication and turn off password authentication. Also disallow root from logging in via SSH (add your nonroot user to the sudoers file first)
  • Install and configure fail2ban

Footnotes

  1. The reasons don't really matter, but they're basically what you'd expect
  2. For the reasons you would expect.
  3. Be extra careful running recursive chown commands with wildcards as root, unless you like blowing away your whole server and starting over from scratch. Then go nuts.
  4. Or you could just su fro the root user to the www-data user (using something like #su www-data -s $SHELL, and then use that account to unzip the file contents. It's a lot safer, but significantly less exciting
  5. You may not need HTTPS for your use case, but it's required if I wanted to have the site listed in the Owncast directory, so I'm adding it
  6. I'm not using Let's Encrypt for reasons that don't need going into here. I'm doing this by hand.
  7. This is mostly cribbed from the Namecheap docs
  8. This is mostly taken from the nginx docs
  9. This is based on the Owncast docs


Read more articles · Go back to the homepage