ruby on rails http://tech.oeru.org/ en Installing Mastodon with Docker-Compose on Ubuntu 16.04 http://tech.oeru.org/installing-mastodon-docker-compose-ubuntu-1604 <span class="field field--name-title field--type-string field--label-hidden">Installing Mastodon with Docker-Compose on Ubuntu 16.04</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--mastodon"> <span class="field__item-wrapper"><a href="/taxonomy/term/31" hreflang="en">mastodon</a></span> </div> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--_604"> <span class="field__item-wrapper"><a href="/taxonomy/term/27" hreflang="en">16.04</a></span> </div> <div class="field__item field__item--nginx"> <span class="field__item-wrapper"><a href="/taxonomy/term/30" hreflang="en">nginx</a></span> </div> <div class="field__item field__item--lets-encrypt"> <span class="field__item-wrapper"><a href="/taxonomy/term/17" hreflang="en">let&#039;s encrypt</a></span> </div> <div class="field__item field__item--ruby-on-rails"> <span class="field__item-wrapper"><a href="/taxonomy/term/22" hreflang="en">ruby on rails</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/25" hreflang="en">docker compose</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" class="username">dave</a></span> <span class="field field--name-created field--type-created field--label-hidden">Fri 02/06/2017 - 15:02</span> <div class="field field-node--field-image field-name-field-image field-type-image field-label-hidden has-multiple"> <figure class="field-type-image__figure image-count-1"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-06/mastodon_userscreen2.png?itok=pyxlNcbL" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;The Mastodon webapp, showing a federated timeline (right panel - and yes, Mastodon is popular in Japan)&quot;}" role="button" title="The Mastodon webapp, showing a federated timeline (right panel - and yes, Mastodon is popular in Japan)" data-colorbox-gallery="gallery-field_image-s7IGI4s7mgA" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The Mastodon webapp, showing a federated timeline (right panel - and yes, Mastodon is popular in Japan)&quot;}"><img src="/sites/default/files/styles/medium/public/2017-06/mastodon_userscreen2.png?itok=P5YMy5Yf" width="220" height="141" alt="The Mastodon webapp, showing a federated timeline (right panel - and yes, Mastodon is popular in Japan)" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-2"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-06/mastodon_user_settings.png?itok=1CcpPZP0" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Mastodon user settings&quot;}" role="button" title="Mastodon user settings" data-colorbox-gallery="gallery-field_image-s7IGI4s7mgA" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Mastodon user settings&quot;}"><img src="/sites/default/files/styles/medium/public/2017-06/mastodon_user_settings.png?itok=w2QahwB4" width="220" height="141" alt="Mastodon user settings" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-3"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-06/mastodon_admin_settings.png?itok=cRSdDIKu" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Mastodon administrator settings&quot;}" role="button" title="Mastodon administrator settings" data-colorbox-gallery="gallery-field_image-s7IGI4s7mgA" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Mastodon administrator settings&quot;}"><img src="/sites/default/files/styles/medium/public/2017-06/mastodon_admin_settings.png?itok=jrYKu26i" width="220" height="141" alt="Mastodon administrator settings" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-4"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-06/mastodon_social_info.png?itok=_LbvxOiq" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Info on a heavily used Mastodon node (Mastodon.Social, the reference node)&quot;}" role="button" title="Info on a heavily used Mastodon node (Mastodon.Social, the reference node)" data-colorbox-gallery="gallery-field_image-s7IGI4s7mgA" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Info on a heavily used Mastodon node (Mastodon.Social, the reference node)&quot;}"><img src="/sites/default/files/styles/medium/public/2017-06/mastodon_social_info.png?itok=N2x5gmY2" width="220" height="143" alt="Info on a heavily used Mastodon node (Mastodon.Social, the reference node)" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-5"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-06/mastodon_nzoss_info.png?itok=mT-9glYy" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Info on a more humble node (the NZ Open Source Society node I run)&quot;}" role="button" title="Info on a more humble node (the NZ Open Source Society node I run)" data-colorbox-gallery="gallery-field_image-s7IGI4s7mgA" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Info on a more humble node (the NZ Open Source Society node I run)&quot;}"><img src="/sites/default/files/styles/medium/public/2017-06/mastodon_nzoss_info.png?itok=93rckq5L" width="220" height="141" alt="Info on a more humble node (the NZ Open Source Society node I run)" loading="lazy" class="image-style-medium" /> </a> </div> </figure> </div> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>Not long ago, <a href="https://github.com/tootsuite/mastodon" title="Scroll down to see the write up - the source code is front and centre.">Mastodon</a>, an open source, <em>federated</em> alternative to the proprietary network-effect wunderkind, Twitter, came out of no where. Actually, it came out of an insane amount of work done by free and open source powerhouse Eugen Rochko aka <a href="https://github.com/Gargron">Gargron</a> and a small elite developer community, and many predecessors who are part of the <a href="https://www.coactivate.org/projects/disintermedia/blog/2017/04/01/a-brief-history-of-the-gnu-social-fediverse-and-the-federation/">GNU Social Fediverse</a> (kudos to <a href="http://www.coactivate.org/people/strypey/profile">Danyl Strype</a> for compiling that excellent history).</p> <p>Mastodon, unlike Twitter, is entirely community driven - there are no ads, there are no privacy threats, there are no corporate Terms and Conditions to blindly "I Accept". And your Mastodon "persona" can be on a server you control (or that is controlled by someone you trust). Despite being distributed, you're still part of a global network, but one made resilient by its federated, decoupled nature.</p> <p>Instead of "Tweeting" in 140 characters like on Twitter, your "Toots" are limited to 500 characters (a lot more information can usefully be passed). You can follow people (and they you) by learning their handle - which looks like an email. I've got a couple Mastodon accounts, but my main one right now is <a href="mailto:lightweight@mastodon.social">lightweight@mastodon.social</a> (I set it up quite a while back, before I set up my first couple Mastodon servers). Actually, Mastodon's biggest problem (in my opinion) right now is that you can't easily migrate your "main" persona from one server to another without losing a lot of its value (historical toots, followers, those you follow, etc.). You can migrate some things, like those you're following, and any users you've "blocked" but it's still fairly rudimentary.</p> <p>Mastodon includes a nice web interface which will look somewhat familiar to anyone who's used Twitter's "Tweetdeck" web application. Similarly, the GNU Social community has rallied to provide at least 2 separate open source mobile apps (I run <a href="https://play.google.com/store/apps/details?id=com.keylesspalace.tusky&amp;hl=en">Tusky</a> on my <a href="https://lineageos.org" title="Open Source Android - the way it was supposed to be before the OEMs messed it up.">LineageOS</a> powered phone at the moment) - I think there're some for iOS, too, although Apple's not as amenable to open source apps. </p> <p>There's a useful <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md">Mastodon FAQ</a>.</p> <h2>Running with the Mastodon Herd</h2> <p>The way I implement a complex Ruby on Rails app like Mastodon is to do as much as possible to keep it at arms length (and stop it from getting anything gooey on my virtual machine). To achieve that comforting isolation, I employ Docker Compose on Ubuntu Linux 16.04. See our <a href="/docker-compose-better-way-deploy-rocketchat-wekan-and-mongodb">Docker Compose article</a> on how to install it (and its dependencies, like Docker itself).</p> <p>Once you've got Docker Compose running, you can do what I did. </p> <p>A couple notes:</p> <ul><li>I have an unprivileged user on my server, "ubuntu". You can use any unprivileged users - I'd encourage you to use sudo rather than login as root.</li> <li>I use "vim" as my terminal-based text editor below. I think it's a great tool, but it does have a learning curve. If you're daunted (no shame in that), I recommend using "nano" instead - it'll probably installed on most Ubuntu 16.04 instances. If someone suggests you use "emacs" instead, they're jerkin' yer chain (I used emacs for over a decade, I know what I'm talking about).</li> <li>make sure you have the "git" VCS system installed... <code>sudo apt install git</code> should do it.</li> <li>you'll need nginx installed, too... <code>sudo apt install nginx-full</code> will do that for you.</li> </ul><p>After logging into my server (via SSH remotely) as the ubuntu user (you might have different non-privileged user name, that's ok), I did the following (to avoid permissions problems later on, we'll create a "mastodon" group and user with the id 991, used by the Mastodon app by default, on the hosting platform):</p> <p><code>groupadd -g 991 mastodon<br /> useradd -u 991 -g 991 -c "Mastodon User" -s /usr/bin/nologin -d /home/data/mastodon mastodon</code><br /><code>sudo mkdir -p /home/docker /home/data/mastodon<br /> sudo chown -R ubuntu:ubuntu /home/docker<br /> sudo chown -R mastodon:mastodon /home/data/mastodon</code><br /><code>cd /home/docker<br /> git clone https://github.com/tootsuite/mastodon.git docker-mastodon<br /> cd docker-mastodon</code></p> <p>What you then need to do is ensure you're using the current "tagged" release (it'll make your life easiest). You can find out what tags are available:</p> <p><code>git tag -l </code></p> <p>At present, the latest tag is "v1.4.7" - to use it do this:</p> <p><code>git checkout tags/v1.4.7</code></p> <p>Obviously, replace this with the most recent tag (note, you might have to look through the whole list to find it!). Then you're using the specific collection of files corresponding to the v1.4.7 tagged release. We can carry on...</p> <p><code>cp .env.production.sample .env.production<br /> vim .env.production</code></p> <p>Edit this file to look like the .env.production sample below, but replacing the [tokens] with your values. Then run this:</p> <p><code>vim docker-compose.yml</code></p> <p>Edit this file to look like docker-compose.yml below.</p> <p><code>docker-compose run --rm web rake secret </code></p> <p>Run this last command 3 times - to get 3 secrets - long random strings - for .env.production! Copy and paste your 3 secrets into your .env.production file with your preferred editor as shown below.</p> <p><code>docker-compose build<br /> docker-compose up</code></p> <p>That should download the required Docker images (might take quite a while depending on how fast your server's network connection is) and result in starting 5 different Docker containers, and you'll be able to watch them put out status (and error) messages as they boot and find their various dependencies. If there're no obvious errors, you can hit CTRL-C to shut them down again and restart them in a mode that keeps them running after you log out</p> <p><code>docker-compose up -d</code></p> <p>Note, you can always stop the containers by running docker-compose stop in that directory. You can check their status by running</p> <p><code>docker ps</code></p> <p>which should show you something like this:</p> <p><code>CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                                NAMES<br /> c6be9f3eef1e        gargron/mastodon         "bundle exec rails s "   13 days ago         Up 13 days          127.0.0.1:3000-&gt;3000/tcp, 4000/tcp   dockermastodon_web_1<br /> 6a123d9b1843        gargron/mastodon         "bundle exec sidekiq "   13 days ago         Up 13 days          3000/tcp, 4000/tcp                   dockermastodon_sidekiq_1<br /> f06c4a9bc479        gargron/mastodon         "npm run start"          13 days ago         Up 13 days          3000/tcp, 127.0.0.1:4000-&gt;4000/tcp   dockermastodon_streaming_1<br /> 6dbfad0669f8        postgres:alpine          "docker-entrypoint.sh"   2 weeks ago         Up 13 days          5432/tcp                             dockermastodon_postgres_1<br /> 8026b79e976d        redis:alpine             "docker-entrypoint.sh"   4 weeks ago         Up 13 days          6379/tcp                             dockermastodon_redis_1</code></p> <p>You can use the 12 digit IDs to run other Docker commands, like <code>docker inspect [ID]</code> or <code>docker exec -it [ID] bash</code> to log into the container itself and get a bash prompt. After all that's running, you can do some final housekeeping:</p> <p><code>docker-compose run --rm web rails db:migrate<br /> docker-compose run --rm web rails assets:precompile<br /> sudo vim /etc/nginx/sites-available/mastodon</code></p> <p>Edit this to look like the mastodon nginx config file below.</p> <p><code>sudo cd /etc/nginx/sites-enabled<br /> sudo ln -sf ../sites-available/mastodon .</code></p> <p>to enable the new configuration...</p> <p><code>sudo nginx -t</code></p> <p>To check for typos in you file. If you get no errors, you can restart nginx:</p> <p><code>sudo service nginx restart</code></p> <p>When that's done,  to [your domain] in your browser, which should take you to https://[your domain] and create a new user. If your email is set up properly, you'll get an email confirmation, and this will allow you to log in. If that works, I'd encourage you to modify your configuration to use a Let's Encrypt SSL certificate to protect your users' (and your server's) security. <a href="/protecting-your-users-lets-encrypt-ssl-certs">We provide this dedicated howto</a>! The .env.production template below <em>assumes you've done this</em>, so if your Mastodon isn't working, that might be why (you can try turning <code>LOCAL_HTTPS=false</code> temporarily if that's helpful).</p> <p>You will want to create an admin user - create the user first through the web interface, and then on the command line run (replacing <code>[admin username] </code>with the username you set up:</p> <p><code>cd /home/docker</code><code>/docker-mastodon<br /> docker-compose run --rm web rails mastodon:make_admin USERNAME=[admin username]</code></p> <p>Then go to that user's Mastodon preferences and define the relevant info for your instance (see the administration options).</p> <h2>Debugging</h2> <p>If you run in to problems, a very useful Docker Compose option to use (from within the docker-mastodon directory) is </p> <p><code>docker-compose logs -f</code></p> <p>It will provide you with the automatically updating integrated logs of all the containers you've unleashed!</p> <h3>Sample .env.production</h3> <p>Here's a sample with (hopefully obviously named) [placeholders]</p> <p><code># Service dependencies<br /> REDIS_HOST=redis<br /> REDIS_PORT=6379<br /> DB_HOST=postgres<br /> DB_USER=postgres<br /> DB_NAME=postgres<br /> DB_PASS=<br /> DB_PORT=5432</code></p> <p><code># Federation<br /> LOCAL_DOMAIN=[your domain]<br /> LOCAL_HTTPS=true</code></p> <p><code># Application secrets<br /> # Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)<br /> PAPERCLIP_SECRET=[first secret]<br /> SECRET_KEY_BASE=[second secret]<br /> OTP_SECRET=</code>[third secret]</p> <p><code># Registrations<br /> # Single user mode will disable registrations and redirect frontpage to the first profile<br /> # SINGLE_USER_MODE=true<br /> # Prevent registrations with following e-mail domains<br /> # EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc</code></p> <p><code># E-mail configuration<br /> SMTP_SERVER=[smtp server domain name]<br /> SMTP_PORT=587<br /> SMTP_LOGIN=[smtp user name]<br /> SMTP_PASSWORD=[smtp user password]<br /> SMTP_FROM_ADDRESS=[sender address for outgoing mastodon emails]<br /> SMTP_DOMAIN=[your site's base domain]<br /> SMTP_OPENSSL_VERIFY_MODE=none</code></p> <p><code># Optional asset host for multi-server setups<br /> # CDN_HOST=assets.example.com</code></p> <p><code># S3 (optional)<br /> # S3_ENABLED=true<br /> # S3_BUCKET=<br /> # AWS_ACCESS_KEY_ID=<br /> # AWS_SECRET_ACCESS_KEY=<br /> # S3_REGION=<br /> # S3_PROTOCOL=http<br /> # S3_HOSTNAME=192.168.1.123:9000</code></p> <p><code># Optional alias for S3 if you want to use Cloudfront or Cloudflare in front<br /> # S3_CLOUDFRONT_HOST=</code></p> <p><code># Streaming API integration<br /> # STREAMING_API_BASE_URL=</code></p> <h3>Sample docker-compose.yml</h3> <p>Here's a sample with [placeholders]. Note - this generates <strong>five Docker containers. </strong>Yeah, like I said, this is a serious, complex app.</p> <p><code>version: '2'<br /> services:<br />   postgres:<br />     restart: unless-stopped<br />     image: postgres:alpine<br />     volumes:<br />      - /home/data/mastodon/postgres:/var/lib/postgresql/data<br />   redis:<br />     restart: unless-stopped<br />     image: redis:alpine<br />     volumes:<br />      - /home/data/mastodon/redis:/data<br />   web:<br />     restart: unless-stopped<br />     build: .<br />     image: gargron/mastodon<br />     env_file: .env.production<br />     command: bundle exec rails s -p 3000 -b '0.0.0.0'<br />     ports:<br />       - "127.0.0.1:3000:3000"<br />     depends_on:<br />       - postgres<br />       - redis<br />     volumes:<br />       - /home/data/mastodon/packs:/mastodon/public/packs</code><br /><code>      - /home/data/mastodon/assets:/mastodon/public/assets</code><br /><code>      - /home/data/mastodon/system:/mastodon/public/system<br />   streaming:<br />     restart: unless-stopped<br />     build: .<br />     image: gargron/mastodon<br />     env_file: .env.production<br />     command: npm run start<br />     ports:<br />       - "127.0.0.1:4000:4000"<br />     depends_on:<br />       - postgres<br />       - redis<br />   sidekiq:<br />     restart: unless-stopped<br />     build: .<br />     image: gargron/mastodon<br />     env_file: .env.production<br />     command: bundle exec sidekiq -q default -q mailers -q pull -q push<br />     depends_on:<br />       - postgres<br />       - redis<br />     volumes:<br />       - /home/data/mastodon/system:/mastodon/public/system</code></p> <h3>Sample nginx mastodon config file</h3> <p>Here's a copy of the nginx configuration file I use (with [placeholders], obviously) - it's the result of quite a lot of tweaking. Have fun!</p> <p><code>map $http_upgrade $connection_upgrade {<br />     default upgrade;<br />     ''      close;<br /> }</code></p> <p><code>server {<br />     listen 80;<br /> #    listen [::]:80;<br />     server_name [your domain];<br />     root /var/www/html;</code></p> <p><code>    # for let's encrypt renewals!<br />     location /.well-known/acme-challenge/ {<br />         default_type text/plain;<br />         root /var/www/html;<br />    }</code></p> <p><code>    # redirect all HTTP traffic to HTTPS.<br />     location / {<br />         return 302 https://[your domain]$request_uri;<br />     }<br /> }</code></p> <p><code>server {<br />     listen 443 ssl;<br /> #    listen [::]:443 ssl;<br />     server_name [your domain];</code></p> <p><code>    ssl_certificate /etc/letsencrypt/live/[your domain]/fullchain.pem;<br />     ssl_certificate_key /etc/letsencrypt/live/[your domain]/privkey.pem;<br />     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br />     # from https://0x39b.fr/post/nginx_security/<br />     ssl_session_timeout 1d;<br />     ssl_session_cache shared:SSL:50m;<br />     #ssl_session_tickets off;<br />     ssl_prefer_server_ciphers on;<br />     ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';<br />     # OCSP Stapling ---<br />     # fetch OCSP records from URL in ssl_certificate and cache them<br />     ssl_stapling on;<br />     ssl_stapling_verify on;<br />     # to create this, see https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html<br />     ssl_dhparam /etc/ssl/certs/dhparam.pem;</code></p> <p><code>    # for let's encrypt renewals!<br />     location /.well-known/acme-challenge/ {<br />         default_type text/plain;<br />         root /var/www/html;<br />     }</code></p> <p><code>    keepalive_timeout    70;<br />     sendfile             on;<br />     client_max_body_size 0;<br />    </code>  <code># update from https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Production-guide.md<br />     gzip on;<br />     gzip_vary on;<br />     gzip_proxied any;<br />     gzip_comp_level 6;<br />     gzip_buffers 16 8k;<br />     gzip_http_version 1.1;<br />     gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;</code></p> <p><code>    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";</code></p> <p><code>    location / {<br />         try_files $uri @proxy;<br />     }</code></p> <p><code>    location ~ ^/(packs|system/media_attachments/files|system/accounts/avatars) {<br />         add_header Cache-Control "public, max-age=31536000, immutable";<br />         try_files $uri @proxy;<br />     }</code></p> <p> </p> <p><code>    location @proxy {<br />         proxy_set_header Host $host;<br />         proxy_set_header X-Real-IP $remote_addr;<br />         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br />         proxy_set_header X-Forwarded-Proto https;</code><br /><code>        proxy_set_header Proxy "";</code><br /><code>        proxy_pass_header Server;<br />         proxy_pass http://localhost:3000;<br />         proxy_buffering off;<br />         proxy_redirect off;<br />         proxy_http_version 1.1;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection $connection_upgrade;<br />         tcp_nodelay on;<br />     }</code></p> <p><code>    location /api/v1/streaming {<br />         proxy_set_header Host $host;<br />         proxy_set_header X-Real-IP $remote_addr;<br />         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br />         proxy_set_header X-Forwarded-Proto https;<br />         proxy_set_header Proxy "";<br />         proxy_pass http://localhost:4000;<br />         proxy_buffering off;<br />         proxy_redirect off;<br />         proxy_http_version 1.1;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection $connection_upgrade;<br />         tcp_nodelay on;<br />     }</code></p> <p><code>    error_page 500 501 502 503 504 /500.html;<br />     # this should give you an A+ rating on https://instances.mastodon.xyz/<br />     add_header X-XSS-Protection "1; mode=block";<br />     add_header Content-Security-Policy "default-src 'none'; font-src 'self'; media-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data: blob:; connect-src 'self' wss://[your domain]";<br /> }</code></p> <p>Enjoy!</p> <h2>Keeping Mastodon up to date</h2> <p>To ensure your Mastodon doesn't become a run down abandoned trailerpark node bit rotting quietly in the ether, I recommend you keep it up to date! There is a useful <a href="https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Docker-Guide.md">Mastodon Administrator's guide for Docker instances</a> that I consult every time I want to update. Note, if the "git stash" part of it is too hard, I recommend that any time you change your docker-compose.yml file, you copy it to</p> <p><code>cp docker-compose.yml docker-compose.yml-backup</code></p> <p>That way, you can simply remove docker-compose.yml (double check your docker-compose.yml-backup is up-to-date first!), do the <code>git checkout TAG_NAME</code>, and then</p> <p><code>cp docker-compose.yml-backup docker-compose.yml </code></p> <p>and you're done. Welcome the Fediverse!</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=14&amp;2=field_blog_comments&amp;3=comment" token="MWkKHP2AmYlJPPoRbiZwT_Yf5xOfApOMw_Kb2WBZEpo"></drupal-render-placeholder> </div> </section> Fri, 02 Jun 2017 03:02:31 +0000 dave 14 at http://tech.oeru.org Multiple Discourse Forums on the same server http://tech.oeru.org/multiple-discourse-forums-same-server <span class="field field--name-title field--type-string field--label-hidden">Multiple Discourse Forums on the same server</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--discourse"> <span class="field__item-wrapper"><a href="/taxonomy/term/19" hreflang="en">discourse</a></span> </div> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--postgresql"> <span class="field__item-wrapper"><a href="/taxonomy/term/20" hreflang="en">postgresql</a></span> </div> <div class="field__item field__item--redis"> <span class="field__item-wrapper"><a href="/taxonomy/term/21" hreflang="en">redis</a></span> </div> <div class="field__item field__item--ruby-on-rails"> <span class="field__item-wrapper"><a href="/taxonomy/term/22" hreflang="en">ruby on rails</a></span> </div> <div class="field__item field__item--oauth2"> <span class="field__item-wrapper"><a href="/taxonomy/term/23" hreflang="en">oauth2</a></span> </div> <div class="field__item field__item--lets-encrypt"> <span class="field__item-wrapper"><a href="/taxonomy/term/17" hreflang="en">let&#039;s encrypt</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" class="username">dave</a></span> <span class="field field--name-created field--type-created field--label-hidden">Fri 24/03/2017 - 13:20</span> <div class="field field-node--field-image field-name-field-image field-type-image field-label-hidden has-multiple"> <figure class="field-type-image__figure image-count-1"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-03/CommunityDiscourse_frontpage.png?itok=zflBNfcG" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Our Discourse front page from a user&#039;s perspective&quot;}" role="button" title="Our Discourse front page from a user&#039;s perspective" data-colorbox-gallery="gallery-field_image-bJQbOpH2zXo" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Our Discourse front page from a user&#039;s perspective&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/CommunityDiscourse_frontpage.png?itok=PKmMwvtz" width="220" height="125" alt="Our Discourse front page from a user&#039;s perspective" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-2"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-03/CommunityDiscourse_badgepage.png?itok=wB27ZtNy" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community&quot;}" role="button" title="&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community" data-colorbox-gallery="gallery-field_image-bJQbOpH2zXo" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/CommunityDiscourse_badgepage.png?itok=j_6aa6dT" width="220" height="125" alt="&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-3"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2017-03/CommunityDiscourse_topicediting.png?itok=UKvo5xRW" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users&quot;}" role="button" title="Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users" data-colorbox-gallery="gallery-field_image-bJQbOpH2zXo" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/CommunityDiscourse_topicediting.png?itok=osrNHUAn" width="220" height="164" alt="Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users" loading="lazy" class="image-style-medium" /> </a> </div> </figure> </div> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>At the OERu we have two separate instances of market category leading <a href="https://discourse.org" title="The Discourse Forum Community"> Discourse Forum</a>: one for <a href="https://community.oeru.org" title="The OERu Partner and Contributor Collaboration Forum">OER partner and contributor collaborators</a> and the <a href="https://forum.oeru.org" title="The OERu Learner Discourse Forum">other for learners</a>. These days, online forums are seen as a bit old-school: fuddy-duddy. From my point of view, however, Discourse is "Forum-NG" (a Next Generation forum). I think it's both very cool and innovative - not at all fuddy-duddy. Even better, Discourse also happens to be free and open source. Its active development community is storming ahead with updates and improvements at an impressive pace.</p> <p>Discourse is what we developers refer to as a "non-trivial" application. It's complex, no question, but it's also very mature and well engineered. It's built entirely on open source components. It uses the <a href="https://rubyonrails.org">Ruby on Rails</a> framework and pulls in a bunch of external systems including <a href="https://redis.io">Redis</a> (for caching and queuing) and <a href="https://www.postgresql.org/">PostgreSQL</a> for persistent data storage. The most common mode for running Discourse is via a single Docker container which includes PostgreSQL, Redis, and the full Ruby on Rails stack and Discourse application. Typically, an organisation only deploys a single Discourse instance. We, however, identified the need to segment our audiences and so decided to deploy the two instances on our main hosting server. This was much more challenging deployment, and not overly well documented. It took a while to get it right. I wrote up a blow-by-blow of how I did it in hopes it would benefit others in my position! See these two threads:</p> <ul><li><a href="Multiple Discoursen, multiple Docker containers, one server with one nginx">Multiple Discourses, multiple containers, one server</a> for the whole story (and some community comments)</li> <li><a class="fancy-title" data-ember-action="" data-ember-action-932="932" href="https://meta.discourse.org/t/discourse-in-docker-nginx-reverse-proxy-ssl-everywhere-oauth2-custom/52280">Discourse in Docker + NGINX reverse proxy + SSL everywhere + OAuth2 Custom</a> - for protecting the privacy and security of our users, and making it quick and easy for them to log in using existing credentials (but preferably not ones controlled by foreign corporations)</li> </ul><p>Discourse is impressive. It <a href="https://meta.discourse.org/t/benefits-of-discourse-have-i-missed-anything/39849">offers a lot more</a> than I've described so far. I recommend your organisation has a look - if you don't want to manage it in-house (it's easy once it's set up), by all means support the developers by buying their hosted service!</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=9&amp;2=field_blog_comments&amp;3=comment" token="q1H1hJgcXmYH07bFGODqnJapU96HbkXSo3XOYNpq97w"></drupal-render-placeholder> </div> </section> Fri, 24 Mar 2017 00:20:39 +0000 dave 9 at http://tech.oeru.org