Peertube Remote storage (S3)

It works, but I think it’s not yet optimal.

I ran a live for several hours and checking what’s happening here and there. The live was normal without saving for playback.

I’ve noticed that .ts files stay in the bucket for a minute before being deleted. In my current test, there is always the last 15 .ts files in the bucket which is the data received in the last minute. Not caching .ts files at all generates unnecessary traffic between the bucket and the reverse proxy. But still not figured out how to create two different caches…
0.m3u8 and segments-sha256.json will be modified in the bucket at each new .ts, so if we want to cache it, not more than three or four secends max (but not tested)!
master.m3u8 will never change after the live strated, we can cache it.

Still working on it…

Cache won’t work with the config of my previous posts.
Here is the correction :

proxy_cache_path /var/cache/s3 levels=1:2 keys_zone=CACHE-S3:100m inactive=48h max_size=10G;
proxy_cache_path /var/cache/s3-ts levels=1:2 keys_zone=CACHE-S3-TS:10m inactive=60s max_size=1G;

server {
  listen 80;
  server_name peertube.tld;
  root /var/www/html;
  location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl;
  http2 on;
  server_name peertube.tld;

  access_log /var/log/nginx/medias.access.log; # reduce I/0 with buffer=10m flush=5m
  error_log  /var/log/nginx/medias.error.log;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_tickets off;

  ssl_certificate     /etc/letsencrypt/live/peertube.tld/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/peertube.tld/privkey.pem;

  root /var/www/html;

  keepalive_timeout 30;

  location = / {
    index index.html;
  }

  location / {
    try_files $uri @s3;
  }

  location ~ \.ts$ {
    try_files $uri @s3-ts;
  }

  location ~ \.(json|m3u8)$ {
    try_files $uri @s3_nocache;
  }

  set $s3_backend 'https://my-bucket.s3.bhs.perf.cloud.ovh.net';

  location @s3 {
    limit_except GET OPTIONS {
        deny all;
    }

    resolver 1.1.1.1 8.8.8.8 208.67.222.222 208.67.220.220;
    proxy_set_header Host my-bucket.s3.bhs.perf.cloud.ovh.net;
    proxy_set_header Connection '';
    proxy_set_header Authorization '';
    proxy_set_header Range $slice_range;
    proxy_hide_header Set-Cookie;
    proxy_hide_header 'Access-Control-Allow-Origin';
    proxy_hide_header 'Access-Control-Allow-Methods';
    proxy_hide_header 'Access-Control-Allow-Headers';
    proxy_hide_header x-amz-id-2;
    proxy_hide_header x-amz-request-id;
    proxy_hide_header x-amz-meta-server-side-encryption;
    proxy_hide_header x-amz-server-side-encryption;
    proxy_hide_header x-amz-bucket-region;
    proxy_hide_header x-amzn-requestid;
    proxy_ignore_headers Set-Cookie;
    proxy_pass $s3_backend$uri;
    proxy_intercept_errors off;

    proxy_cache CACHE-S3;
    proxy_cache_valid 200 206 48h;     # <---- Needs 206 too
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    slice              1m;
    proxy_cache_key    $host$uri$is_args$args$slice_range;
    proxy_http_version 1.1;

    expires 1y;
    add_header Cache-Control public;
    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 X-Cache-Status $upstream_cache_status;
    add_header X-Content-Type-Options nosniff;
    add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
  }

location @s3-ts {
    limit_except GET OPTIONS {
        deny all;
    }

    resolver 1.1.1.1 8.8.8.8 208.67.222.222 208.67.220.220;
    proxy_set_header Host my-bucket.s3.bhs.perf.cloud.ovh.net;
    proxy_set_header Connection '';
    proxy_set_header Authorization '';
    proxy_set_header Range $slice_range;
    proxy_hide_header Set-Cookie;
    proxy_hide_header 'Access-Control-Allow-Origin';
    proxy_hide_header 'Access-Control-Allow-Methods';
    proxy_hide_header 'Access-Control-Allow-Headers';
    proxy_hide_header x-amz-id-2;
    proxy_hide_header x-amz-request-id;
    proxy_hide_header x-amz-meta-server-side-encryption;
    proxy_hide_header x-amz-server-side-encryption;
    proxy_hide_header x-amz-bucket-region;
    proxy_hide_header x-amzn-requestid;
    proxy_ignore_headers Set-Cookie;
    proxy_pass $s3_backend$uri;
    proxy_intercept_errors off;

    proxy_cache CACHE-S3-TS;
    proxy_cache_valid 200 206 2m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    slice              1m;
    proxy_cache_key    $host$uri$is_args$args$slice_range;
    proxy_http_version 1.1;

    expires 1y;
    add_header Cache-Control public;
    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 X-Cache-Status $upstream_cache_status;
    add_header X-Content-Type-Options nosniff;
    add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
  }

  location @s3_nocache {
    limit_except GET OPTIONS {
        deny all;
    }

    resolver 1.1.1.1 8.8.8.8 208.67.222.222 208.67.220.220;
    proxy_set_header Host my-bucket.s3.bhs.perf.cloud.ovh.net;
    proxy_set_header Connection '';
    proxy_set_header Authorization '';
    proxy_set_header Range $http_range;
    proxy_hide_header Set-Cookie;
    proxy_hide_header 'Access-Control-Allow-Origin';
    proxy_hide_header 'Access-Control-Allow-Methods';
    proxy_hide_header 'Access-Control-Allow-Headers';
    proxy_hide_header x-amz-id-2;
    proxy_hide_header x-amz-request-id;
    proxy_hide_header x-amz-meta-server-side-encryption;
    proxy_hide_header x-amz-server-side-encryption;
    proxy_hide_header x-amz-bucket-region;
    proxy_hide_header x-amzn-requestid;
    proxy_ignore_headers Set-Cookie;
    proxy_pass $s3_backend$uri;
    proxy_intercept_errors off;

    expires 0;
    proxy_cache off;

    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 X-Cache-Status $upstream_cache_status;
    add_header X-Content-Type-Options nosniff;
    add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
  }
}