rails fly:build fails because it doesn't have access to secrets or env vars

fly deploy crashes for me because it’s evauating my Rails code which depends on the ENV vars. E.g.,

 > [stage-4 6/6] RUN bin/rails fly:build:
#24 4.070 rails aborted!
#24 4.071 KeyError: key not found: "RAILS_LOG_LEVEL"

I’ve created the secrets, but it doesn’t see them. I’ve also tried adding ENV vars to the [env] section of fly.toml and that has no effect.

Has anyone figured this out how to supply ENV vars to fly:build?

If you want to set an environment variable that is only visible at build time this can be done entirely in lib/tasks/fly.rake. For example:

task :build do
  ENV['RAILS_LOG_LEVEL'] = 'info'
  Rake::Task['assets:precompile'].invoke
end

ENV values set in your Dockerfile will be visible to both the build and at deployment.

1 Like

Hmm … I might be able to do this if I restructure my production.rb and initializers. They need access to secrets which I don’t commit to the repo. Like SMTP config.

I could add an ENV var to the rake file signaling e.g. not to set up SMTP because it’s only the build stage…

?

I got around this problem by using Rails credentials and feeding the builder the RAILS_MASTER_KEY. It was a pretty big restructure but we found it ended up being simpler to maintain. YMMV

1 Like

I wonder if I could just read in the ENV in the :build rake task.

The builder won’t have the secrets as ENV variables by default. But if you have an ENV file (.env) or something like that, you most definitely can read it from within the builder. That was our workaround for a small amount of time but it’s definitely not ideal; that’s what pushed us to switch to Rails credentials.

1 Like

Yep - I already have a fly-secrets file which I don’t commit. This works:

  task :build do
    File.read('fly-secrets')
        .split("\n")
        .map { |line| line.split('=') }
        .select { |pair| pair.length == 2 }
        .each { |pair| ENV[pair[0]] = pair[1] }

    Rake::Task['assets:precompile'].invoke
  end

Needing to fake out rails during a docker build is a pretty common thing, and moreover, you probably don’t want secrets avail in your docker build environment. I tend to just set fake values for docker build and then set the real ones at runtime. So, typically you’ll end up with lines like this in your Dockerfile:

ENV RAILS_MASTER_KEY=fake
4 Likes

Oh, that’s an interesting idea.

Yeah that sounds much better than what I’m doing. Lol

Yea, in general it’s probably a better way to go. It’s good to imaging your build environments as ephemeral, eg not places where you should need to have secrets present.