Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Knowledge-Base for getting a Rails5 app with Cable running on dokku v0.5.6

Dokku v0.5.6 Rails workflow

Straight to Maintenance

0. Set up Droplet

Initialize the droplet with dokku app pre-setup
Visit the droplet IP to configure Dokku (keys, domain, virtual domain)

1. Customize Dokku

dokku plugin:install https://github.com/dokku/dokku-postgres postgres
dokku plugin:install https://github.com/dokku/dokku-redis redis

2. Try a test deploy

Deployment Checklist

  1. App is configured for dokku
  2. Dokku is configured to receive a deploy (dokku app, its ENV and services are set up)
  3. git push dokku succeeds
  4. migrate manually with dokku run <app_name> bundle exec rake db:migrate db:seed RAILS_ENV=<env>
  5. Update VHOST file with needed domains
  6. Rebuild/redeploy

  7. See if app's console runs
  8. See if app can be accessed via needed DNS

Follow official deploy guide

1. Create the app on Dokku server first
dokku apps:create ruby-rails-sample
2. install postgres plugin
sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git
3. create the DB service
dokku postgres:create ruby-rails-sample
4. link the app with DB
dokku postgres:link ruby-rails-sample ruby-rails-sample
5. Deploy time!

NB, when you get git errors, run the key add command, so that on the droplet /home/dokku/.ssh/authoreized_keys file contains something like "command="FINGERPRINT=13:9f:e7:8f:58:2e:54:46:19:c8:ae:d8:39:ce:8d:11 NAME="augusts" `cat /home/dokku/.sshcom..."

# you can remove remote with
  git remote rm dokku

# deploy a test app to a name
  git remote add dokku dokku@blogs.creative.gs:ruby-rails-sample
  git push dokku master

# deploy to subdomain
  git remote add dokku dokku@blogs.creative.gs:sub.dokku.me
  git push dokku master
  
# deploy to root domain
  git remote add dokku dokku@blogs.creative.gs:dokku.me
  git push dokku master
  
# In case you need to deploy from non-master branch do
  git push dokku <other_branch>:master

Make Rails app Dokku-friendly

You can learn a lot from the sample rails app for dokku

1. configure the environment file
production.rb https://gist.github.com/Epigene/ea6a1bd070b657189fbe
staging.rb    https://gist.github.com/Epigene/6a2b957285584de0f1c6
2.0 rails_12_factor on v5 apps

Do not use rails_12_factor gem.
Instad set these two ENV options

RAILS_SERVE_STATIC_FILES=true RAILS_LOG_TO_STDOUT=true 
2.1 make sure you have rails_12_factor gem in production
gem 'rails_12factor', group: :production
3. make sure you have a procfile that starts puma
# in /Procfile
web: bundle exec puma -C config/puma.rb
4. make sure you have a decent puma.rb config file
# in /config/puma.rb
workers 2
threads 5, 5

preload_app!

rackup      DefaultRackup
port        ENV['PORT']     || 3000
environment ENV['RAILS_ENV'] || 'development'

on_worker_boot do
  # Worker specific setup for Rails 4.1+
  # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
  ActiveRecord::Base.establish_connection
end
5. (Convenience) Set up automatic migratieon runner

This is a bit of a hack

# in /lib/taks/deploy.rb
# This runs migs during dokku deploy, see https://buildtoship.com/run-migrations-on-dokku-heroku-deployment/
Rake::Task['assets:clean'].enhance do
  puts ">>>>> Running Migs from the enhanced task!"
  Rake::Task['db:migrate'].invoke
end

Setting up Services

Workers

dokku ps:scale <app> web=1 worker=1 clockwork=1

App ENV

Output existing ENV settings

dokku config <app>

Define new settings For Rails apps it is key to define at least SECRET_KEY_BASE and BUILDPACK_URL. See this dokku discussion for buildback necessity

# use command `dokku config:set <app-name> <key>=<value> <another_key>=<value>`
# e.g.
  dokku config:set node-js-app KEY=\"VAL\ WITH\ SPACES\" ANOTHER_KEY="2"
  
# Producton example
  dokku config:set blog.swisslanguages BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby RAILS_ENV=production RACK_ENV=production SECRET_KEY_BASE=d2c7882551be2d54e3eaf0b5e03235096ef8997dbd3490de39d8ee055ae5dea5f210bf3400d3cd97a8cb37431f867da8ca67ad608e3c120ab0dff826455f95fc
  
# Staging example
  dokku config:set blog.swisslanguages RAILS_ENV=staging RACK_ENV=staging SECRET_KEY_BASE=d2c7882551be2d54n3eaf0b5e03235096ef8997dbd3490de39d8ee051ae5dea5f210bf3400d3cd97a8cb37431f867ga8ca67ad608e3c120ab0dff826455f95fc

Git for Private Repos

1. Add the deploy key plugin

Navigate to /home/dokku/.deployment-keys/shared/.ssh and cat the id_rsa.pub, add contens to creative-deployer's ssh-keys

2. Add the host key plugin

Adding github should do the trick

dokku hostkeys:shared:autoadd github.com

Redis + Resque

1. Install the official redis plugin
sudo dokku plugin:install https://github.com/dokku/dokku-redis.git redis
2. Make a redis service (name the same as app for convenience)
# Modify the behavior of creator by setting ENV variables beforehand
# export REDIS_CUSTOM_ENV="PORT=6392"
dokku redis:create staging.app.swisslanguages.com
3. Link the service with the app
dokku redis:link redis_service app_name
5. Boot worker(s) from procfile
# in /Procfile
worker: bundle exec rake resque:work TERM_CHILD=1 RESQUE_TERM_TIMEOUT=10 QUEUE=*

# or multi-worker boot
worker: bundle exec rake resque:workers COUNT=5 QUEUE="*"
6. Enable worker scaling

Consult the documentation for more.

dokku ps:scale creative_invoice_platform web=1 worker=1

Solve logs taking up disk space by removing containers

# in a cron task, sometime at night
dokku ps:restartall && dokku cleanup

Custom NGINX.conf (redirects, exclusions etc)

Consult the official guide

Set up upload path (for assets you want to keep across deploys)

Consider this SO question

mkdir /home/dokku/shared/<app_name>/
sudo chmod 777 -R /home/dokku/shared
sudo chown dokku:dokku -R /home/dokku/shared
dokku docker-options:add myapp deploy "-v /home/dokku/shared/<app_name>/path/in/container:/path/in/container"
dokku docker-options:add myapp run "-v /home/dokku/shared/<app_name>/path/in/container:/path/in/container"

e.g.
dokku docker-options:add uz-veselibu.lv deploy "-v /home/dokku/shared/uz-veselibu.lv/media:/app/public/media"
dokku docker-options:add uz-veselibu.lv run "-v /home/dokku/shared/uz-veselibu.lv/media:/app/public/media"

SSL

Follow the documentation

dokku certs:generate <app> DOMAIN 

Deployment Checks

TODO See http://dokku.viewdocs.io/dokku~v0.5.6/checks-examples/

Production WebSocket

Given your app will be reachable at www.example.com domain and use /cable path for websocket communication..

Configure production environment settings

config.action_cable.url = "ws://ccb.creative.gs/cable"
config.action_cable.mount_path = "/cable"
#config.action_cable.disable_request_forgery_protection = true # this is very lax!
config.action_cable.allowed_request_origins = [/https?:\/\/(\w+\.)*example.com/]

Make WebSocket friendly custom nginx template

# in /nginx.conf.sigil
# THIS FILE IS USED BY DOKKU TO GENERATE APP'S custom nginx.conf

upstream {{ .APP }} {
{{ range .DOKKU_APP_LISTENERS | split " " }}
  server {{ . }};
{{ end }}
}

server {
  listen      [::]:{{ .NGINX_PORT }};
  listen      {{ .NGINX_PORT }};
  server_name {{ .NOSSL_SERVER_NAME }};
  access_log  /var/log/nginx/{{ .APP }}-access.log;
  error_log   /var/log/nginx/{{ .APP }}-error.log;

  location    / {
    proxy_pass  http://{{ .APP }};
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Request-Start $msec;
  }

  location /cable {
    #proxy_pass http://ccb.creative.gs/cable;
    proxy_pass  http://{{ .APP }}/cable;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_read_timeout 90000s;

    proxy_redirect off;
  }

  include {{ .DOKKU_ROOT }}/{{ .APP }}/nginx.conf.d/*.conf;
}

Maintenance

Execute commands in app

  dokku run <app_name> bundle exec rake db:migrate db:seed RAILS_ENV=production
  dokku run <app_name> rails c

Redeploy without changes

  dokku ps:restart <app>
  
  # harder version
  dokku ps:rebuild <app>

Read app server logs

Container app not starting for some reason?

dokku logs <app>
$ docker ps
$ docker logs -f <CONTAINER_ID>

Get shell inside a container (you should never need this!)

# get containers by running `sudo docker ps`
sudo docker run -it dokku/wp:latest /bin/bash

Make sure your ssh key is on server's dokku user

cat ~/.ssh/id_rsa.pub | ssh root@<IP> "sudo sshcommand acl-add dokku <key name>"
# e.g.
cat ~/.ssh/id_rsa.pub | ssh root@146.185.130.153 "sudo sshcommand acl-add dokku deployer"

Clear unused containers

docker ps --filter status=dead --filter status=exited -aq | xargs -r docker rm -v

docker images --no-trunc | grep '<none>' | awk '{ print $3 }' | xargs -r docker rmi

Emergency App rebuild

1. Document used configuration, Unlink app

dokku config <app>
dokku cat <app>/VHOST 

2. Unlink servicesp

dokku redis:unlink <app> <app>
dokku postgres:unlink <app> <app>

3. Destroy and Remake the app

dokku apps:destroy <app>
dokku apps:create <app>

4. Reset configuration

dokku config:set <app> ...
nano <app>/VHOST

5. Link services

dokku redis:link <app> <app>
dokku postgres:link <app> <app>

Remove a container

# SSH onto the server, then execute:
dokku apps:destroy <app>

DB manipulation

Dump production, restore locally

# ssh into server, $ fish, $ dok
# dokku postgres:list
dokku postgres:export <name> > /home/dokku/latest.dump

# scp the dump
  # general
  scp_dokku (scp root@178.62.175.11:/home/dokku/latest.dump ~/Desktop/)

  # staging
  scp root@staging.creative.gs:/home/dokku/latest.dump

# restire the DB from dump, username directive may be unnecessary
pg_restore --verbose --clean --no-acl --no-owner -h localhost -U <username> -d <database> ~/Desktop/latest.dump

## OLD
psql --username=postgres <databasename> < data_base_dump.sql

Dump locally, restore in production

# Dump it in sql form
pg_dump -h localhost --username creative swiss > ~/latest.dump
scp_swiss (scp it to own comp)
# scp it to dokku server
scp ~/Desktop/latest.dump deployer@82.196.9.147:/home/deployer/latest.sql
# restore
dokku postgresql:restore <app> < latest.sql
# if fails, drop the db, run restore, migrate
dokku postgresql:delete <app>
dokku postgresql:create <app>
dokku postgresql:link <app-name> <db-name>
dokku postgresql:restore <app-name> < latest.sql
dokku run <app> bundle exec rake db:migrate db:seed RAILS_ENV=staging
(opt) dokku run <app> bundle exec rake clear:all_redundant_data RAILS_ENV=staging
      
# TO CLEAN DISC, PERMANENTLY removing exited containers
sudo docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs sudo docker rm

Give Staging server trimmed-down Production DB

1. Make a lite dump in production
dumpit_lite
2. Scp the s_latest.dump to local machine
scp NORD:/home/deployer/apps/nordenhealth/s_latest.dump ~/Desktop/

# you may want to see if it works locally
# rake db:drop db:create db:migrate
# psql -U <username> <db> < <file>
# e.g.
# psql -U augusts nord < /Users/augusts/Desktop/s_latest.dump
3. Scp the s_latest.dump from local to staging
# a single file
scp ~/Desktop/s_latest.dump STAGING:/home/dokku

# all files in a directory
scp ~/Desktop/files/* CPP:/home/deployer/apps/
4. Make sure the staging DB can be partially restored
# not the seeds must be skipped
dokku run <app> bundle exec rake db:drop db:create db:migrate RAILS_ENV=staging
5. Run the restore command
# SQL-type dumps
dokku postgres:connect staging.v1.stockholmhealth < /home/dokku/s_latest.dump

# binary dumps
dokku postgres:import <app> < /home/dokku/s_latest.dump
@jschee
Copy link

jschee commented Oct 17, 2020

Thank you. I appreciate this beast of a gist. Please let me know how I can treat you to a coffee!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment