{"id":9614,"date":"2022-09-18T17:05:37","date_gmt":"2022-09-18T15:05:37","guid":{"rendered":"https:\/\/monodes.com\/predaelli\/?p=9614"},"modified":"2022-09-18T17:05:38","modified_gmt":"2022-09-18T15:05:38","slug":"self-hosted-streaming-server-nginx-rtmp-youtube-facebook-live","status":"publish","type":"post","link":"https:\/\/monodes.com\/predaelli\/2022\/09\/18\/self-hosted-streaming-server-nginx-rtmp-youtube-facebook-live\/","title":{"rendered":"Self-Hosted Streaming Server \u2013 NGINX + RTMP \u2013 YouTube + Facebook Live"},"content":{"rendered":"\n<p>Copying this here in case the original goes offline<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"self-hosted-streaming-server-nginx-rtmp-youtube-facebook-live\"><a href=\"https:\/\/dubdub.co.uk\/self-hosted-streaming-server-nginx-rtmp-youtube-facebook-live\/\">Self-Hosted Streaming Server \u2013 NGINX + RTMP \u2013 YouTube + Facebook Live<\/a><\/h1>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"published-by-anthony-on-14th-april-2020\">Published by <a href=\"https:\/\/dubdub.co.uk\/author\/anthony\/\"><strong>Anthony<\/strong><\/a> on 14th April 2020<\/h4>\n\n\n\n<p>I\u2019m writing these notes up, mainly as a reference for me in the future \u2013 but they might just help others looking to do similar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"issue\">Issue:<\/h3>\n\n\n\n<p>During the Coronavirus season, our church, like many, ventured into the world of streaming services, primarily to YouTube but with an eye to multiplexing to both YouTube and Facebook at the same time.<\/p>\n\n\n\n<p>Initially, the plan was to stream from OBS Studio to an intermediate platform, restream.io<\/p>\n\n\n\n<p>We tested with OBS sending the stream to restream and for the first week, everything worked fine.<br \/>Then, the second week about 10 minutes before due to go live restream.io suffered an outage. Fortunately, we were quickly able to flip to sending our stream directly to YouTube and our broadcast worked successfully.<br \/>However, for me, an issue with a shared service leaves a sour taste and so began the journey to discover what would be involved in running our own RTMP server.<\/p>\n\n\n\n<p>It turns out the solution has been relatively straightforward\u2026<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"our-solution\">Our solution:<\/h3>\n\n\n\n<p>Firstly, we signed up for a new VPS with vultr.com<br \/>We opted for a 1 x vCPU, 2GB RAM, High-Frequency machine with 64GB SSD storage, and 2TB monthly bandwidth. Research suggested that RTMP streaming\/multiplexing is not particularly CPU\/RAM intensive and is more about bandwidth consumption.<\/p>\n\n\n\n<p>Once the VPS was provisioned, we selected the Ubuntu 18.04 LTS install image. The machine was duly created and connected via SSH to its IP shown in the portal.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"basic-installation\">Basic Installation:<\/h4>\n\n\n\n<p>Update all packages:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">root@hostname#    sudo apt-get update<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">root@hostname#   sudo apt-get upgrade<\/pre>\n\n\n\n<p>Then install NGINX:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sudo apt-get install nginx<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">sudo apt install libnginx-mod-rtmp<\/pre>\n\n\n\n<p>At this point you can test that NGINX has been installed correctly by testing with your browser:<\/p>\n\n\n\n<p>http:\/\/&lt;your-servers-IP-address&gt;<\/p>\n\n\n\n<p>If successful, you should see the NGINX default page:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/dubdub.co.uk\/wp-content\/uploads\/2020\/04\/welcome-screen-e1450116630667.png?w=910&#038;ssl=1\" alt=\"\" class=\"wp-image-284\"\/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"configuration-for-youtube\">Configuration for YouTube<\/h4>\n\n\n\n<p>Now, on to adding the RTMP config.<\/p>\n\n\n\n<p>Edit the nginx.conf file as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">nano \/etc\/nginx\/nginx.conf<\/pre>\n\n\n\n<p>Add a section at the bottom of the file as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">rtmp {\n     server {\n             listen 1935;\n             chunk_size 4096;\n\n             application live { \n                        live on; \n                        record off; \n                        # Edit and enable line below to push incoming stream to YouTube \n                        # push rtmp:\/\/x.rtmp.youtube.com\/live2\/&lt;your-Stream-Key-Copied-From-YouTube&gt;; \n             }\n      }\n}<\/pre>\n\n\n\n<p>If you aren\u2019t familiar with YouTube Live stream settings the key can be obtained from the following screens:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" height=\"710\" width=\"910\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/dubdub.co.uk\/wp-content\/uploads\/2020\/04\/Screenshot-2020-04-14-at-22.08.11-1024x799.png?resize=910%2C710&#038;ssl=1\" alt=\"\" class=\"wp-image-285\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" height=\"882\" width=\"910\" decoding=\"async\" data-attachment-id=\"286\" data-permalink=\"https:\/\/monodes.com\/predaelli\/?attachment_id=286\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2015\/06\/screenshot-2015-06-10-16-01-521.png?fit=600%2C302&amp;ssl=1\" data-orig-size=\"600,302\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"screenshot-2015-06-10-16-01-521\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2015\/06\/screenshot-2015-06-10-16-01-521.png?fit=300%2C151&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2015\/06\/screenshot-2015-06-10-16-01-521.png?fit=510%2C257&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/dubdub.co.uk\/wp-content\/uploads\/2020\/04\/Screenshot-2020-04-14-at-22.08.43-1024x992.png?resize=910%2C882&#038;ssl=1\" alt=\"\" class=\"wp-image-286\"\/><\/figure>\n\n\n\n<p>Save the nginx.conf file (Ctrl +X ) and start the NGINX service<\/p>\n\n\n\n<p>root@hostname# systemctl restart nginx<\/p>\n\n\n\n<p>Best practice would suggest installing a firewall package on your VPS to minimise its exposure to the Internet.<\/p>\n\n\n\n<p>For Ubuntu 18.04, UFW is perfectly acceptable and well documented. Vultr have a comprehensive document at <a href=\"https:\/\/www.vultr.com\/docs\/configure-ubuntu-firewall-ufw-on-ubuntu-18-04\">https:\/\/www.vultr.com\/docs\/configure-ubuntu-firewall-ufw-on-ubuntu-18-04<\/a><\/p>\n\n\n\n<p>For me, I created 4 simple rules permitting inbound SSH from my home ISP\u2019s fixed IP, and a friend\u2019s IP plus equivalent rules permitting inbound TCP1935 from each of our addresses.<\/p>\n\n\n\n<p>We chose to use OBS to create the stream footage, therefore we had to fill in the stream details within OBS Stream settings:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" height=\"342\" width=\"910\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/dubdub.co.uk\/wp-content\/uploads\/2020\/04\/Screenshot-2020-04-14-at-22.18.05-1024x385.png?resize=910%2C342&#038;ssl=1\" alt=\"\" class=\"wp-image-289\"\/><\/figure>\n\n\n\n<p>If you then click on Start Streaming within OBS, your OBS content should start appearing within your YouTube Studio account (albeit with a 30second delay).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"facebook-live\">Facebook Live<\/h2>\n\n\n\n<p>Facebook made some changes a little while back to only accepts RTMPS streams which NGINX and the RTMP can\u2019t natively support. So, another jigsaw piece needs to be added to the puzzle. Enter stunnel!<\/p>\n\n\n\n<p>Edit your nginx.conf file to include a new push statement.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">push rtmp:\/\/127.0.0.1:1936\/rtmp\/&lt;Facebook-persistent-stream-key&gt;<\/pre>\n\n\n\n<p>restart nginx<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">systemctl restart nginx<\/pre>\n\n\n\n<p>Install Stunnel<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">apt-get install stunnel4 -y<\/pre>\n\n\n\n<p>Edit the stunnel boot configuration as follows (change ENABLE from 0 to 1)<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">nano \/etc\/default\/stunnel4<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">ENABLE=1<\/pre>\n\n\n\n<p>Edit the stunnel config as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">nano \/etc\/stunnel\/stunnel.conf<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">pid = \/var\/run\/stunnel4\/stunnel.pid\noutput = \/var\/log\/stunnel4\/stunnel.log\n\nsetuid = stunnel4\nsetgid = stunnel4\n\n# https:\/\/www.stunnel.org\/faq.html\nsocket = r:TCP_NODELAY=1\nsocket = l:TCP_NODELAY=1\n\ndebug = 4\n\n<\/pre>\n\n\n<p>[fb-live]<\/p>\n\n\n\n<p>client = yes accept = 1936 connect = live-api-s.facebook.com:443 verifyChain = no<\/p>\n\n\n\n<p>Enable Stunnel and start it<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">systemctl enable stunnel4.service<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">systemctl restart stunnel4.service<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><\/h3>\n","protected":false},"excerpt":{"rendered":"<p class=\"excerpt\">Copying this here in case the original goes offline Self-Hosted Streaming Server \u2013 NGINX + RTMP \u2013 YouTube + Facebook Live Published by Anthony on 14th April 2020 I\u2019m writing these notes up, mainly as a reference for me in the future \u2013 but they might just help others looking to do similar. Issue: During&hellip;<\/p>\n<p class=\"more-link-p\"><a class=\"more-link\" href=\"https:\/\/monodes.com\/predaelli\/2022\/09\/18\/self-hosted-streaming-server-nginx-rtmp-youtube-facebook-live\/\">Read more &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[97,72],"tags":[],"class_list":["post-9614","post","type-post","status-publish","format-standard","hentry","category-basilicas-website","category-documentations"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6daft-2v4","jetpack-related-posts":[{"id":7361,"url":"https:\/\/monodes.com\/predaelli\/2020\/06\/25\/how-to-easily-stream-to-two-or-more-locations-the-helping-squad\/","url_meta":{"origin":9614,"position":0},"title":"How to easily stream to two or more locations? \u2013 The Helping Squad","author":"Paolo Redaelli","date":"2020-06-25","format":false,"excerpt":"Streaming to more than one service can be done using an RTMP Server that relays your input to several outputs or using an external service. Services like restream.io offer free and paid options to send your stream to several services at the same time. Source: How to easily stream to\u2026","rel":"","context":"In &quot;Documentations&quot;","block_context":{"text":"Documentations","link":"https:\/\/monodes.com\/predaelli\/category\/documentations\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7993,"url":"https:\/\/monodes.com\/predaelli\/2021\/01\/03\/audio-how-to-transform-live-rtmp-video-to-live-mp3-icecast-stream-stack-overflow\/","url_meta":{"origin":9614,"position":1},"title":"audio &#8211; how to transform live RTMP video to live mp3 Icecast stream &#8211; Stack Overflow","author":"Paolo Redaelli","date":"2021-01-03","format":false,"excerpt":"Source: audio - how to transform live RTMP video to live mp3 Icecast stream - Stack Overflow I have RTMP live stream with video and audio. I can get ogg audio live stream from RTMP and translate it to Icecast2 and get it on my site using JPlayer. Now i\u2026","rel":"","context":"In &quot;Basilica's website&quot;","block_context":{"text":"Basilica's website","link":"https:\/\/monodes.com\/predaelli\/category\/basilicas-website\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7987,"url":"https:\/\/monodes.com\/predaelli\/2021\/01\/03\/broadcast-live-video-live-streaming-html5-webrtc-hls-rtsp-rtmp-wordpress-plugin-wordpress-org\/","url_meta":{"origin":9614,"position":2},"title":"Broadcast Live Video \u2013 Live Streaming : HTML5, WebRTC, HLS, RTSP, RTMP \u2013 WordPress plugin | WordPress.org","author":"Paolo Redaelli","date":"2021-01-03","format":false,"excerpt":"Live video streaming, webcam broadcasting app and player with chat, external apps support, 24\/7 IP cameras, video playlists, video archiving & vod \u2026 I shall someday add this Broadcast Live Video \u2013 Live Streaming : HTML5, WebRTC, HLS, RTSP, RTMP \u2013 WordPress plugin to www.pastoraledesio.it. Or maybe I'll just use\u2026","rel":"","context":"In &quot;Basilica's website&quot;","block_context":{"text":"Basilica's website","link":"https:\/\/monodes.com\/predaelli\/category\/basilicas-website\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":8347,"url":"https:\/\/monodes.com\/predaelli\/2021\/04\/11\/ffmpeg-to-youtube-live\/","url_meta":{"origin":9614,"position":3},"title":"FFMPEG to Youtube Live","author":"Paolo Redaelli","date":"2021-04-11","format":false,"excerpt":"video - FFMPEG to Youtube Live - Stack Overflow After a lot of trial and error the solution below works pretty much perfectly. To make sure it runs 24\/7 wrap it inside a service of some description. This is with an up to date version of FFMPEG to include -stream_loop\u2026","rel":"","context":"In &quot;Documentations&quot;","block_context":{"text":"Documentations","link":"https:\/\/monodes.com\/predaelli\/category\/documentations\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":11351,"url":"https:\/\/monodes.com\/predaelli\/2024\/02\/18\/about-an-nginx-fork\/","url_meta":{"origin":9614,"position":4},"title":"About \u2014 an nginx fork","author":"Paolo Redaelli","date":"2024-02-18","format":false,"excerpt":"Angie, an efficient, powerful, and scalable web server,forked from nginx by some of its former devsto extend the functionality far beyond the original.","rel":"","context":"In &quot;Software&quot;","block_context":{"text":"Software","link":"https:\/\/monodes.com\/predaelli\/category\/software\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1143,"url":"https:\/\/monodes.com\/predaelli\/2016\/03\/14\/how-to-secure-nginx-with-lets-encrypt-on-ubuntu-14-04-digitalocean\/","url_meta":{"origin":9614,"position":5},"title":"How To Secure Nginx with Let&#8217;s Encrypt on Ubuntu 14.04 | DigitalOcean","author":"Paolo Redaelli","date":"2016-03-14","format":false,"excerpt":"Following https:\/\/letsencrypt.readthedocs.org\/en\/latest\/using.html#installation you can obtain a nice SSL certificate for your own webservers; yet for those who likes NGinx like me this guide How To Secure Nginx with Let's Encrypt on Ubuntu 14.04 | DigitalOcean is also useful In this tutorial, we will show you how to use Let's Encrypt\u2026","rel":"","context":"In &quot;Software Libero&quot;","block_context":{"text":"Software Libero","link":"https:\/\/monodes.com\/predaelli\/category\/software\/software-libero\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/9614","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/comments?post=9614"}],"version-history":[{"count":0,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/9614\/revisions"}],"wp:attachment":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/media?parent=9614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/categories?post=9614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/tags?post=9614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}