How to Self-Host PeerTube, a Federated YouTube Alternative

How to Self-Host PeerTube, a Federated YouTube Alternative

Here's an odd fact about our own blog: after more than 600 articles about self-hosted software, we've never written a single one about PeerTube. It's one of the best-known projects in the fediverse, and it answers a question plenty of teams ask quietly: what if our videos didn't have to live on someone else's platform?

So let's fix that. This guide covers what PeerTube actually is, what it costs to run, how to put it on Elestio, and the three things that decide whether it scales: storage, transcoding and updates.

What PeerTube Actually Is

PeerTube is an open-source video platform (AGPL-3.0) built by Framasoft, a French non-profit. On the surface it looks like a familiar video site: channels, playlists, comments, embeds, live streams. The difference is underneath.

It federates. PeerTube speaks ActivityPub, the same protocol as Mastodon. Your instance can follow other PeerTube instances and show their videos, and anyone on Mastodon can follow one of your channels by searching @channelname@videos.example.com. No account on your server needed.

It shares the load. During playback, viewers can exchange video segments with each other over WebRTC, which takes some pressure off your server when a video gets popular.

Nobody else sets the rules. No algorithm deciding who sees what, no ads in front of your onboarding videos, no surprise demonetization or policy change. You choose who can sign up, what gets federated and what gets blocked.

Who uses it? Universities posting lectures, companies hosting internal training they don't want on a public platform, municipalities publishing council meetings, and creators who want a home base they control. If your videos are part of your business, that last point matters more than it sounds.

What It Costs to Run

PeerTube is open source, so there are no license fees. You pay for the server, and how big it needs to be depends almost entirely on transcoding. The official sizing guidance from joinpeertube.org:

ScenarioCPURAMUpload bandwidth
Minimum1 vCore1.5 GB20 Mbit/s
Large instance (about 1,000 concurrent viewers)4 vCores4 GB1 Gbit/s
Transcoding on the same machine8 vCores8 GBDepends on audience

That last row is the one people underestimate. Every upload gets re-encoded into several resolutions, and ffmpeg will happily eat every core you give it. You can avoid paying for an 8-core machine around the clock, and I'll show how below.

Deploying PeerTube on Elestio

PeerTube on Elestio starts at $11/month for the VM, with automated backups, SSL and updates included. The setup:

  1. Pick PeerTube in the catalog, choose a provider and region, and deploy. For a small team, start with 2 vCPU and 4 GB. You can resize later.
  2. Attach your own domain (for example videos.example.com) by following the custom domain guide. Do this before you federate with anyone, because other instances remember your hostname.
  3. Log in with the admin credentials from your service dashboard and change the password.
  4. In the admin configuration, decide on signups. For a company instance, turn open registration off and invite people instead.
  5. Set up SMTP so password resets and notifications actually reach people.

That gets you a working instance. The next three sections are what make it a good one.

Storage: Get Videos Off the VM Disk

Video fills disks fast. A single hour of 1080p, transcoded into a few resolutions, can easily take several gigabytes. Rather than resizing the VM forever, point PeerTube at S3-compatible object storage.

Elestio Object Storage is a good fit here because it charges no egress fees, and video is almost all egress. Standard storage starts at $0.02/GB per month. In the bucket's dashboard:

  • Attach a custom domain such as media.example.com, then set Settings > Visibility to Public read (public access requires a custom domain).
  • Add a CORS rule allowing https://videos.example.com with the GET method, so the player can fetch segments.
  • Create an API key with read and write access.

Then add these variables to your PeerTube .env (on Elestio: Update config in the dashboard):

PEERTUBE_OBJECT_STORAGE_ENABLED=true
PEERTUBE_OBJECT_STORAGE_ENDPOINT=https://storage.elestio.com
PEERTUBE_OBJECT_STORAGE_REGION=auto
PEERTUBE_OBJECT_STORAGE_CREDENTIALS_ACCESS_KEY_ID=your-key-id
PEERTUBE_OBJECT_STORAGE_CREDENTIALS_SECRET_ACCESS_KEY=your-secret
PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BUCKET_NAME=your-bucket
PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_PREFIX=hls/
PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BASE_URL=https://media.example.com
PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BUCKET_NAME=your-bucket
PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_PREFIX=web-videos/
PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BASE_URL=https://media.example.com

One gotcha: PeerTube sends a per-object ACL with every upload by default. Elestio controls access at the bucket level, so those ACLs do nothing useful, and S3-compatible stores without ACL support can reject the uploads. Switch them off. They can't be set to null through environment variables, so create a local.json file in the folder mounted at /config in your docker-compose.yml:

{
  "object_storage": {
    "upload_acl": { "public": null, "private": null }
  }
}

The trade-off: with a public bucket, files of private or internal videos can be read by anyone who has the direct URL. The URLs contain long random IDs and PeerTube never publishes them, but if most of your library is confidential, keep those videos on the VM disk or use a separate instance.

Restart the stack. New uploads now go to the bucket. To move existing videos, run this from the stack's folder under /opt/app/ (replace peertube if your compose file names the service differently):

docker compose exec -u peertube peertube npm run create-move-video-storage-job -- --to-object-storage --all-videos

Transcoding: Rent the CPU Only When You Need It

PeerTube's remote runners move transcoding to another machine. The PeerTube server queues jobs and a runner picks them up, encodes, and sends the results back. That keeps the main VM small, and the runner can be any box with spare CPU, even one you only switch on for upload days.

In PeerTube, go to Administration > Runners > Remote runners, create a registration token, then enable remote transcoding under Administration > Settings > Configuration > VOD. On the runner machine (Node.js 22+ and ffmpeg 4.3+):

sudo npm install -g @peertube/peertube-runner

# Terminal 1: start the runner and pick the job types
peertube-runner server \
  --enable-job vod-web-video-transcoding \
  --enable-job vod-hls-transcoding \
  --enable-job vod-audio-merge-transcoding

# Terminal 2: register it with your instance
peertube-runner register --url https://videos.example.com \
  --registration-token ptrrt-xxxxxxxx --runner-name encoder-1

The PeerTube docs put it bluntly: registered runners can alter video content. Only run them on machines you control.

Updates: This Part Is Not Optional

A federated server talks to strangers by design, and that shows up in the changelog. PeerTube 8.3.1 (September 28) fixed an SSRF in webfinger and video imports. PeerTube 8.3.2 (October 26) patched a critical remote code execution in the sharp image library, triggered by a crafted SVG, plus a high-severity federation flaw that let a remote attacker impersonate actors from another instance.

On Elestio, updates run automatically in the maintenance window you set, and you can trigger one right away with Update now. To confirm which version you're on:

curl -s https://videos.example.com/api/v1/config | jq -r .serverVersion

Troubleshooting

Videos upload but won't play from object storage. Almost always CORS. Make sure the bucket rule allows your exact PeerTube origin (https://, no trailing slash) and the GET method.

Uploads to the bucket fail with access errors. Check that the API key has write access and that the local.json ACL override is in place. Restart after creating it.

You upgraded to 8.3 and skipped the release notes. That release needs a one-time script run while PeerTube is running, after the logs show Migrations finished:

docker compose exec -u peertube peertube node dist/scripts/migrations/peertube-8.3.js

Live streaming doesn't connect. PeerTube receives streams over RTMP on port 1935. Make sure live is enabled in the admin configuration and that TCP 1935 is open in your service's firewall.

Mastodon users can't find your channel. Search the full handle, @channelname@videos.example.com, not the URL. If you changed your domain after launch, older follows point to the old one.

Worth It?

If video is a side detail for you, a public platform is fine. If your videos are training material, lectures, product demos or your actual product, owning the server is cheap insurance: about $11/month to start, storage with no egress bill, and nobody else deciding the rules.

Thanks for reading ❤️ See you in the next one 👋