Skip to content

Instantly share code, notes, and snippets.

@cwant
Last active March 8, 2024 21:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cwant/8b68c50a4f783be0313aa0b3b930eb85 to your computer and use it in GitHub Desktop.
Save cwant/8b68c50a4f783be0313aa0b3b930eb85 to your computer and use it in GitHub Desktop.
TeSS: Dokku-ization and 12-factor-ization

This guide provides some notes on how I used Dokku to deploy TeSS.

Dokku is a platform that allows easy deployment of apps via git (akin to Heroku) for applications that follow 12-factor principles.

My preferred setup:

  • I like to use docker-compose in development. I follow the guide in the TeSS documentation to get this working.
  • I like to use Dokku in production. It makes zero-downtime upgrades a breeze. Steps below are intended to make this work.
  • I prefer to use Rails credentials to minimize the number of secrets I need to communicate to my app through environment variables
  • I have DNS setup so that I can have my Dokku apps as subdomains of my Dokku deployment host (e.g., training.mydokkuhost.whatever has same IP as mydokkuhost.whatever)

My failings:

  • Issues with the Solr setup documented
  • I don't do anything for Sidekiq (I'm not sure all of the tasks it performs).

Setup

Setup on Dokku remote server

TeSS needs redis, PostGres and Solr. It uses Paperclip for uploads, so it also needs some persistent storage mounted at public/system (note: is there a better way?).

Here I assume that Dokku is installed on the host, and that the application we want to create is called training. I use similar names for the supporting services. Note that linking a service usually just means Inject a very standard environment variable to find the service into the app (e.g. DATABASE_URL, REDIS_URL, etc).

First we create the app:

dokku apps:create training

Next we create the Postgres database and link it to the training app:

sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git
dokku postgres:create training-db
dokku postgres:link training-db training

We now setup Redis and link it to the training app:

sudo dokku plugin:install https://github.com/dokku/dokku-redis.git redis
dokku redis:create training-redis
dokku redis:link training-redis training

Now we setup Solr and connect it to the training app (we'll need to modify the TeSS source a bit to make this work, more on that elsewhere in this guide):

sudo dokku plugin:install https://github.com/dokku/dokku-solr.git solr
dokku solr:create training-solr
dokku solr:link training-solr training

Now we set up the persistent storage for assets:

dokku storage:ensure-directory training
dokku storage:mount training /var/lib/dokku/data/storage/training:/code/public/system

Now to setup some environment variables, one for the rails environment, another for the credentials master key, and one for serving static files (e.g., uploaded logos):

dokku config:set training RAILS_ENV=production
dokku config:set training RAILS_MASTER_KEY=[REDACTED]
dokku config:set training RAILS_SERVE_STATIC_FILES=Yes

I had some problem getting the Dokku Solr service reading the TeSS Solr config. See my comments on this issue. I'm not a Solr expert (and I didn't document this while doing it), but I may have had to do something nasty like uploading the conf to the Dokku host and doing a

sudo cp solr/conf/s* /var/lib/dokku/services/solr/training-solr/data/training_solr/conf
dokku solr:restart training-solr

There are more elegant ways of fixing this (move the xml files, fork the Dokku Solr plugin), but things at least work for me...

With things set up here, we would next want to set up our TeSS source code to have a Dokku git remote (see docs), and we push our code to this remote to deploy (while crossing fingers).

Setup in source code

Here are some steps that I took while getting my app to work:

  • Copy config/tess.example.yml to config/tess.yml and alter .gitignore so that it can be committed to your repository. This file is modified as indicated in the TeSS documentation.
  • Create your credentials file. See bundle exec rails credentials:help for guidance. You will want a master key for your credentials that you will use in production via the environment variable RAILS_MASTER_KEY in production, but in development you can store this in the file config/master.key (.gitignore is already set up mot to commit this file).
  • For the database configuration (config/database.yml), I replace most of settings for the production stanza (host, database, etc.) with the one line: url: <%= ENV['DATABASE_URL'] %> (This variable gets set by linking the Dokku database with the Dokku app.)
  • I'll be honest, I'm not so sure how this works, but in config/initializers/recaptcha.rb I have:
    if Rails.application.credentials
      config.site_key  = Rails.application.credentials.dig(:recaptcha, :sitekey)
      config.site_key  = Rails.application.credentials.dig(:recaptcha, :secret)
    else
      config.site_key  = Rails.application.secrets.recaptcha[:sitekey]
      config.secret_key  = Rails.application.secrets.recaptcha[:secret]
    end
    
  • For the seeds (db/seeds.rb) I have modified it to have:
    username = ENV["ADMIN_USERNAME"] ||
               Rails.application.credentials.dig(:admin, :username)
    email = ENV["ADMIN_EMAIL"] ||
            Rails.application.credentials.dig(:admin, :email)
    password = ENV["ADMIN_PASSWORD"] ||
               Rails.application.credentials.dig(:admin, :password)
    
    if username && email && password
      puts "\nSeeding admin user"
      u = User.find_or_initialize_by(username: username, role: Role.find_by_name('admin'))
      u.update!(email: email, password: password, processing_consent: "1") unless u.persisted?
    end
    
  • I use Sendgrid for sending emails, so in my credentials I have:
    smtp:
    :address: smtp.sendgrid.net
    :user_name: [REDACTED]
    :password: [REDACTED]
    :port: 587
    :authentication: plain
    :enable_starttls_auto: true
    
    In config/environments/production.rb, I have in the section about smtp:
    config.action_mailer.smtp_settings = Rails.application.credentials[:smtp] if Rails.application.credentials.key?(:smtp)
    
    And in config/tess.yml under the production node I have:
    mailer:
      delivery_method: smtp
    
  • Add a app.json file to configure the Dokku deployment:
    {
      "name": "training",
      "description": "Deployment for training",
      "keywords": [],
      "scripts": {
        "dokku": {
          "predeploy": "bundle exec rake assets:precompile",
          "postdeploy": "bundle exec rake db:migrate"
        },
        "postdeploy": "rake db:seed"
      }
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment