From ab4f5f5da5229bbc33f3c86815eaf1e057c697b1 Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Tue, 17 Jan 2017 22:00:03 +1100 Subject: [PATCH] Add Heroku deployment support --- Procfile | 1 + README.md | 13 +++++ app.json | 91 +++++++++++++++++++++++++++++++ config/cable.yml | 2 +- config/environments/production.rb | 12 +++- config/initializers/redis.rb | 1 + config/initializers/sidekiq.rb | 5 +- config/puma.rb | 5 ++ 8 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 Procfile create mode 100644 app.json diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..c2c566e8c --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: bundle exec puma -C config/puma.rb diff --git a/README.md b/README.md index 7e95da41f..4ba402379 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,19 @@ Which will re-create the updated containers, leaving databases and data as is. D Docker is great for quickly trying out software, but it has its drawbacks too. If you prefer to run Mastodon without using Docker, refer to the [production guide](https://github.com/tootsuite/mastodon/wiki/Production-guide) for examples, configuration and instructions. +## Deployment on Heroku (experimental) + +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) + +Mastodon can theoretically run indefinitely on a free [Heroku](https://heroku.com) app. It should be noted this has limited testing and could have unpredictable results. + +1. Click the above button. +2. Fill in the options requested. + * You can use a .herokuapp.com domain, which will be simple to set up, or you can use a custom domain. If you want a custom domain and HTTPS, you will need to upgrade to a paid plan (to use Heroku's SSL features), or set up [CloudFlare](https://cloudflare.com) who offer free "Flexible SSL" (note: CloudFlare have some undefined limits on WebSockets. So far, no one has reported hitting concurrent connection limits). + * You will want Amazon S3 for file storage. The only exception is for development purposes, where you may not care if files are not saaved. Follow a guide online for creating a free Amazon S3 bucket and Access Key, then enter the details. + * If you want your Mastodon to be able to send emails, configure SMTP settings here (or later). Consider using [Mailgun](https://mailgun.com) or similar, who offer free plans that should suit your interests. +3. Deploy! The app should be set up, with a working web interface and database. You can change settings and manage versions from the Heroku dashboard. + ## Development with Vagrant A quick way to get a development environment up and running is with Vagrant. You will need recent versions of [Vagrant](https://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) installed. diff --git a/app.json b/app.json new file mode 100644 index 000000000..c0579d33e --- /dev/null +++ b/app.json @@ -0,0 +1,91 @@ +{ + "name": "Mastodon", + "description": "A GNU Social-compatible microblogging server", + "repository": "https://github.com/tootsuite/mastodon", + "logo": "https://github.com/tootsuite/mastodon/raw/master/app/assets/images/logo.png", + "env": { + "HEROKU": { + "description": "Leave this as true", + "value": "true", + "required": true + }, + "LOCAL_DOMAIN": { + "description": "The domain that your Mastodon instance will run on (this can be appname.herokuapp.com or a custom domain)", + "required": true + }, + "LOCAL_HTTPS": { + "description": "Will your domain support HTTPS? (Automatic for herokuapp, requires manual configuration for custom domains)", + "value": "false", + "required": true + }, + "PAPERCLIP_SECRET": { + "description": "The secret key for storing media files", + "generator": "secret" + }, + "SECRET_KEY_BASE": { + "description": "The secret key base", + "generator": "secret" + }, + "SINGLE_USER_MODE": { + "description": "Should the instance run in single user mode? (Disable registrations, redirect to front page)", + "value": "false", + "required": true + }, + "S3_ENABLED": { + "description": "Should Mastodon use Amazon S3 for storage? This is highly recommended, as Heroku does not have persistent file storage (files will be lost).", + "value": "true", + "required": false + }, + "S3_BUCKET": { + "description": "Amazon S3 Bucket", + "required": false + }, + "S3_REGION": { + "description": "Amazon S3 region that the bucket is located in", + "required": false + }, + "AWS_ACCESS_KEY_ID": { + "description": "Amazon S3 Access Key", + "required": false + }, + "AWS_SECRET_ACCESS_KEY": { + "description": "Amazon S3 Secret Key", + "required": false + }, + "SMTP_SERVER": { + "description": "Hostname for SMTP server, if you want to enable email", + "required": false + }, + "SMTP_PORT": { + "description": "Port for SMTP server", + "required": false + }, + "SMTP_LOGIN": { + "description": "Username for SMTP server", + "required": false + }, + "SMTP_PASSWORD": { + "description": "Password for SMTP server", + "required": false + }, + "SMTP_DOMAIN": { + "description": "Domain for SMTP server. Will default to instance domain if blank.", + "required": false + } + }, + "buildpacks": [ + { + "url": "heroku/nodejs" + }, + { + "url": "heroku/ruby" + } + ], + "scripts": { + "postdeploy": "bundle exec rails db:migrate && bundle exec rails db:seed" + }, + "addons": [ + "heroku-postgresql", + "heroku-redis" + ] +} \ No newline at end of file diff --git a/config/cable.yml b/config/cable.yml index 978f721af..34759a772 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -7,4 +7,4 @@ test: production: adapter: redis - url: redis://<%= ENV['REDIS_HOST'] || 'localhost' %>:<%= ENV['REDIS_PORT'] || 6379 %>/1 + url: redis://<%= ENV['REDIS_PASSWORD'] ? ':' + ENV['REDIS_PASSWORD'] + '@' : '' %><%= ENV['REDIS_HOST'] || 'localhost' %>:<%= ENV['REDIS_PORT'] || 6379 %>/1 diff --git a/config/environments/production.rb b/config/environments/production.rb index 9254d494c..8b8d974b3 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -45,10 +45,20 @@ Rails.application.configure do # Use a different logger for distributed setups. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + # Parse and split the REDIS_URL if passed (used with hosting platforms such as Heroku). + # Set ENV variables because they are used elsewhere. + if ENV['REDIS_URL'] + redis_url = URI.parse(ENV['REDIS_URL']) + ENV['REDIS_HOST'] = redis_url.host + ENV['REDIS_PORT'] = redis_url.port.to_s + ENV['REDIS_PASSWORD'] = redis_url.password + end + # Use a different cache store in production. config.cache_store = :redis_store, { host: ENV.fetch('REDIS_HOST') { 'localhost' }, port: ENV.fetch('REDIS_PORT') { 6379 }, + password: ENV.fetch('REDIS_PASSWORD') { false }, db: 0, namespace: 'cache', expires_in: 20.minutes @@ -85,7 +95,7 @@ Rails.application.configure do :address => ENV['SMTP_SERVER'], :user_name => ENV['SMTP_LOGIN'], :password => ENV['SMTP_PASSWORD'], - :domain => config.x.local_domain, + :domain => ENV['SMTP_DOMAIN'] || config.x.local_domain, :authentication => :plain, } diff --git a/config/initializers/redis.rb b/config/initializers/redis.rb index 3825710b8..3660c4a9b 100644 --- a/config/initializers/redis.rb +++ b/config/initializers/redis.rb @@ -3,5 +3,6 @@ Redis.current = Redis.new( host: ENV.fetch('REDIS_HOST') { 'localhost' }, port: ENV.fetch('REDIS_PORT') { 6379 }, + password: ENV.fetch('REDIS_PASSWORD') { false }, driver: :hiredis ) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 63fdb3f16..ecdd07b08 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,10 +1,11 @@ host = ENV.fetch('REDIS_HOST') { 'localhost' } port = ENV.fetch('REDIS_PORT') { 6379 } +password = ENV.fetch('REDIS_PASSWORD') { false } Sidekiq.configure_server do |config| - config.redis = { host: host, port: port } + config.redis = { host: host, port: port, password: password} end Sidekiq.configure_client do |config| - config.redis = { host: host, port: port } + config.redis = { host: host, port: port, password: password } end diff --git a/config/puma.rb b/config/puma.rb index ad2dbfffd..e6b0da91b 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -40,6 +40,11 @@ preload_app! # cannot share connections between processes. # on_worker_boot do + + if ENV["HEROKU"] #Spwan the workers from Puma, to only use one dyno + @sidekiq_pid ||= spawn('bundle exec sidekiq -q default -q mailers -q push') + end + ActiveRecord::Base.establish_connection if defined?(ActiveRecord) end