Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DeanWronowski/a34f7c13ad03eb4e2b299c68c0113ba6 to your computer and use it in GitHub Desktop.
Save DeanWronowski/a34f7c13ad03eb4e2b299c68c0113ba6 to your computer and use it in GitHub Desktop.

Revisions

  1. @TrillCyborg TrillCyborg renamed this gist Nov 23, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. @TrillCyborg TrillCyborg created this gist Nov 23, 2018.
    246 changes: 246 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,246 @@
    # Mastodon Docker Setup

    ## Setting up

    Clone Mastodon's repository.

    # Clone mastodon to ~/live directory
    git clone https://github.com/tootsuite/mastodon.git live
    # Change directory to ~/live
    cd ~/live
    # Checkout to the latest stable branch
    git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)


    Review the settings in `docker-compose.yml`. Note that it is **not default** to store the postgresql database and redis databases in a persistent storage location. If you plan on running your instance in production, you **must** uncomment the [`volumes` directive](https://github.com/tootsuite/mastodon/blob/972f6bc861affd9bc40181492833108f905a04b6/docker-compose.yml#L7-L16) in `docker-compose.yml`.

    ## Getting the Mastodon image

    ### Using a prebuilt image

    If you're not making any local code changes or customizations on your instance, you can use a prebuilt Docker image to avoid the time and resource consumption of a build. Images are available from Docker Hub: https://hub.docker.com/r/tootsuite/mastodon/

    To use the prebuilt images:

    1. Open `docker-compose.yml` in your favorite text editor.
    1. Comment out the `build: .` lines for all images (web, streaming, sidekiq).
    2. Edit the `image: tootsuite/mastodon` lines for all images to include the release you want. The default is `latest` which is the most recent stable version, however it recommended to explicitly pin a version: If you wanted to use v2.2.0 for example, you would edit the lines to say: `image: tootsuite/mastodon:v2.2.0`
    3. Save the file and exit the text editor.
    2. Run `cp .env.production.sample .env.production` to bootstrap the configuration. Edit the correct values now.
    3. Run `docker-compose build`. It will now pull the correct image from Docker Hub.
    4. Set correct file-owner with `sudo chown -R 991:991 public/system`

    ### Building your own image

    You must build your own image if you've made any code modifications. To build your own image:

    1. Open `docker-compose.yml` in your favorite text editor.
    1. Uncomment the `build: .` lines for all images (web, streaming, sidekiq) if needed.
    2. Save the file and exit the text editor.
    2. Run `cp .env.production.sample .env.production` to bootstrap the configuration. Edit the correct values now.
    3. Run `docker-compose build`.
    4. Set correct file-owner with `chown -R 991:991 public`

    ## Building the app

    Now the image can be used to generate a configuration with:

    docker-compose run --rm web bundle exec rake mastodon:setup

    This is an interactive wizard that will guide you through the basic and necessary options and generate new app secrets. At some point it will output your configuration, copy and paste that configuration into the `.env.production` file.

    The wizard will setup the database schema and precompile assets. After it's done, you can launch Mastodon with:

    docker-compose up -d

    ## nginx Configuration

    You need to configure [nginx](http://nginx.org) to serve your [Mastodon](https://github.com/tootsuite/mastodon/) instance.

    **Reminder: Replace all occurrences of example.com with your own instance's domain or sub-domain.**

    `cd` to `/etc/nginx/sites-available` and open a new file:

    `nano /etc/nginx/sites-available/example.com.conf`

    Copy and paste the following and make edits as necessary:

    ```nginx
    map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
    }

    server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    root /home/mastodon/live/public;
    # Useful for Let's Encrypt
    location /.well-known/acme-challenge/ { allow all; }
    location / { return 301 https://$host$request_uri; }
    }

    server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;

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

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    keepalive_timeout 70;
    sendfile on;
    client_max_body_size 80m;

    root /home/mastodon/live/public;

    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    add_header Strict-Transport-Security "max-age=31536000";

    location / {
    try_files $uri @proxy;
    }

    location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
    }

    location /sw.js {
    add_header Cache-Control "public, max-age=0";
    try_files $uri @proxy;
    }

    location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
    }

    location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
    }

    error_page 500 501 502 503 504 /500.html;
    }
    ```

    Activate the [nginx](http://nginx.org) configuration added:

    ```sh
    cd /etc/nginx/sites-enabled
    ln -s ../sites-available/example.com.conf
    ```

    This configuration makes the assumption you are using [Let's Encrypt](https://letsencrypt.org) as your TLS certificate provider.

    **If you are going to be using Let's Encrypt as your TLS certificate provider, see the
    next sub-section. If not edit the `ssl_certificate` and `ssl_certificate_key` values
    accordingly.**

    ## Let's Encrypt

    This section is only relevant if you are using [Let's Encrypt](https://letsencrypt.org/)
    as your TLS certificate provider.

    ### Generation Of The Certificate

    We need to generate Let's Encrypt certificates.

    **Make sure to replace any occurrence of 'example.com' with your Mastodon instance's domain.**

    Make sure that [nginx](http://nginx.org) is stopped at this point:

    ```sh
    systemctl stop nginx
    ```

    We will be creating the certificate twice, once with TLS SNI validation in standalone mode and the second time we will be using the webroot method. This is required due to the way
    [nginx](http://nginx.org) and the [Let's Encrypt](https://letsencrypt.org/) tool works.

    ```sh
    certbot certonly --standalone -d example.com
    ```

    After that successfully completes, we will use the webroot method. This requires [nginx](http://nginx.org) to be running:

    ```sh
    systemctl start nginx
    # The certbot tool will ask if you want to keep the existing certificate or renew it. Choose to renew it.
    certbot certonly --webroot -d example.com -w /home/mastodon/live/public/
    ```

    ### Automated Renewal Of Let's Encrypt Certificate

    [Let's Encrypt](https://letsencrypt.org/) certificates have a validity period of 90 days.

    You need to renew your certificate before the expiration date. Not doing so will make users of your instance unable to access the instance and users of other instances unable to federate with yours.

    We can create a cron job that runs daily to do this:

    ```sh
    nano /etc/cron.daily/letsencrypt-renew
    ```

    Copy and paste this script into that file:

    ```sh
    #!/usr/bin/env bash
    certbot renew
    systemctl reload nginx
    ```

    Save and exit the file.

    Make the script executable and restart the cron daemon so that the script runs daily:

    ```sh
    chmod +x /etc/cron.daily/letsencrypt-renew
    systemctl restart cron
    ```

    That is it. Your server will renew your [Let's Encrypt](https://letsencrypt.org/) certificate.

    ## Resources

    * https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Docker-Guide.md
    * https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Production-guide.md