Peertube¶
Installing dependencies¶
First ensure that we have the basics (by this point, they should already be installed):
sudo -s
apt update && apt install curl sudo unzip vim
Now we install Node.js v12:
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
apt install -y nodejs
And the latest stable version of Yarn:
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
apt update && apt install yarn
Make sure it's installed and check the version with:
yarn --version
Install remaining dependencies:
apt install ffmpeg postgresql postgresql-contrib openssl g++ make redis-server git python3-dev
If you haven't yet installed and enabled the services, do so now:
systemctl enable redis --now
systemctl enable postgresql --now
Setting up Redis¶
The following assumes that the base Redis configuration has already been done as per the Rspamd guide.
Generate a password:
pwgen -Bvsc 64 1
This results in something like the following:
rtVh7zMTFHWm7HWVFXfcgNLYYtjC9scTt4mHVwjnrvqwsJ9qv9CjdwcCWtpqNVbL
Create a configuration file:
nano /etc/redis/redis-funkwhale.conf
Enter the following, using the password you generated above:
include /etc/redis/common.conf
# Listen on localhost
bind 127.0.0.1 ::1
port 6384
unixsocket /var/run/redis-peertube/redis-server.sock
unixsocketperm 700
daemonize yes
supervised systemd
pidfile /var/run/redis-peertube/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-peertube.log
dbfilename dump-peertube.rdb
requirepass <enter password here>
# maxmemory <bytes>
# maxmemory-policy noeviction
# maxmemory-samples 5
Change permissions on the configuration files:
chown -Rc redis:redis /etc/redis
Enable and start the services
systemctl enable redis-server@peertube
systemctl start redis-server@peertube.service
Check that the service are running with:
ps aux | grep redis
or
service redis-server@peertube status
Install Peertube¶
Create the peertube user:
useradd -m -d /var/www/video.example.com -s /bin/bash -p peertube peertube
and create a password:
passwd peertube
Create the database:
sudo -u postgres createuser -P peertube
sudo -u postgres createdb -O peertube -E UTF8 -T template0 peertube_prod
Then enable extensions PeerTube needs:
sudo -u postgres psql -c "CREATE EXTENSION pg_trgm;" peertube_prod
sudo -u postgres psql -c "CREATE EXTENSION unaccent;" peertube_prod
Fetch the latest tagged version of Peertube
VERSION=$(curl -s https://api.github.com/repos/chocobozzz/peertube/releases/latest | grep tag_name | cut -d '"' -f 4) && echo "Latest Peertube version is $VERSION"
Open the peertube directory, create a few required directories
$ cd /var/www/video.example.com && sudo -u peertube mkdir config storage versions && cd versions
Download the latest version of the Peertube client, unzip it and remove the zip
$ sudo -u peertube wget -q "https://github.com/Chocobozzz/PeerTube/releases/download/${VERSION}/peertube-${VERSION}.zip"
$ sudo -u peertube unzip peertube-${VERSION}.zip && sudo -u peertube rm peertube-${VERSION}.zip
Install Peertube:
$ cd ../ && sudo -u peertube ln -s versions/peertube-${VERSION} ./peertube-latest
$ cd ./peertube-latest && sudo -H -u peertube yarn install --production --pure-lockfile
Peertube Configuration¶
Copy example configuration:
cd /var/www/video.example.com && sudo -u peertube cp peertube-latest/config/production.yaml.example config/production.yaml
Then edit the config/production.yaml
file according to your webserver configuration. An example follows (I've omitted the commented lines):
listen:
hostname: 'localhost'
port: 9000
# Correspond to your reverse proxy server_name/listen configuration
webserver:
https: true
hostname: 'video.example.com'
port: 443
rates_limit:
api:
window: 10 seconds
max: 50
login:
window: 5 minutes
max: 15
signup:
window: 5 minutes
max: 2
ask_send_email:
window: 5 minutes
max: 3
trust_proxy:
- 'loopback'
database:
hostname: 'localhost'
port: 5432
suffix: '_prod'
username: 'peertube'
password: '<your-db-password>'
pool:
max: 5
redis:
hostname: 'localhost'
port: 6384
auth: <your-redis-password>
db: 0
smtp:
hostname: mail.example.com
port: 587
username: admin@example.com
password: <your-email-password>
tls: false
disable_starttls: false
ca_file: null
from_address: 'admin@example.com'
email:
body:
signature: "PeerTube"
subject:
prefix: "[PeerTube]"
storage:
tmp: '/var/www/video.example.com/storage/tmp/'
avatars: '/var/www/video.example.com/storage/avatars/'
videos: '/var/www/video.example.com/storage/videos/'
streaming_playlists: '/var/www/video.example.com/storage/streaming-playlists/'
redundancy: '/var/www/video.example.com/storage/redundancy/'
logs: '/var/www/video.example.com/storage/logs/'
previews: '/var/www/video.example.com/storage/previews/'
thumbnails: '/var/www/video.example.com/storage/thumbnails/'
torrents: '/var/www/video.example.com/storage/torrents/'
captions: '/var/www/video.example.com/storage/captions/'
cache: '/var/www/video.example.com/storage/cache/'
plugins: '/var/www/video.example.com/storage/plugins/'
client_overrides: '/var/www/video.example.com/storage/client-overrides/'
log:
level: 'info'
rotation:
enabled : true
maxFileSize: 12MB
maxFiles: 20
anonymizeIP: false
search:
remote_uri:
users: true
anonymous: false
trending:
videos:
interval_days: 7
redundancy:
videos:
check_interval: '1 hour'
-
size: '10GB'
min_lifetime: '48 hours'
strategy: 'most-views'
remote_redundancy:
videos:
accept_from: 'anybody'
csp:
enabled: false
report_only: true
report_uri:
tracker:
enabled: true
private: true
reject_too_many_announces: true
history:
videos:
max_age: -1
views:
videos:
remote:
max_age: -1
plugins:
index:
enabled: true
check_latest_versions_interval: '12 hours'
url: 'https://packages.joinpeertube.org'
###############################################################################
#
# From this point, all the following keys can be overridden by the web interface
# (local-production.json file). If you need to change some values, prefer to
# use the web interface because the configuration will be automatically
# reloaded without any need to restart PeerTube.
#
# /!\ If you already have a local-production.json file, the modification of the
# following keys will have no effect /!\.
#
###############################################################################
...
PeerTube does not support webserver host change. Keep in mind your domain name is definitive after your first PeerTube start.
Certificates¶
Issue the RSA certificates with:
~/.acme.sh/acme.sh --issue --dns dns_cloudns -d video.example.com --keylength 4096 --key-file /etc/letsencrypt/rsa-certs/video.example.com/privkey.pem --ca-file /etc/letsencrypt/rsa-certs/video.example.com/chain.pem --cert-file /etc/letsencrypt/rsa-certs/video.example.com/cert.pem --fullchain-file /etc/letsencrypt/rsa-certs/video.example.com/fullchain.pem --pre-hook "mkdir -p /etc/letsencrypt/rsa-certs/video.example.com" --post-hook "find /etc/letsencrypt/rsa-certs/video.example.com/ -name '*.pem' -type f -exec chmod 600 {} \;" --renew-hook "find /etc/letsencrypt/rsa-certs/video.example.com/ -name '*.pem' -type f -exec chmod 600 {} \; -exec service nginx reload \;"
and ECC certificates with:
~/.acme.sh/acme.sh --issue --dns dns_cloudns -d video.example.com --keylength ec-384 --key-file /etc/letsencrypt/ecc-certs/video.example.com/privkey.pem --ca-file /etc/letsencrypt/ecc-certs/video.example.com/chain.pem --cert-file /etc/letsencrypt/ecc-certs/video.example.com/cert.pem --fullchain-file /etc/letsencrypt/ecc-certs/video.example.com/fullchain.pem --pre-hook "mkdir -p /etc/letsencrypt/ecc-certs/video.example.com" --post-hook "find /etc/letsencrypt/ecc-certs/video.example.com/ -name '*.pem' -type f -exec chmod 600 {} \;" --renew-hook "find /etc/letsencrypt/ecc-certs/video.example.com/ -name '*.pem' -type f -exec chmod 600 {} \; -exec service nginx reload \;"
Nginx¶
Create your server block:
nano /etc/nginx/sites-available/video.example.com
Enter the following:
server {
listen 80;
listen [::]:80;
server_name video.example.com;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name video.example.com;
ssl_certificate /etc/letsencrypt/rsa-certs/video.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/rsa-certs/video.example.com/privkey.pem;
ssl_certificate /etc/letsencrypt/ecc-certs/video.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/ecc-certs/video.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/ecc-certs/video.example.com/chain.pem;
include /etc/nginx/custom-config/ssl.conf;
gzip on;
gzip_types text/css application/javascript;
gzip_vary on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ {
add_header Cache-Control "public, max-age=31536000, immutable";
alias /var/www/video.example.com/peertube-latest/client/dist/$1;
}
location ~ ^/static/(thumbnails|avatars)/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
add_header Cache-Control "public, max-age=7200";
root /var/www/video.example.com/storage;
rewrite ^/static/(thumbnails|avatars)/(.*)$ /$1/$2 break;
try_files $uri /;
}
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 8G;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {
set $peertube_limit_rate 800k;
if ($request_uri ~ -fragmented.mp4$) {
set $peertube_limit_rate 5000k;
}
limit_rate $peertube_limit_rate;
limit_rate_after 5000k;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
access_log off;
}
root /var/www/video.example.com/storage;
rewrite ^/static/webseed/(.*)$ /videos/$1 break;
rewrite ^/static/redundancy/(.*)$ /redundancy/$1 break;
rewrite ^/static/streaming-playlists/(.*)$ /streaming-playlists/$1 break;
try_files $uri /;
}
location /tracker/socket {
proxy_read_timeout 1200s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:9000;
}
location /socket.io {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Activate the configuration file:
ln -s /etc/nginx/sites-available/video.example.com /etc/nginx/sites-enabled/
Reload Nginx:
service nginx reload
Check config for errors:
nginx -t
Apparmor¶
Create an apparmor profile:
nano /etc/apparmor.d/peertube
Enter the following:
#include <tunables/global>
profile peertube flags=(complain, attach_disconnected) {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/ssl_keys>
#include <abstractions/user-tmp>
/proc/version r,
/sys/devices/system/node/ r,
/sys/devices/system/node/node0/meminfo r,
/sys/fs/cgroup/memory/memory.limit_in_bytes r,
/usr/bin/dash mrix,
/usr/bin/ffmpeg mrix,
/usr/bin/ffprobe mrix,
/usr/bin/getconf mrix,
/usr/bin/nice mrix,
/usr/bin/node mrix,
/usr/bin/openssl mrix,
/usr/bin/sh mrix,
owner /var/www/video.example.com.uk/** r,
owner /var/www/video.example.com.uk/.config/** rw,
owner /var/www/video.example.com.uk/.npm/ w,
owner /var/www/video.example.com.uk/.npm/_logs/ w,
owner /var/www/video.example.com.uk/.npm/_logs/* w,
owner /var/www/video.example.com.uk/config/* rw,
owner /var/www/video.example.com.uk/storage/** w,
owner /var/www/video.example.com.uk/storage/**/ rw,
owner /var/www/video.example.com.uk/storage/logs/*.log w,
owner /var/www/video.example.com.uk/versions/*/node_modules/**.node mr,
owner /var/www/video.example.com.uk/versions/peertube-v2.4.0/node_modules/youtube-dl/bin/* w,
}
Reload apparmor:
service apparmor reload
It would be advisable to use for a while under complain mode and keep checking with aa-logprof
on a daily basis. Once you're happy with the config, enforce with aa-enforce
.
It would also be advisable to switch back to complain mode during any upgrade.
Systemd¶
Create a service file:
nano /etc/systemd/system/peertube.service
Add the following:
[Unit]
Description=PeerTube daemon
After=network.target postgresql.service redis-server.service
[Service]
Type=simple
Environment=NODE_ENV=production
Environment=NODE_CONFIG_DIR=/var/www/video.example.com/config
User=peertube
Group=peertube
AppArmorProfile=peertube
ExecStart=/usr/bin/npm start
WorkingDirectory=/var/www/video.example.com/peertube-latest
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=peertube
Restart=always
; Some security directives.
; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
ProtectSystem=full
; Sets up a new /dev mount for the process and only adds API pseudo devices
; like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled
; by default because it may not work on devices like the Raspberry Pi.
PrivateDevices=true
; Ensures that the service process and all its children can never gain new
; privileges through execve().
NoNewPrivileges=true
; This makes /home, /root, and /run/user inaccessible and empty for processes invoked
; by this unit. Make sure that you do not depend on data inside these folders.
ProtectHome=true
; Drops the sys admin capability from the daemon.
CapabilityBoundingSet=~CAP_SYS_ADMIN
[Install]
WantedBy=multi-user.target
Enable and start the Peertube service:
systemctl enable peertube.service --now
The Administrator Password¶
The administrator password is automatically generated and can be found in the logs. You can set another password with:
cd /var/www/video.example.com/peertube-latest && NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run reset-password -- -u root
Alternatively you can set the environment variable PT_INITIAL_ROOT_PASSWORD, to your own administrator password, although it must be 6 characters or more.