mina icon indicating copy to clipboard operation
mina copied to clipboard

Rails 7.1 Credentials in Production

Open TimeTravelerFromNow opened this issue 1 year ago • 1 comments

Mina and Rails 7.1 Credentialling broke my production when I updated to Ruby 3.2.0 and Rails 7.1.3.2

The credentials system in Rails 7.1 manages the secret differently. I'm still learning how they work, but my production deploys would all fail with Rails version 7.1 (at the db:migrate step).

Today I fixed it after watching deanin's credential management video.

Production was down for more than a week before today, I did a lot of guesswork, here's what I remember doing to get production working again:

  1. I had to comment out my mina deploy script at the db:migrate step. (see my script below)
  2. ssh to my server where the app is (under ~/sebsite-live/current for me)
  3. generate credentials the Rails 7.1 way: VISUAL="vim" rails credentials:edit --environment production
  4. pasting the key from the terminal output that appears AFTER SAVING to new file config/master.key in production
  5. running rails setup commands starting with RAILS_ENV=production rails db:migrate rails assets:precompile
  6. restarting (it's a unicorn server process) sebsite-live@vultr:~/app/current$ sudo systemctl restart sebsite-live

How do we automate step 3 in Mina?

The new solution mina should be easy, but safely handle credentials too. I would be happy to contribute in any way I can, if I can. Why don't I just switch to Docker deploys? (See comments below)

ERROR LOG (shortened but this would occur when secrets were not setup in production
       rake aborted!
       ActiveSupport::MessageEncryptor::InvalidMessage: missing separator (ActiveSupport::MessageEncryptor::InvalidMessage)
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/messages/codec.rb:57:in `catch_and_raise'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/message_encryptor.rb:242:in `decrypt_and_verify'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/encrypted_file.rb:109:in `decrypt'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/encrypted_file.rb:72:in `read'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/encrypted_configuration.rb:57:in `read'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/encrypted_configuration.rb:76:in `config'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/encrypted_configuration.rb:95:in `options'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/core_ext/module/delegation.rb:332:in `method_missing'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/railtie.rb:384:in `block (2 levels) in <class:Railtie>'
...
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/zeitwerk-2.6.13/lib/zeitwerk/kernel.rb:34:in `require'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/application.rb:402:in `require_environment!'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/application.rb:588:in `block in run_tasks_blocks'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/sprockets-rails-3.4.2/lib/sprockets/rails/task.rb:61:in `block (2 levels) in define'
       /home/sebsite-live/app/tmp/build-171565374418312/vendor/bundle/ruby/3.2.0/gems/rake-13.2.1/exe/rake:27:in `<top (required)>'
       /usr/local/rvm/gems/ruby-3.2.0/bin/ruby_executable_hooks:22:in `eval'
       /usr/local/rvm/gems/ruby-3.2.0/bin/ruby_executable_hooks:22:in `<main>'
       Tasks: TOP => environment
       (See full trace by running task with --trace)
 !     ERROR: Deploy failed.
-----> Cleaning up build
       Unlinking current
       OK
       Connection to 149.248.16.163 closed.

 !     Run Error

config/deploy.rb

require 'mina/rails'
require 'mina/git'
require 'mina/rvm'

# Basic settings:
#   domain       - The hostname to SSH to.
#   deploy_to    - Path to deploy into.
#   repository   - Git repo to clone from. (needed by mina/git)
#   branch       - Branch name to deploy. (needed by mina/git)

set :application_name, 'sebsite-live'
set :domain, '149.248.16.163'
set :user, fetch(:application_name)
set :deploy_to, "/home/#{fetch(:user)}/app"
set :repository, '[email protected]:TimeTravelerFromNow/sebsite-live.git'
set :branch, 'main'
set :rvm_use_path, '/etc/profile.d/rvm.sh'

# Optional settings:
#   set :user, 'foobar'          # Username in the server to SSH to.
#   set :port, '30000'           # SSH port number.
#   set :forward_agent, true     # SSH forward_agent.

# Shared dirs and files will be symlinked into the app-folder by the 'deploy:link_shared_paths' step.
# Some plugins already add folders to shared_dirs like `mina/rails` add `public/assets`, `vendor/bundle` and many more
# run `mina -d` to see all folders and files already included in `shared_dirs` and `shared_files`
# set :shared_dirs, fetch(:shared_dirs, []).push('public/assets')
set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/secrets.yml')
set :shared_dirs, fetch(:shared_dirs, []).push('public/packs', 'node_modules')

# This task is the environment that is loaded for all remote run commands, such as
# `mina deploy` or `mina rake`.
task :remote_environment do
  ruby_version = File.read('.ruby-version').strip
  raise "Couldn't determine Ruby version: Do you have a file .ruby-version in your project root?" if ruby_version.empty?

  invoke :'rvm:use', ruby_version
end

# Put any custom commands you need to run at setup
# All paths in `shared_dirs` and `shared_paths` will be created on their own.
task :setup do

  in_path(fetch(:shared_path)) do

    command %[mkdir -p config]

    # Create database.yml for Postgres if it doesn't exist
    path_database_yml = "config/database.yml"
    database_yml = %[production:
  database: #{fetch(:user)}
  adapter: postgresql
  pool: 5
  timeout: 5000]
    command %[test -e #{path_database_yml} || echo "#{database_yml}" > #{path_database_yml}]


    # Remove others-permission for config directory
    command %[chmod -R o-rwx config]
  end

end

desc "Deploys the current version to the server."
task :deploy do
  # uncomment this line to make sure you pushed your local branch to the remote origin
  invoke :'git:ensure_pushed'
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    on :link_shared_paths do
      ruby_version = File.read('.ruby-version').strip
      raise "Couldn't determine Ruby version: Do you have a file .ruby-version in your project root?" if ruby_version.empty?

      invoke :'rvm:use', ruby_version
    end

    invoke :'bundle:install'

 # commented out and ran these manually because they would need secrets properly set up.
 #   invoke :'rails:db_migrate'
 #   invoke :'rails:assets_clean'
 #  invoke :'rails:assets_precompile'
    on :launch do
#      command "rake title_to_slug"
      command "sudo systemctl restart #{fetch(:user)}"
    end

  end

  # you can use `run :local` to run tasks on local machine before of after the deploy scripts
  # run(:local){ say 'done' }
end

# For help in making your deploy script, see the Mina documentation:
#
#  - https://github.com/mina-deploy/mina/tree/master/docs

since 2022

Docker Question:

The train of dockerization and deploy seems to have left the station, but maybe... Skipping containerization comes with small performance benefits. Is there a world where we have a second option – to deploy to production without docker? Since 2022 I have used mina to deploy sebastiandetering.com, following Ralf Ebert's Tutorial. Mina is so super cool, Docker is too, but I want to be part of the minority who can deploy without containerizing when we want to.

TimeTravelerFromNow avatar May 14 '24 04:05 TimeTravelerFromNow

I want to be part of the minority who can deploy without containerizing

+1

mihael avatar Jan 28 '25 22:01 mihael

Wanted to close this issue, since it is not an issue with the mina. I can share what I did to ensure my production credentials are on my VPS, so that your Rails 7 or 8 application can start properly when deploying with mina

The hardest part for myself was that in local dev for Rails 7-8, tmp/development_secret.txt is used instead of the one in your credentials file, so trying to reproduce this error on local is pointless

Solution

  1. Make sure your production credentials exist with a valid secret_key_base: somethingsomethingsekret inside the yaml by

EDITOR=nano bin/rails credentials:edit -e=production

  1. if editing credentials fails because the key is incorrect, dont be afraid to remove credentials/production.yml.enc and then try step 1 again (for solo devs)

  2. Ensure your git-ignored production.key makes it to your production rails application. This is how I'm copying production.key to the server with mina:

set :shared_files, fetch(:shared_files, []).push('config/credentials/production.key')
...
task :setup do
...

    production_key_file = 'config/credentials/production.key'
    command %[test -e #{production_key_file} || echo "#{fetch(:production_key)}" > #{production_key_file}]

These lines in my deploy config solves the problem by:

  1. Setting config/credentials/production.key as a shared file so you dont need to re-create the production.key in every deploy release
  2. Copy the production.key contents from your local onto the shared file on your server if it doesnt exist

Linking Deanin's credential management youtube tutorial again for how to use rails credentials:edit in practice For a deep dive on Rails secrets: https://gist.github.com/brianjbayer/9c7782232327287005b8065697c1041a

TimeTravelerFromNow avatar Jul 14 '25 23:07 TimeTravelerFromNow