├── README.md ├── files └── env.production ├── host-setup.yml ├── inventory ├── mastodon.yml ├── services.yml ├── templates ├── checkout.sh ├── clear-feeds.sh ├── clear-media.sh ├── mastodon-sidekiq.conf ├── mastodon-streaming.conf ├── mastodon-web.conf ├── nginx.conf └── refresh-push.sh └── vars.yml /README.md: -------------------------------------------------------------------------------- 1 | ## ansible for mastodon 2 | 3 | Install [Mastodon](https://github.com/Tootsuite/mastodon) on Ubuntu Trusty with upstart instead of anything more recent and [insert muttering here]. 4 | 5 | A work in progress as I make this more like a typical ansible role instead of a set of one-off playbooks. Seems to work just fine (I'm using this for my own instance, at least). 6 | 7 | 1. Clone this repo. Edit the [vars.yml](vars.yml) file. 8 | 1. Register a domain name. Buy a cert for it. 9 | 1. Spin up Ubuntu Trusty 14.04 on AWS & point DNS at the instance. Don't bother making user accounts or anything; only somebody with your key should be able to ssh in. Add security group rules allowing https from anywhere, or maybe http if you want to redirect. 10 | 1. Make an EBS volume or raid up some instance stores and mount them on `{{install_dir}}`, owned by ubuntu. 11 | 1. Clone this repo. 12 | 1. Edit the `inventory` file to replace the hostname with your name. This is just to make running the playbooks easier. 13 | 1. Edit the [vars.yml](vars.yml) file. 14 | 1. Note that I have assumed your cert files are named `yourhost.tld.pem` and `yourhost.tld.key`, which they might not be. Edit [host-setup.yml](host-setup.yml) if not. 15 | 1. Run `ansible-playbook -i inventory host-setup.yml`. You should now have a host with all required dependencies installed. 16 | 1. you might get ruby 2.3.3 instead of 2.3.1 in which case you should just edit the Gemfile. 17 | 1. Open up `templates/env.production`. Edit. Set up any implied required external services, like S3 buckets & a mailer. This will take you a bit. Have [the Mastodon production readme](https://github.com/Tootsuite/mastodon/blob/master/docs/Running-Mastodon/Production-guide.md) up while you do this. 18 | 1. NOTE that you need to replace `mastodon` with `ubuntu` in the postgres setup. This still needs to be done by hand. (Sorry!) 19 | 1. Run `ansible-playbook -i inventory mastodon.yml`. This does initial checkout & db creation. 20 | 1. Run `ansible-playbook -i inventory services.yml`. This sets up upstart services & the nginx config. 21 | 22 | The ansible plays set up the required cron jobs for you. They also create a handy script `scripts/checkout.sh` to pull source, run migrations, and restart all services for updating. 23 | 24 | ## BUGS 25 | 26 | * Postgres is still on the default volume! Argh. 27 | * No backups. Argh. 28 | * Need to write handlers so we stop/start only when the files change. 29 | * Should use rbenv to get ruby 2.3.1 instead of ruby 2.3.3. 30 | 31 | ## LICENCE 32 | 33 | ISC. Copy and edit at will! 34 | -------------------------------------------------------------------------------- /files/env.production: -------------------------------------------------------------------------------- 1 | # Service dependencies 2 | REDIS_HOST=redis 3 | REDIS_PORT=6379 4 | DB_HOST=db 5 | DB_USER=postgres 6 | DB_NAME=postgres 7 | DB_PASS= 8 | DB_PORT=5432 9 | 10 | # Federation 11 | LOCAL_DOMAIN=example.com 12 | LOCAL_HTTPS=true 13 | 14 | # Application secrets 15 | # Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose) 16 | PAPERCLIP_SECRET= 17 | SECRET_KEY_BASE= 18 | OTP_SECRET= 19 | 20 | # Registrations 21 | # Single user mode will disable registrations and redirect frontpage to the first profile 22 | # SINGLE_USER_MODE=true 23 | # Prevent registrations with following e-mail domains 24 | # EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc 25 | # Only allow registrations with the following e-mail domains 26 | # EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc 27 | 28 | # Optionally change default language 29 | # DEFAULT_LOCALE=de 30 | 31 | # E-mail configuration 32 | SMTP_SERVER=smtp.mailgun.org 33 | SMTP_PORT=587 34 | SMTP_LOGIN= 35 | SMTP_PASSWORD= 36 | SMTP_FROM_ADDRESS=notifications@example.com 37 | 38 | # Optional asset host for multi-server setups 39 | # CDN_HOST=assets.example.com 40 | 41 | # S3 (optional) 42 | # S3_ENABLED=true 43 | # S3_BUCKET= 44 | # AWS_ACCESS_KEY_ID= 45 | # AWS_SECRET_ACCESS_KEY= 46 | # S3_REGION= 47 | # S3_PROTOCOL=http 48 | # S3_HOSTNAME=192.168.1.123:9000 49 | 50 | # S3 (Minio Config (optional) Please check Minio instance for details) 51 | # S3_ENABLED=true 52 | # S3_BUCKET= 53 | # AWS_ACCESS_KEY_ID= 54 | # AWS_SECRET_ACCESS_KEY= 55 | # S3_REGION= 56 | # S3_PROTOCOL=https 57 | # S3_HOSTNAME= 58 | # S3_ENDPOINT= 59 | 60 | # Optional alias for S3 if you want to use Cloudfront or Cloudflare in front 61 | # S3_CLOUDFRONT_HOST= 62 | 63 | # Streaming API integration 64 | # STREAMING_API_BASE_URL= 65 | -------------------------------------------------------------------------------- /host-setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: general 3 | remote_user: ubuntu 4 | tasks: 5 | - include_vars: vars.yml 6 | - name: set readable host name 7 | become: true 8 | hostname: name="{{inventory_hostname}}" 9 | 10 | - name: add ppas 11 | become: true 12 | apt_repository: repo="{{item}}" state=present 13 | with_items: "{{ppas}}" 14 | 15 | - name: node ppa 16 | become: true 17 | shell: curl -sL https://deb.nodesource.com/setup_{{node_version}}.x | sudo bash - 18 | - name: install node 19 | become: true 20 | apt: pkg={{item}}={{node_version}}* force=true update_cache=yes 21 | with_items: 22 | - nodejs 23 | - nodejs-dbg 24 | - name: npm install some things 25 | become: true 26 | command: "npm install -g npm@latest json@latest json-diff@latest yarn" 27 | 28 | - name: install all apt packages 29 | become: true 30 | apt: pkg={{item}} state=present force=true update_cache=yes 31 | with_items: "{{packages}}" 32 | 33 | - name: create cert dir 34 | become: true 35 | file: 36 | path: "{{install_dir}}/certs" 37 | state: directory 38 | mode: 0600 39 | - name: copy TLS certs 40 | become: true 41 | copy: 42 | src: "../certs/{{item}}" 43 | dest: "/{{install_dir}}/certs/{{item}}" 44 | mode: 0600 45 | with_items: 46 | - "{{hostname}}.pem" 47 | - "{{hostname}}.key" 48 | 49 | - name: install ruby with rbenv 50 | command: "rbenv install {{ruby_version}}" 51 | 52 | - name: set rbenv global version 53 | command: "rbenv global {{ruby_version}}" 54 | 55 | - name: install bundler 56 | command: gem install bundler --no-ri 57 | -------------------------------------------------------------------------------- /inventory: -------------------------------------------------------------------------------- 1 | [general] 2 | yourserver.tld 3 | -------------------------------------------------------------------------------- /mastodon.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: general 3 | remote_user: ubuntu 4 | tasks: 5 | - include_vars: vars.yml 6 | - name: create live dir 7 | become: true 8 | file: 9 | path: "{{install_dir}}/live" 10 | state: directory 11 | owner: ubuntu 12 | group: ubuntu 13 | - name: clone the repo 14 | git: > 15 | repo=https://github.com/Gargron/mastodon.git 16 | dest="{{install_dir}}/live" 17 | update=yes 18 | accept_hostkey=true 19 | - name: install bundler deps 20 | command: bundle install --deployment --without development test chdir="{{install_dir}}/live" 21 | - name: install npm deps 22 | command: yarn install chdir="{{install_dir}}/live" 23 | - name: create cron scripts dir 24 | become: true 25 | file: 26 | path: "{{install_dir}}/cron" 27 | state: directory 28 | owner: ubuntu 29 | group: ubuntu 30 | - name: install cron scripts 31 | template: 32 | src: "templates/{{item}}" 33 | dest: "{{install_dir}}/cron/{{item}}" 34 | mode: 0774 35 | with_items: "{{cronscripts}}" 36 | - name: set up cron jobs 37 | cron: > 38 | name="{{item}}" 39 | job="{{install_dir}}/cron/{{item}} >> {{install_dir}}/live/log/{{item}}.log 2>&1" 40 | user=ubuntu 41 | minute="5" 42 | weekday="1" 43 | state=present 44 | with_items: "{{cronscripts}}" 45 | - name: create scripts dir 46 | become: true 47 | file: 48 | path: "{{install_dir}}/scripts" 49 | state: directory 50 | owner: ubuntu 51 | group: ubuntu 52 | - name: install some useful scripts 53 | template: 54 | src: "templates/{{item}}" 55 | dest: "{{install_dir}}/scripts/{{item}}" 56 | mode: 0774 57 | with_items: "{{scripts}}" 58 | -------------------------------------------------------------------------------- /services.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: general 3 | remote_user: ubuntu 4 | tasks: 5 | - include_vars: vars.yml 6 | - name: copy production variables 7 | copy: 8 | src: files/env.production 9 | dest: "{{install_dir}}/live/.env.production" 10 | - name: copy nginx config 11 | become: true 12 | template: 13 | src: templates/nginx.conf 14 | dest: "/etc/nginx/sites-enabled/{{hostname}}" 15 | - name: restart nginx 16 | become: true 17 | service: name=nginx state=restarted 18 | - name: create upstart config 19 | become: true 20 | template: 21 | src: "templates/mastodon-{{item}}.conf" 22 | dest: "/etc/init/mastodon-{{item}}.conf" 23 | with_items: "{{services}}" 24 | - name: enable all upstart services 25 | become: true 26 | with_items: "{{services}}" 27 | service: 28 | name: "mastodon-{{item}}" 29 | enabled: yes 30 | - name: stop them all 31 | become: true 32 | with_items: "{{services}}" 33 | service: 34 | name: "mastodon-{{item}}" 35 | state: stopped 36 | - name: start them all 37 | become: true 38 | with_items: "{{services}}" 39 | service: 40 | name: "mastodon-{{item}}" 41 | state: started 42 | -------------------------------------------------------------------------------- /templates/checkout.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd {{install_dir}}/live 6 | git pull 7 | bundle install 8 | yarn install 9 | RAILS_ENV=production bin/bundle exec rails db:migrate 10 | RAILS_ENV=production bin/bundle exec rails assets:precompile 11 | 12 | {% for service in services %} 13 | echo "Restarting mastodon-{{service}}..." 14 | sudo restart mastodon-{{service}} 15 | {% endfor %} 16 | -------------------------------------------------------------------------------- /templates/clear-feeds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd {{install_dir}}/live 4 | RAILS_ENV=production /usr/local/bin/bundle exec rake mastodon:feeds:clear 5 | -------------------------------------------------------------------------------- /templates/clear-media.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd {{install_dir}}/live 4 | RAILS_ENV=production /usr/local/bin/bundle exec rake mastodon:media:clear 5 | -------------------------------------------------------------------------------- /templates/mastodon-sidekiq.conf: -------------------------------------------------------------------------------- 1 | description "mastodon worker services" 2 | 3 | start on filesystem and static-network-up 4 | stop on deconfiguring-networking 5 | respawn 6 | 7 | setuid ubuntu 8 | setgid ubuntu 9 | 10 | script 11 | cd {{install_dir}}/live 12 | HOME={{install_dir}}/live RAILS_ENV=production DB_POOL=5 /home/ubuntu/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q pull -q mailers -q push 13 | end script 14 | -------------------------------------------------------------------------------- /templates/mastodon-streaming.conf: -------------------------------------------------------------------------------- 1 | description "mastodon streaming service" 2 | 3 | start on filesystem and static-network-up 4 | stop on deconfiguring-networking 5 | respawn 6 | 7 | setuid ubuntu 8 | setgid ubuntu 9 | 10 | script 11 | cd {{install_dir}}/live 12 | NODE_ENV=production PORT=4000 npm start 13 | end script 14 | -------------------------------------------------------------------------------- /templates/mastodon-web.conf: -------------------------------------------------------------------------------- 1 | description "mastodon web service" 2 | 3 | start on filesystem and static-network-up 4 | stop on deconfiguring-networking 5 | respawn 6 | 7 | setuid ubuntu 8 | setgid ubuntu 9 | 10 | script 11 | cd {{install_dir}}/live 12 | RAILS_ENV=production PORT=3000 /home/ubuntu/.rbenv/shims/bundle exec puma -C config/puma.rb 13 | end script 14 | -------------------------------------------------------------------------------- /templates/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name {{hostname}}; 4 | 5 | location / { 6 | rewrite ^(.*) https://{{hostname}}$1 permanent; 7 | } 8 | } 9 | 10 | server { 11 | listen 443; 12 | server_name {{hostname}}; 13 | 14 | ssl on; 15 | ssl_certificate {{install_dir}}/certs/your-cert.pem; 16 | ssl_certificate_key {{install_dir}}/certs/your-cert.key; 17 | 18 | ssl_session_timeout 5m; 19 | 20 | ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; 21 | ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; 22 | ssl_prefer_server_ciphers on; 23 | 24 | keepalive_timeout 70; 25 | sendfile on; 26 | client_max_body_size 0; 27 | gzip off; 28 | 29 | root {{install_dir}}/live/public; 30 | 31 | add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; 32 | 33 | location / { 34 | try_files $uri @proxy; 35 | } 36 | 37 | location @proxy { 38 | proxy_set_header Host $host; 39 | proxy_set_header X-Real-IP $remote_addr; 40 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 41 | proxy_set_header X-Forwarded-Proto https; 42 | 43 | proxy_pass_header Server; 44 | 45 | proxy_pass http://localhost:3000; 46 | proxy_buffering off; 47 | proxy_redirect off; 48 | proxy_http_version 1.1; 49 | proxy_set_header Upgrade $http_upgrade; 50 | proxy_set_header Connection "upgrade"; 51 | # this needs 1.13 52 | # proxy_set_header Connection $connection_upgrade; 53 | 54 | tcp_nodelay on; 55 | } 56 | 57 | location /api/v1/streaming { 58 | proxy_set_header Host $host; 59 | proxy_set_header X-Real-IP $remote_addr; 60 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 61 | proxy_set_header X-Forwarded-Proto https; 62 | 63 | proxy_pass http://localhost:4000; 64 | proxy_buffering off; 65 | proxy_redirect off; 66 | proxy_http_version 1.1; 67 | proxy_set_header Upgrade $http_upgrade; 68 | proxy_set_header Connection "upgrade"; 69 | # this needs 1.13 70 | # proxy_set_header Connection $connection_upgrade; 71 | 72 | tcp_nodelay on; 73 | } 74 | 75 | error_page 500 501 502 503 504 /500.html; 76 | } 77 | -------------------------------------------------------------------------------- /templates/refresh-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd {{install_dir}}/live 4 | RAILS_ENV=production /usr/local/bin/bundle exec rake mastodon:push:refresh 5 | -------------------------------------------------------------------------------- /vars.yml: -------------------------------------------------------------------------------- 1 | ## you'll probably want to edit these 2 | 3 | hostname: yourserver.tld 4 | install_dir: /mnt/mastodon 5 | local_certs: /local/path/to/your/certs 6 | git_repo: https://github.com/Gargron/mastodon.git 7 | ruby_version: 2.4.1 8 | 9 | ## unlikely to edit these 10 | 11 | node_version: 6 12 | services: 13 | - web 14 | - sidekiq 15 | - streaming 16 | ppas: 17 | - ppa:brightbox/ruby-ng 18 | - ppa:git-core/ppa 19 | - ppa:mc3man/trusty-media 20 | - ppa:nginx/stable 21 | packages: 22 | - ack-grep 23 | - build-essential 24 | - ffmpeg 25 | - git 26 | - imagemagick 27 | - libpq-dev 28 | - libxml2-dev 29 | - libxslt1-dev 30 | - nginx 31 | - postgresql 32 | - postgresql-contrib 33 | - redis-server 34 | - redis-tools 35 | - pkg-config 36 | - protobuf-compiler 37 | - libprotobuf-dev 38 | - libreadline-dev 39 | - rbenv 40 | cronscripts: 41 | - clear-feeds.sh 42 | - clear-media.sh 43 | - refresh-push.sh 44 | scripts: 45 | - checkout.sh 46 | --------------------------------------------------------------------------------