heroku / heroku-buildpack-nginx

Run NGINX in a Heroku app
244 stars 787 forks source link

App fails to start on Heroku-22 stack due to lack of erb executable #101

Closed habovh closed 2 years ago

habovh commented 2 years ago

We've been using this buildpack for a while now, and would like to transition from the Heroku-20 stack to the new Heroku-22 stack.

As per the heroku-22 stack documentation/release notes, Ruby is no longer included on the system level.

The Nginx buildpack uses erb to parse the Nginx configuration file, so it needs at least some parts of Ruby:

https://github.com/heroku/heroku-buildpack-nginx/blob/7b37d07896cc8f951a5aeefd00d55d246bf4b4f9/bin/start-nginx#L7-L8

At first I thought we could use the heroku/ruby buildpack to make up for the now-missing erb binary. However, according to the documentation, since our app doesn't actually use Ruby we also don't have a Gemfile, and the build fails because of the failed check from the Ruby buildpack.

Adding empty Gemfile & Gemfile.lock files in our repo seems like quite a hack to include Ruby buildpack, only to be able to use the Nginx buildpack. It also feels quite odd to add such a large dependency ourselves, increasing slug size as well and forcing us to also update the ruby version whenever it reaches its own EOL. For reference, our slug size when using nginx-buildpack only is 191.4M. When adding the ruby buildpack, we're at 213.9M.

Now I know our current heroku-20 stack is nowhere near its end of life (scheduled April 2025), but I'd still like to be able to do a clean update to heroku-22 when able.

Could we somehow include erb in the nginx buildpack to avoid having to installing another buildpack just for the configuration parsing? Would it make sense to create a buildpack that would only include erb to use along nginx-buildpack, if that's even doable? Is the Ruby buildpack the "correct" approach here, even for non-ruby apps, with an empty Gemfile to please the validator?

Here's an excerpt from the app logs when starting on heroku-22 with only the Nginx buildpack.

2022-06-20T08:18:47.166786+00:00 heroku[web.1]: Starting process with command `bin/start-nginx yarn start`
2022-06-20T08:18:48.098961+00:00 app[web.1]: bin/start-nginx: line 8: erb: command not found
2022-06-20T08:18:48.108720+00:00 app[web.1]: buildpack=nginx at=logs-initialized
2022-06-20T08:18:48.109129+00:00 app[web.1]: buildpack=nginx at=app-initialization
2022-06-20T08:18:48.109435+00:00 app[web.1]: buildpack=nginx at=start-app cmd=yarn start
2022-06-20T08:18:50.117432+00:00 app[web.1]: buildpack=nginx at=app-initialized
2022-06-20T08:18:50.117779+00:00 app[web.1]: buildpack=nginx at=nginx-start
2022-06-20T08:18:50.206450+00:00 app[web.1]: nginx: [emerg] no "events" section in configuration
2022-06-20T08:18:50.208053+00:00 app[web.1]: buildpack=nginx at=exit process=nginx
2022-06-20T08:18:50.372744+00:00 heroku[web.1]: State changed from starting to crashed

For people needing to get nginx buildpack work on heroku-22 asap, you can use the following Gemfile and Gemfile.lock along with the Ruby buildpack:

Gemfile:

source "https://rubygems.org"
ruby "3.1.2"

Gemfile.lock:

GEM
  remote: https://rubygems.org/
  specs:

PLATFORMS
  ruby

DEPENDENCIES

RUBY VERSION
   ruby 3.1.2p20

BUNDLED WITH
   1.17.3
edmorley commented 2 years ago

@habovh Hi! Thank you for the report - I agree this isn't ideal at present. This repo currently has no CI (filed #102) :-(

I've started a discussion internally as to how we should proceed. I wasn't aware this buildpack used Ruby for the custom templating feature.

It seems our options are:

  1. Have the nginx buildpack vendor Ruby iff there isn't already a Ruby installed (some apps will have the Ruby buildpack anyway). Though this requires that users have the nginx buildpack after any Ruby buildpack, or they'll still get two copies unnecessarily. And for apps that weren't already using Ruby, they get a larger slug size (albeit still not exactly massive, given nginx is not that big).
  2. Swap the .erb templates for something else, as a breaking change (that's opt-in for older stacks, and mandatory for Heroku-22+).
  3. See if there is any tooling out there that can handle .erb templates that doesn't actually need Ruby (eg a bash/Go/... port).
  4. Just throw in the towel and add Ruby back to the stack image.
habovh commented 2 years ago

@edmorley thanks for the quick reply!

The buildpack would indeed benefit from CI that's for sure 😅

I think that the best short-term approach is the option 1 —just as you planned on #103. Slug size will increase in our case by about 20MB, give or take. I guess that's acceptable for the time being, as long as the slug size is not already nearing the max slug size and the vendoring does not require users to put placeholder Gemfile and Gemfile.lock files in their app. Not having used Ruby in quite some time, it was pretty much a trial-and-error approach to get these two files right to satisfy the ruby buildpack validation.

In the longer run, I could see either option 2 or 3 working fine.

In our case we're mainly using the templating feature of ERB to leverage environment variables and include their values in the generated config. Now, I know ERB can do much more than that so it might be wise to check actual use before going on another templating engine. Regarding option 3 specifically, I have no experience with ports and don't know of any, but if such a port exists and meets the standard it might be a nice lightweight and zero-effort update for users to switch to the new heroku-22 stacks.

edmorley commented 2 years ago

@habovh The latest published version of this buildpack will now work on Heroku-22 without the Ruby buildpack having been manually added to the app's buildpacks list. An empty Gemfile / Gemfile.lock are also no longer required in the app source.

habovh commented 2 years ago

@edmorley nice! Seems to work as expected on our end as well. Thanks for the quick reaction time! I'll be monitoring the buildpack in case you decide to go with another solution down the road.