16.04 http://tech.oeru.org/ en Protecting your users with Let's Encrypt SSL Certs http://tech.oeru.org/protecting-your-users-lets-encrypt-ssl-certs <span class="field field--name-title field--type-string field--label-hidden">Protecting your users with Let&#039;s Encrypt SSL Certs</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--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--install"> <span class="field__item-wrapper"><a href="/taxonomy/term/11" hreflang="en">install</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--_404"> <span class="field__item-wrapper"><a href="/taxonomy/term/13" hreflang="en">14.04</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--_804"> <span class="field__item-wrapper"><a href="/taxonomy/term/68" hreflang="en">18.04</a></span> </div> <div class="field__item field__item--_004"> <span class="field__item-wrapper"><a href="/taxonomy/term/75" hreflang="en">20.04</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">Thu 08/07/2021 - 14:23</span> <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><a class="visually-hidden focusable skip-link" href="https://tech.oeru.org/node/add/blog_post#main-content">Sk</a>For any website that requires anyone (users or even just a few admins) to transfer secrets to and from it, you want to ensure the data is end-to-end encrypted. Today various browsers (like Firefox) give warnings when you're sending secret data (like passwords) "in the clear", namely unencrypted. In early 2017, Google <a href="http://searchengineland.com/google-starts-giving-ranking-boost-secure-httpsssl-sites-199446">added further urgency</a> to doing the right thing for your users. </p> <p>In the past, getting an SSL certificate to achieve encryption for your domain (the little "lock" icon in browser address bar indicating that your communication with the site is encrypted), was a complicated, expensive proposition, requiring a lot of annoying and time consuming "identity verification" (sometimes via post in the dark old days) and a fee of, in some cases, a couple hundred dollars per year paid to your "SSL Cert Provider" to pay for those administrative services along with the software needed to gin up a long prime number to act as your encryption key (the long string of characters making up your SSL certificate).</p> <p>Thankfully, thanks to the efforts of the <a href="https://letsencrypt.org" title="Let's Encrypt - democratising SSL and making it ubquitous.">Let's Encrypt</a> community, the process is both far far easier, and free of cost. Now there really isn't an excuse for not having an SSL certificate on your site.</p> <p>Members of the Let's Encrypt community have provided a range of useful open source tools you can use to create and maintain certificates on your hosting infrastructure (e.g. the Virtual Machine (VM) on which you're installing web services detailed in the howtos on this site!). In this case we're going to use a tool, "<a href="https://certbot.eff.org/">certbot</a>" provided by the good folks at the <a href="https://eff.org">Electronic Frontier Foundation</a>.</p> <p>For VMs running Ubuntu 18.04 or more recent Long Term Support (LTS) versions of the Ubuntu Linux platform, you should be able to just run:</p> <p><code>sudo apt-get install letsencrypt</code></p> <p>For VMs running Ubuntu 14.04 or 16.04 which are what we use, the install is easy - at your VM command line, run:</p> <pre> <code>sudo add-apt-repository ppa:certbot/certbot sudo apt-get update sudo apt-get install certbot </code></pre> <p>We run Nginx on our VMs, which makes one or more hosted services (normally running in Docker containers) available on the Internet. Strictly speaking, we don't use full "end-to-end" encryption - in our case, on the server-end the encryption terminates at the Nginx server. We, perhaps cavalierly, assume that transfer between the host machine and a Docker container running on that host will be implicitly secure... The only way it could be compromised is if the VM itself is compromised, in which case, the Docker containers running on it could be, too. Avoiding having secure transfer between Nginx on the VM host and the various Docker containers also substantially simplifies setting up each application.</p> <p>Thanks to a service which Nginx provides SNI (or <a href="https://en.wikipedia.org/wiki/Server_Name_Indication">Server Name Indication</a>) - Apache and a few other web servers also provide this - which removes the historical limitation that meant you could only have one SSL certificate per IP address on a web server. The only downside of SNI is that some older browsers (and platforms) don't support it. Since those older technologies are rapidly dying out and it's quite expensive and difficult to have a single IP address for each SSL service on a server, we accept this compromise.</p> <h2>The Let's Encrypt Cert Process</h2> <p>Here's (roughly) how the process works:</p> <ol><li>Point your domain (via A or CNAME record) to point to the/an external IP address on your VM.</li> <li>Set up a domain (or domains) for non-secure hosting (on port 80) via your Nginx instance.</li> <li>The domain's configuration must include a special directory reserved for Let's Encrypt verification content.</li> <li>You request that certbot (on the VM) acquires a certificate for that domain (or domains) at which point <ol><li>the certbot writes a file with a hard-to-guess name to that special directory and requests that the Let's Encrypt infrastructure checks the domain name from outside</li> <li>Let's Encrypt checks that domain name and special directory to see that the expected number appears there, thus verifying that the requesting party actually has the ability to set content at this hard-to-guess filename, and therefore has legitimate claim to being the party controlling the domain name and server.</li> <li>Let's Encrypt registers the certificate request in the name of the party running the certbot (so it can, for example, send emails to the administrator warning them that the certificate needs to be renewed - which happens every 8 weeks or so), and</li> <li>Let's Encrypt sends verification back to your VM's certbot telling it to complete the certificate generation, which it then (digitally) signs in the name of the Let's Encrypt Certificate Authority (which, in turn, is recognised by almost all web browsers out-of-the-box - no mean feat, I can tell you).</li> </ol></li> <li>You get an alert telling you that you have created a valid SSL certificate.</li> <li>You alter your Nginx domain configuration to <ol><li>redirect connections to port 80 (un-encrypted) to port 443 (encrypted), and</li> <li>you set up the 443 configuration including your new certificates.</li> </ol></li> <li>You reload your Nginx configuration, and your site will now be end-to-end encrypted.</li> </ol><h2>The Let's Encrypt Snippet</h2> <p>To make it easy to include the relevant directory info, I recommend that you create a new file in your Nginx configuration (substitute your preferred text editor for "vim" in the following - "nano" is a good choice if you haven't already got a preference):</p> <p><code>sudo mkdir /etc/nginx/includes<br /> sudo vim /etc/nginx/includes/letsencrypt.conf</code></p> <p>and make sure it has the following content (note, I learned this thanks to <a href="https://community.letsencrypt.org/t/how-to-nginx-configuration-to-enable-acme-challenge-support-on-all-http-virtual-hosts/5622">someone else's howto</a> on the global Internet knowledge commons :))</p> <p><code># Rule for legitimate ACME Challenge requests<br /> location ^~ /.well-known/acme-challenge/ {<br />     default_type "text/plain";<br />     # this can be any directory, but this name keeps it clear<br />     root /var/www/letsencrypt;<br /> }</code></p> <p><code># Hide /acme-challenge subdirectory and return 404 on all requests.<br /> # It is somewhat more secure than letting Nginx return 403.<br /> # Ending slash is important!<br /> location = /.well-known/acme-challenge/ {<br />     return 404;<br /> }</code></p> <p>Next, make sure your directory exists (note - you only need to do this once per VM) - it shouldn't need an special permissions - it'll be written by the "root" user, and needs to be readable by the Nginx user, usually "www-data" on a Debian or Ubuntu Linux instance.</p> <p><code>mkdir /var/www/letsencrypt</code></p> <h2>Example Nginx Domain Configuration - unencrypted</h2> <p>Here's an example of a pre-cert Nginx domain configuration for example.org and <a href="http://www.example.org">www.example.org</a> (I usually name the configuration file after the main domain it concerns, so my file would be /etc/nginx/sites-available/example.org) - this should also let you do initial test of your app to make sure it works, before adding the additional complexity of SSL. (<em>Replace example.com (and <a href="http://www.example.com">www.example.com</a>) with your own domain!</em>):</p> <p><code>server {</code></p> <p><code>    listen 80; # this is one of our external IPs on the server.<br />     #listen   [::]:80 default ipv6only=on; ## listen for ipv6<br /><br />     # this root directory isn't really relevant in a proxy situation</code><br /><code>    # so I usually set it to the system default<br />     root /usr/share/nginx/www;<br />     index index.html index.htm;<br /><br />     server_name example.org www.example.org;<br /><br />     access_log /var/log/nginx/example.org_access.log;<br />     error_log /var/log/nginx/example.org_error.log;</code></p> <p><code>    # this is where we include the snippet<br />     include /etc/nginx/includes/letsencrypt.conf;</code></p> <p><code>    # this is just an example of a "proxy" configuration<br />     # for, say, a Docker-based service, exposed on the VM's<br />     # local port 8081<br />     location / {<br />         proxy_read_timeout      300;<br />         proxy_connect_timeout   300;<br />         proxy_redirect          off;<br />         proxy_set_header    Host                $http_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   $scheme;<br />         proxy_pass      http://127.0.0.1:8081;<br />     }</code><br /><code>}</code></p> <p>You can make sure that the configuration is visible to Nginx by adding it into the "sites-enabled" directory via a file link</p> <p><code>sudo ln -sf /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled </code></p> <p>Test things to make sure the new configuration doesn't have typos or bad references:</p> <p><code>sudo nginx -t</code></p> <p>and if not, make it live:</p> <p><code>sudo service nginx reload</code></p> <p>You should now be able to go to <a href="http://example.com">http://example.com</a> (or your domain) and you'll hopefully get your proxied application (if it's set up) or an Nginx error (see you nginx error file for more info!).</p> <p>Now it's time to request the certificate!</p> <h2>Example Certbot invocation</h2> <p>Once cerbot is installed, and a domain is configured, it's pretty straightforward to get a certificate.</p> <p>On the first invocation of certbot, you might get a coloured interface that requests your user details (e.g. name and email address) so that Let's Encrypt can register them for the purposes of future emails. They email if one of your certificates is on the verge of expiring, or if there's been a change to Let's Encrypt policy or process. It's worth being on the list. </p> <p>You can request your certificate with the following:</p> <p><code>sudo certbot certonly --webroot -w /var/www/letsencrypt -d example.org -d www.example.org</code></p> <p>If it works, it gratifyingly results in a message that starts with "Congratulations"!</p> <h2>Example Nginx Domain Configuration - unencrypted</h2> <p>Once you've got your certificate, you can reference it in your configuration. We normally set up a redirect from the unencrypted version of the site to the encrypted on (except for the Let's Encrypt verification directory!):</p> <p><code>server {<br />     listen 80; # listen on port 80 on all IPv4 addresses</code><br /><code>    listen [::]:80; # listen on port 80 on all IPv6 addresses</code><br /><code> <br />     root /var/www/html;<br />     index index.html index.htm;<br /><br />     server_name example.org www.example.org;<br /><br />     access_log /var/log/nginx/example.org_access.log;<br />     error_log /var/log/nginx/example.org_error.log;</code></p> <p><code>    include /etc/nginx/includes/letsencrypt.conf;</code></p> <p><code>    # a 302 is a "soft" redirect. A 301 can never be reversed.<br />     location / {<br />         return 302 https://example.org$request_uri;<br />     }       <br /> }<br /><br /> server {</code><br /><span style="font-family:monospace"><span style="color:#000000;background-color:#ffffff;">    listen 443 ssl; # listen on port 443 on all IPv4 addresses</span></span><br /><span style="font-family:monospace"><span style="color:#000000;background-color:#ffffff;">    listen [::]:443 ssl; # listen on port 443 on all IPv6 addresses</span></span><br /><br /><code>    # this is a deprecated rule, not required in Ubuntu 20.04's nginx<br />     # ssl on; <br />     ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;<br />     ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;</code><br /><code>    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br />     ssl_dhparam /etc/ssl/certs/dhparam.pem;<br />     keepalive_timeout 20s;</code></p> <p><code>    access_log /var/log/nginx/example.org_access.log;<br />     error_log /var/log/nginx/example.org_error.log;</code></p> <p><code>    root /var/www/html;<br />     index index.html index.htm;</code><br /><br /><code>    server_name example.org www.example.org;<br />    <br />     # this is just an example of a "proxy" configuration<br />     # for, say, a Docker-based service, exposed on the VM's<br />     # local port 8081<br />     location / {<br />         proxy_read_timeout      300;<br />         proxy_connect_timeout   300;<br />         proxy_redirect          off;<br />         proxy_set_header    Host                $http_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   $scheme;<br />         proxy_pass      http://127.0.0.1:8081;<br />     }</code><br /><code>}</code></p> <p>Note - you also need to set up the <code>ssl_dhparam</code> file for this configuration to work. You can do this based on <a href="https://michael.lustfield.net/nginx/getting-a-perfect-ssl-labs-score" title="Setting hp ssl_dhparam">these instructions</a> after installing OpenSSL tools:</p> <p><code>sudo apt-get install openssl</code></p> <p>by running (warning - this can take quite a long time, 5-15 minutes in my experience - the system needs to generate sufficient <a href="https://en.wikipedia.org/wiki/Entropy">entropy</a> to achieve acceptable randomness):</p> <pre class="literal-block"> <code>openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096</code> </pre> <p>When you've set up the file, you can enable it:</p> <p><code>sudo ln -sf /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled</code></p> <p>Test the file to ensure there aren't any syntax errors before reloading nginx:</p> <p><code>sudo nginx -t</code></p> <p>If this shows an error, you'll need to fix the file. If all's well, reload nginx to enable the new configuration:</p> <p><code>sudo service nginx reload</code></p> <p>You should now be able to point your browser at your domain name, and it should automatically redirect you to <a href="https://example.org">https://example.org</a> - and (based on the above configuration, <a href="https://www.example.org">https://www.example.org</a> should work too. You might want to redirect <a href="http://www.example.org">www.example.org</a> to example.org or visa versa).</p> <p>A word to the wise - if it doesn't work, check your firewall settings!</p> <p><strong>Update:</strong> discovered this <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04">very well done how-to</a> on Let's Encrypt that offers additional background to this one.</p> <h2>Ongoing Certificate Maintenance</h2> <p>One of the nice things about EFF's certbot is that when it's installed, it also installs a nightly cron task (see <code>/etc/cron.d/certbot</code>) which checks all domains registered on the server for renewals. Assuming your domains have been configured in Nginx as described above, renewals should occur automatically, and you'll just receive a periodic email to let you know that they've happened.</p> <p>If you want to check your renewal status, you can run this:</p> <pre> <code>sudo certbot renew --dry-run</code></pre> <p>Good on you for securing your users and your site!</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=11&amp;2=field_blog_comments&amp;3=comment" token="u_cC5Zw8SKo5_CwG7txdoRLRmx6G2xIvWYWTc95IdY0"></drupal-render-placeholder> </div> </section> Thu, 08 Jul 2021 02:23:06 +0000 dave 11 at http://tech.oeru.org 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 Docker Compose: A better way to deploy Rocketchat, Wekan, and MongoDB http://tech.oeru.org/docker-compose-better-way-deploy-rocketchat-wekan-and-mongodb <span class="field field--name-title field--type-string field--label-hidden">Docker Compose: A better way to deploy Rocketchat, Wekan, and MongoDB</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--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/49" hreflang="en">docker-compose</a></span> </div> <div class="field__item field__item--rocketchat"> <span class="field__item-wrapper"><a href="/taxonomy/term/18" hreflang="en">rocket.chat</a></span> </div> <div class="field__item field__item--wekan"> <span class="field__item-wrapper"><a href="/taxonomy/term/15" hreflang="en">wekan</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--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--mongodb"> <span class="field__item-wrapper"><a href="/taxonomy/term/14" hreflang="en">mongodb</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> </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">Tue 23/05/2017 - 11:03</span> <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>A few months back, I posted instructions on deploying <a href="/installing-rocketchat-docker-ubuntu-linux-1404">Rocket.Chat</a> and <a href="/installing-wekan-docker-ubuntu-linux-1404">Wekan</a> instances (and their mutual dependency, <a href="/installing-mongodb-docker-ubuntu-linux-1404">MongoDB</a>) individually. Since then, I've spent some time with Docker Compose, a set of scripts which help you to define, build, and manage a set of Docker containers. Docker Compose is a thing of beauty. This is the way I now deploy Rocket.Chat, Wekan, and MongoDB together.</p> <h2>Install Docker and Docker Compose</h2> <p>Install <a href="https://docs.docker.com/engine/installation/linux/ubuntu/">Docker</a> (including the "<a href="https://docs.docker.com/engine/installation/linux/linux-postinstall/">post-installation</a>" steps to allow non-root users to run Docker) and <a href="https://docs.docker.com/compose/install/#alternative-install-options" title="We recommend the &quot;pip&quot; install method">Docker Compose</a> on your server (we recommend Ubuntu 16.04 or the older 14.04). We recommend using the "pip" (Python package manager) to do the install.</p> <h2>Create your Docker Compose recipe</h2> <p>We recommend creating a directory with an obvious name - in my case, it's <code>/home/www/docker-rocketchat-wekan-mongo</code></p> <p>In that directory, I create a file called <code>docker-compose.yml</code> containing (I've removed implementation specific details and replaced them with [placeholders]):</p> <p><code>version: '2'<br /> services:<br />   mongo:<br />     restart: unless-stopped<br />     image: mongo<br />     volumes:<br />       - [data directory path]:/data/db<br />       - [backup directory path]:/backups<br />     command: --smallfiles<br />   rocketchat-oeru:<br />     restart: unless-stopped<br />     image: rocketchat/rocket.chat<br />     ports:<br />       - "127.0.0.1:[port number]:3000" # should be a free port above 1024<br />     depends_on:<br />       - mongo<br />     environment:<br />       - MONGO_URL=mongodb://mongo/rocket<br />       - ROOT_URL=[domain name (including schema, e.g. http://)]<br />     volumes:<br />       - [upload directory path]:/var/www/rocket.chat/uploads<br />   wekan:<br />     restart: unless-stopped<br />     image: mquandalle/wekan<br />     ports:<br />       - "127.0.0.1:[port number]:80" # should be a free port above 1024<br />     depends_on:<br />       - mongo<br />     environment:<br />       - VIRTUAL_HOST=[domain name (don't include schema, e.g. https://)]<br />       - MONGO_URL=mongodb://mongo/plan<br />       - ROOT_URL=[domain name (include schema, e.g. https://)]<br />       - MAIL_URL=smtp://[smtp username]:[smtp password]@[server name or IP]:[port: 25, 465, or 587]/<br />     volumes:<br />       - [path to public content]:/built_app/programs/web.browser/app</code></p> <p>Note, you can include multiple instances of either Rocket.Chat or Wekan simply by providing a new name (e.g. rocketchat2 or wekan2 or similar) and a new set of properties - just make sure you're using a unique (and otherwise unused) port number! You can check what's on your server's ports using <code>netstat -punta | less </code>to make sure you're not doubling up. </p> <p>In case it's not obvious, you can leave out either the rocketchat or wekan definitions if you don't want to run those services!</p> <h2>Creating and Running the Docker Containers</h2> <p>It's easy to create the containers: simply run</p> <p><code>docker pull mongo<br /> docker pull rocket.chat<br /> docker pull mquandalle/wekan</code></p> <p>and when it's finished, run</p> <p><code>docker-compose up </code></p> <p>which should start all your containers, but leave you with a running log - this is great for testing, but when you're happy it's all running you hit CTRL-C (to shut down the current set of containers) and then run</p> <p><code>docker-compose up -d </code></p> <p>which runs the containers in daemon mode, without the running log. You can then log out of your server, and your containers will continue running!</p> <p>Based on the configuration above (the "unless-stopped" directive), your containers will restart automatically if your server is rebooted. If you <em>do</em> want to stop them for some reason, you can via</p> <p><code>docker-compose stop</code></p> <p>Easy.</p> <h2>Serving them to the Web</h2> <p>Once you've got your containers running, you need to make sure you've got a web server running on your host to act as the reverse proxy so that external requests get sent to them reliably! We use <a href="nginx.org">Nginx</a>.</p> <h3>RocketChat Nginx</h3> <p>Here's our configuration (with appropriate [substitutions]) - you can create it as <code>/etc/nginx/sites-available/[domain name]</code>:</p> <p><code>server {<br />     listen 80;<br />     server_name [domain name];</code></p> <p><code>  ## Access and error logs.<br />   access_log /var/log/nginx/[domain name]_access.log;<br />   error_log /var/log/nginx/[domain name]_error.log;</code></p> <p><code>  # see https://tech.oeru.org/protecting-your-users-lets-encrypt-ssl-certs<br />   include /etc/nginx/includes/letsencrypt.conf</code></p> <p><code>  # we use a 302 temporary redirect rather than a 301 permanent redir</code><br /><code>  location / {<br />     return 302 https://[domain name]$request_uri;<br />   }<br /> }<br /><br /> server {<br />     listen 443 ssl;<br />     ssl on;</code><br />      <code>  # see https://tech.oeru.org/protecting-your-users-lets-encrypt-ssl-certs<br />     ssl_certificate /etc/letsencrypt/live/[domain name]/fullchain.pem;<br />     ssl_certificate_key /etc/letsencrypt/live/[domain name]/privkey.pem;<br />     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br />     ssl_dhparam /etc/ssl/certs/dhparam.pem;</code></p> <p><code>    keepalive_timeout 20s;</code></p> <p><code>    root /var/www/html;<br />     index index.html index.htm;</code></p> <p><code>    server_name [domain name];</code></p> <p><code>    ## Access and error logs.<br />     access_log /var/log/nginx/[domain name]_access.log;<br />     error_log /var/log/nginx/[domain name]_error.log;</code></p> <p><code>    location / {<br />         proxy_pass http://127.0.0.1:[your rocketchat port];<br />         proxy_http_version 1.1;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection "upgrade";<br />         proxy_set_header Host $http_host;<br />         proxy_set_header X-Forwarded-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-Forward-Proto http;<br />         proxy_set_header X-Nginx-Proxy true;<br />         proxy_redirect off;<br />     }<br /> }</code></p> <p> </p> <h3>Wekan Nginx</h3> <p>Here's our configuration (with appropriate [substitutions]) - you can create it as <code>/etc/nginx/sites-available/[domain name]</code>: </p> <p><code># from https://github.com/wekan/wekan/wiki/Install-Wekan-Docker-in-production<br /> upstream websocket {<br />     server 127.0.0.1:[wekan port];<br /> }</code></p> <p><code>map $http_upgrade $connection_upgrade {<br />     default upgrade;<br />     '' close;<br /> }</code><br /><br /><code>server {<br />     listen    80;<br /><br />     root /var/www/html;<br />     index index.html index.htm;</code></p> <p><code>    # Make site accessible from http://localhost/<br />     server_name [domain name];</code></p> <p><code>    access_log /var/log/nginx/[domain name]_access.log;<br />     error_log /var/log/nginx/[domain name]_error.log;</code></p> <p><code>    # see https://tech.oeru.org/protecting-your-users-lets-encrypt-ssl-certs<br />     include /etc/nginx/includes/letsencrypt.conf</code></p> <p><code>    location / {<br />         return 302 https://[domain name]$request_uri; <br />     }<br /> }</code></p> <p><code>server {<br />     listen 443 ssl;<br />     ssl on;</code><br />      <code>  # see https://tech.oeru.org/protecting-your-users-lets-encrypt-ssl-certs<br />     ssl_certificate /etc/letsencrypt/live/[domain name]/fullchain.pem;<br />     ssl_certificate_key /etc/letsencrypt/live/[domain name]/privkey.pem;<br />     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br />     ssl_dhparam /etc/ssl/certs/dhparam.pem;</code></p> <p><code>    keepalive_timeout 20s;</code></p> <p><code>    access_log /var/log/nginx/[domain name]_access.log;<br />     error_log /var/log/nginx/[domain name]_error.log;</code></p> <p><code>    root /var/www/html;<br />     index index.html index.htm;</code></p> <p><code>    server_name [domain name];</code></p> <p><code>    location / {<br />         proxy_read_timeout 300;<br />         proxy_connect_timeout 300;<br />         proxy_redirect off;<br />         proxy_set_header Host $http_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 scheme;<br />         proxy_pass http://127.0.0.1:[your wekan port];<br />         proxy_set_header Host $host;<br />     }</code></p> <p><code>    location ~ websocket$ {<br />         proxy_pass http://websocket;<br />         proxy_http_version 1.1;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection $connection_upgrade;<br />     }</code><br /><code>}</code></p> <h3>Enable Nginx Configuration</h3> <p>To make your configurations active, do the following for each of your Nginx configurations:</p> <p><code>cd /etc/nginx/sites-enabled</code></p> <p>Do this for each file:<br /><code>ln -sf ../sites-available/[filename] .</code></p> <p>To check if there are any errors in the files, run</p> <p><code>nginx -t</code></p> <p>If not, you can restart Nginx to incorporate the new configuration files:</p> <p><code>sudo service nginx reload</code></p> <p>You can check for errors in the relevant log files specified in your nginx configurations above in <code>/var/log/nginx/*_error.log</code> or <code>/var/log/nginx/*_access.log</code>.</p> <h2>Protecting your users and reputation with encryption</h2> <p>We encourage you to ensure that these services are made available with full encryption to protect your users' privacy. It's <a href="/protecting-your-users-lets-encrypt-ssl-certs">easy (and no cost) to set up</a>!  The "include" directive in the Nginx configuration files above are examples of this approach.</p> <h2>Upgrades and Backups</h2> <p>We also encourage you to keep your services upgraded. It's easy to do and you'll experience little if any perceptible down time!</p> <p>Simply re-pull the containers and restart them - the updated containers will be launched without loss of data!</p> <p><code>docker pull mongo<br /> docker pull rocket.chat<br /> docker pull mquandalle/wekan</code></p> <p><code>docker-compose up -d</code></p> <p>If you want to back up your data - you need to do normal file backups of the directories on your local server that you've configured in the <code>docker-compose.yml</code> file, and you can do MongoDB backups based on <a href="/installing-mongodb-docker-ubuntu-linux-1404">our previous article</a> on the topic!</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=13&amp;2=field_blog_comments&amp;3=comment" token="IKqqNGzna0isfyEknk_oRE2sfWThSeFDbglHdf4AmjA"></drupal-render-placeholder> </div> </section> Mon, 22 May 2017 23:03:29 +0000 dave 13 at http://tech.oeru.org Installing Mautic with PHP7-FPM on Docker, Nginx, and MariaDB on Ubuntu 16.04 http://tech.oeru.org/installing-mautic-php7-fpm-docker-nginx-and-mariadb-ubuntu-1604 <span class="field field--name-title field--type-string field--label-hidden">Installing Mautic with PHP7-FPM on Docker, Nginx, and MariaDB 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--mautic"> <span class="field__item-wrapper"><a href="/taxonomy/term/24" hreflang="en">mautic</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 class="field__item field__item--marketing"> <span class="field__item-wrapper"><a href="/taxonomy/term/26" hreflang="en">marketing</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--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> </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">Thu 23/03/2017 - 09:30</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/OERu_Mautic_Configuration.png?itok=86FoDESx" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Mautic&#039;s configuration page&quot;}" role="button" title="Mautic&#039;s configuration page" data-colorbox-gallery="gallery-field_image-IlEFKXHG9Uw" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Mautic&#039;s configuration page&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/OERu_Mautic_Configuration.png?itok=tEfm85UE" width="220" height="162" alt="Mautic&#039;s configuration page" 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/OERu_Mautic_EmailEditor.png?itok=aY4cti9R" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;The Mautic email editor&quot;}" role="button" title="The Mautic email editor" data-colorbox-gallery="gallery-field_image-IlEFKXHG9Uw" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The Mautic email editor&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/OERu_Mautic_EmailEditor.png?itok=v5u4RrHk" width="220" height="160" alt="The Mautic email editor" 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/OERu_Mautic_CourseEmailRules.png?itok=RCQ1Wvo-" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Example of some campaign email logic rules&quot;}" role="button" title="Example of some campaign email logic rules" data-colorbox-gallery="gallery-field_image-IlEFKXHG9Uw" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Example of some campaign email logic rules&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/OERu_Mautic_CourseEmailRules.png?itok=Up6u3DvX" width="220" height="160" alt="Example of some campaign email logic rules" 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><a href="https://mautic.com" title="Mautic - marketing automation (think Mailchimp on steroids)">Mautic</a> is an open source marketing automation web application. Here at the OER Foundation, we use it to manage enquiries from prospective learners and partner institutions, to deliver timely emails to cohorts of learners undertaking our, and our partners', online courses, and to measure our effectiveness in achieving our <a href="https://oeru.org/about-oeru/" title="Our mission, goals, values, and other things that make us tick.">goals</a> and mission: to makes higher education accessible to everyone.</p> <p>Like proprietary tools (Campaign Monitor, Constant Contact, MailChimp, and others) Mautic provides Javascript forms that can easily be incorporated into your organisational website (see the forms at the bottom of each page of <a href="http://oeru.org" title="The OERu Website">our website</a> for example) by copying and pasting the "embed" code... These can be customised to your heart's content, and they allow you to engage with site visitors and make it easy for them to opt into receiving further communication from you. Based on how they interact with you subsequently, you can tune your communication with them to make it more relevant and compelling.</p> <p>Communications can be newsletters, lead follow up emails, and, in our case, a variety of course-specific email communications posts which convey specific instructions to cohorts of learners. Mautic has a full HTML email templating system with a variety of sharp looking pre-made templates, among other advanced capabilities. I like to describe Mautic to people as "MailChimp, on steroids".</p> <p>You can either purchase a supported Mautic service for your organisation, which we did for a while (they have just recently increased the prices substantially) but because it's open source, if you're interested, you can also install your own instance and track the development of the Mautic software. We have made a <a href="https://hub.docker.com/r/kiwilightweight/mautic/" title="OERu's Mautic image">Docker Image you can download</a> and the recipe for how to use it, and or modify it to better suit your purposes <a href="https://github.com/oeru/docker-mautic" title="Our Mautic Docker image recipe">is on Github</a>.</p> <p>We also <a href="/2018-update-oeru-technology-stack#case-study">did a case study</a> on Mautic because (particularly in light of recent price hikes from the proprietary competition like Mailchimp) turns out there's a lot of value in picking an open source option, even if you use their Software-as-a-Service option.</p> <h2>Our configuration</h2> <p>We run Mautic in the following configuration. On our virtual host, we run Ubuntu Linux 16.04 with <a href="http://nginx.org/" title="Open Source Webserving - it's the way the web's moving... ">Nginx</a> as the web server and <a href="https://mariadb.org/">MariaDB</a> (a more open drop-in replacement for MySQL) as the database. We use ufw as the firewall, and <a href="https://letsencrypt.org/" title="Do the right thing and protect your users' privacy through encrypted data transfer to/from your site!">Let's Encrypt</a> for (automatically) procuring signed SSL certs.</p> <p>We use AWS's <a href="https://aws.amazon.com/ses/?nc2=h_l3_as">Simple Email Service</a> for sending outgoing email to minimise the likelihood of being spam binned by people's overzealous mailservers, and we run Docker containers and the additional Docker Compose scripts.</p> <h2>Preparing the host</h2> <p>First things first, make sure you're logged into your host (probably via SSH) as a user who has "sudo" capabilities! You need to log into the host from your local machine. We recommend setting up <a href="https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server">key-based authentication</a>.</p> <h3>Keeping it secure</h3> <p>No computer system is ever full secure - there're always exploits waiting to be found, so security is a process of maintaining vigilance. Part of that is reducing exposure - minimising your "attack surface". Use a firewall - "<a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-16-04" title="Uncomplicated FireWall">ufw</a>" is installed on Ubuntu by default. Make sure you've got exceptions for SSH (without them, you could lock yourself out of your machine! Doh!).</p> <p>Run the following commands to allow your Docker containers to talk to other services on your host.</p> <p><code>sudo ufw allow in on docker0<br /> sudo ufw allow from 172.0.0.0/8 to any</code></p> <p>Specifically for Docker's benefit, you need to tweak the default Forwarding rule (I use "vim" as my editor. If you don't know how to/want to use it, replace <strong>vim</strong> with <strong>nano</strong> everywhere you see it in the following - nano's easier to use for simple edits like this):</p> <p><code>sudo vim /etc/default/ufw</code></p> <p>and copy the line <code>DEFAULT_FORWARD_POLICY="DROP"</code> tweak it to look like this (commenting out the default, but leaving it there for future reference!):</p> <p><code>#DEFAULT_FORWARD_POLICY="DROP"<br /> DEFAULT_FORWARD_POLICY="ACCEPT"</code></p> <p>You also have to edit <code>/etc/ufw/sysctl.conf</code> and remove the "#" at the start of the following lines, so they look like this:</p> <p><code>sudo vim /etc/ufw/sysctl.conf</code></p> <p><code># Uncomment this to allow this host to route packets between interfaces<br /> net/ipv4/ip_forward=1<br /> net/ipv6/conf/default/forwarding=1<br /> net/ipv6/conf/all/forwarding=1</code></p> <p>and finally restart the network stack and ufw on your server<code> </code></p> <p><code>sudo service networking restart<br /> sudo service ufw restart</code></p> <h3>Install Nginx</h3> <p>We like the efficiency of Nginx and clarity of Nginx configurations over those of Apache and other open source web servers. Here's how you install it.</p> <p><code>sudo apt-get install nginx-full</code></p> <p>To allow nginx to be visible via ports 80 and 443, run</p> <p><code>sudo ufw allow "Nginx Full"</code></p> <p><strong>Note</strong>: make sure your hosting service is not blocking these ports at some outer layer (depending on who's providing that hosting service you may have to set up port forwarding).</p> <h3>Install MariaDB</h3> <p>MariaDB is effectively a drop-in alternative to MySQL and we prefer it because it's not controlled by Oracle and has a more active developer community. On Ubuntu, MariaDB pretends to be MySQL for compatibility purposes, so don't be weirded out by the interchangeable names below. Install the server and the client like this.</p> <p><code>sudo apt-get install mariadb-server-10.0 mariadb-client-10.0</code></p> <p>You need to set a root (admin) user password - you might want to create a /root/.my.cnf file containing the following (replacing YOURPASSWORD) to let you access MariaDB without a password from the commandline<code>:</code></p> <p><code>[client]<br /> user=root<br /> password=YOURPASSWORD</code></p> <p>You should now be able to type "mysql" at the command prompt</p> <p>Tweak the configuration so that it's listening on</p> <p><code>sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf </code></p> <p>and copy the bind-address line and adjust so it looks like this - we want MariaDB to be listening on all interfaces, not just localhost (127.0.0.1)...</p> <p><code># Instead of skip-networking the default is now to listen only on<br /> # localhost which is more compatible and is not less secure.<br /> #bind-address           = 127.0.0.1<br /> bind-address            = 0.0.0.0</code></p> <p>Then restart MariaDB:</p> <p><code>sudo service mysql restart</code></p> <p>It should now be listening on port 3306 on all interfaces, i.e. 0.0.0.0.</p> <p>Now set up the database which will hold Mautic's data. Log into the MySQL client on the host (if you've created a .my.cnf file in your home directory as describe above, you won't need to enter your username and password):</p> <p><code>mysql -u root -p</code></p> <p>Enter your root password when prompted. It's also a good idea to gin up a password for your "mautic" database user. I usually use pwgen (<code>sudo apt-get install pwgen</code>) - for example running this command will give you a single 12 character password without special characters (just numbers and letters):</p> <p><code>pwgen -s 12 1<br /> T7KR2osrMkyC</code></p> <p>At the prompt (which will look something like MariaDB [(none)]&gt;) enter the following lines (putting your password in place of [passwd]):</p> <p><code>CREATE DATABASE mautic CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;<br /> CREATE USER "mautic"@"%" IDENTIFIED BY "[passwd]";<br /> GRANT ALL ON mautic.* to "mautic"@"%";<br /> FLUSH PRIVILEGES;</code></p> <p>Then enter \q to exit.</p> <h3>Docker and Docker Compose</h3> <p>Finally, to run the Mautic app itself the way we like to run it, you'll need Docker and Docker Compose installed. For Docker, follow <a href="https://docs.docker.com/engine/installation/linux/ubuntu/" title="Remember, you'll follow the &quot;Xenial 16.04&quot; instructions.">these excellent instructions</a>. And <a href="https://docs.docker.com/compose/install/" title="The last word on installing Docker Compose">here're instructions</a> for Docker Compose.</p> <p>Follow these "<a href="https://docs.docker.com/engine/installation/linux/linux-postinstall/" title="Things to do after setting up Docker...">post-installation steps</a>" too - your unprivileged user should be set up to run Docker as well - it'll make your life easier.</p> <h2>Setting up Mautic</h2> <p>Once you've got them install, you can grab our git repository. To do that, you'll need to install git (<code>sudo apt-get install git</code>) and you'll install the Docker configurations, and create a place to hold the Mautic codebase on the host's filesystem. I do that in my user's home directory - so first we'll got there:</p> <p><code>cd ~</code></p> <p>Then install the git repository in a directory called "docker-mautic":</p> <p><code>git clone git@github.com:oeru/docker-mautic.git</code></p> <p>Then we need to go into that directory and create the Docker Compose configuration:</p> <p><code>cd docker-mautic<br /> cp docker-compose.yml-sample docker-compose.yml</code></p> <p>Edit your docker-compose.yml and put in your details to replace the [placeholders].</p> <p><code>vim docker-compose.yml</code></p> <p>Your "hostname" will be the IP address assigned to your host by Docker. You can find it by running this:</p> <p><code>ifconfig | grep -1 "docker0" | grep "inet addr"</code></p> <p>It should be 172.nn.0.1 where nn is probably between 16-20.</p> <p>You also need to create a directory for the source code of Mautic to go into. I create a a directory called mautic with a src directory in it:</p> <p><code>mkdir -p mautic/src</code></p> <p>When you first run up the Mautic Docker instance, it will download the current (at that time) Mautic release and copy it into the directory on your host that you've designated.</p> <h3>Configuring Outgoing Mail</h3> <p>Your Mautic configuration has to send out emails for two separate reasons:</p> <ol><li>emails to your Mautic Contacts, and</li> <li>emails to you and/or your system administrators <em>about </em>your Mautic server.</li> </ol><p>The first one is crucial - it's necessary for Mautic to function properly. You do this through the Mautic interface after you've set things up. You might want to set up "bounce management", too so you can adjust your Contacts when, invariably, you have emails that are no longer valid...  You might want to employ an "SMTP-as-a-service" provider. We currently use Amazon's Simple Email Service (SES)... but there are dozens of others. For testing, you can just use any SMTP service you have available to you.</p> <p>But you also need to be able to receive emails from Mautic so that it can alert you to any issues, like, for example, if your scheduled tasks ("cron jobs" that run on your Docker instance) fail to run properly, which can lead to lots of issues. For that, we use msmtp, an app which acts as minimal SMTP relay. You'll need to set up the msmtp configuration to set that up (assuming your mautic dir is ~/mautic):</p> <p><code>cp conf/msmtp/msmtprc-sample mautic/msmtprc<br /> vim mautic/msmtprc</code></p> <p>And configure the values for the four [placeholders] provided.</p> <p>Finally, you're ready to launch. First grab the latest Mautic Docker image we've created.</p> <p><code>docker pull oeru/mautic</code></p> <p>Then, fire up the container. If it's working, you should see a screen which tells you your Mautic Mysql/MariaDB details:</p> <p><code>docker-compose up</code></p> <p>You can stop it by hitting "CTRL-C" and following the directions. To start it so keeps running even after you log out:</p> <p><code>docker-compose up -d</code></p> <p>You can always check it's running with</p> <p><code>docker ps</code></p> <p>and you can log into your instance by copying the 12 character container ID and copying it into this command:</p> <p><code>docker exec -it [containerID] bash</code></p> <p>If all is well, you have a Docker container, running PHP 7 (in FPM mode) ready to serve Mautic via a web socket, which is visible (by default) on port 9000 of the host. Now all we need to do is configure your nginx webserver to talk to it.</p> <h3>Configuring the web server</h3> <p>Although the docker-compose.yml includes a commented out configuration for a Docker container running Nginx (and a default.conf configuration file that should copied to [your mautic directory]/nginx-default.conf linked from the container), that either requires making the container directly visible on the external interface of the host (precluding running any other websites on the server) or the additional complexity of a reverse proxy to make the nginx serving Mautic visible externally. That usually takes another nginx or apache (or some other webserver) instance on the host.</p> <p>To simplify things, we've gone with having our nginx instance running on the host, and talking via sockets to the PHP service running on the Docker container. In the git directory you've downloaded, we've provided a documented nginx configuration file - nginx-on-docker-host.conf. To enable it (this is also documented in the file itself) copy it to, say /etc/nginx/sites-available/mautic - note that we initially have the SSL host commented out to allow you to set up the Let's Encrypt certificates below...</p> <p><code>sudo cp nginx-on-docker-host.conf /etc/nginx/sites-available/mautic<br /> sudo vim /etc/nginx/sites-available/mautic  # to tweak the configuration<br /> sudo ln -sf /etc/nginx/sites-available/mautic /etc/nginx/sites-enabled </code></p> <p>Check to see if there're any configuration errors</p> <p><code>sudo nginx -t</code></p> <p>If there're problems, fix them, and then make it live</p> <p><code>sudo service nginx reload</code></p> <p>and then edit it to tweak any settings required to, for example, configure your Mautic site's external domain name...</p> <h3>Configuring encryption</h3> <p>See our <a href="/protecting-your-users-lets-encrypt-ssl-certs">Let's Encrypt howto</a>!</p> <h2>Conclusion</h2> <p>And you're done - you can go to your domain name via https://[yourdomain] and you should get the Mautic login page. You can log in with the username "<strong>admin</strong>" and the password "<strong>mautic</strong>". <strong>Change the password immediately, and create an admin user for yourself with a strong password</strong>.</p> <p>You can update the Mautic app itself in place through the internal app update functionality (where Mautic knows how to upgrade its own code and will alert you there's an update available for your instance).</p> <p>Updating the container should be as easy as either doing another</p> <p><code>docker pull oeru/mautic</code></p> <p>and then shutting down Docker container via a</p> <p><code>docker-compose stop</code></p> <p>removing the old containers (this won't remove any data you want to save if you followed the directions above! But remember to do it in the right directory!) via</p> <p><code>docker-compose rm -v</code></p> <p>and then restarting it via</p> <p><code>docker-compose up -d</code></p> <p>That should give you the latest version of the container...</p> <p>Of course, always make sure your Ubuntu 16.04 host is up-to-date! Hope this helps a few people!</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=8&amp;2=field_blog_comments&amp;3=comment" token="Kcgd70EVt2416b5P14dLnx5yYMCljUCtMLCZqpJCh4Q"></drupal-render-placeholder> </div> </section> Wed, 22 Mar 2017 20:30:14 +0000 dave 8 at http://tech.oeru.org