Ruby on Rails Deployments

Hey everyone,

I am looking for an example Dockerfile / CircleCI config for deploying a Rails app on Fly (migrating from Heroku)

Thanks in advance!

Here’s an example rails app: https://github.com/superfly/rails-example/blob/master/Dockerfile

1 Like

Thanks @michael - Anything special we need to do on the circle side of things?

@danwetherald I don’t think so, though I don’t have experience with circle’s latest config format. Here’s a few posts that might help though:

1 Like

We also have an example for CircleCI: https://github.com/fly-examples/flygreeting/tree/main/.circleci

The README has pretty good instructions, but you basically use CircleCI like you normally would, then run flyctl deploy after tests pass.

2 Likes

Thanks guys, question, It was a bit confusing when using the heroku buildpack - I am not sure where the server startup command comes from when implementing this.

We are also receiving errors when deploying locally with this buildpack vs the first deploy from the Turboku one click migration.

The heroku buildpack looks for web in the Procfile to discover the startup command.

That error makes me think either tmp or tmp/pids doesn’t exist in the image. Are you trying to migrate your deploy from turboku to flyctl+buildpacks or a Dockerfile?

1 Like

Gotcha, I had a feeling it was using the Procfile - but I was not 100% sure.

I would like to keep it as simple as possible and use the buildpack rather than a long winded Dockerfile. But I can’t seem to get around that tmp/pids issue when deploying with flyctl.

Which buildpack(s) are you using?

[build]
  builder = "heroku/buildpacks:18"

This was the default when using the Turboku tool.

Ah I found something in the heroku docs that says you need to create the tmp/pids directory for Rails 6

1 Like

We actually have already created these dirs locally and are in the repo after talking to @kurt yesterday, still seeing the error.

image

Is that directoy in .gitignore? If it’s not actually in Git our builder won’t pick it up.

Ah, yes the tmp directory is indeed in the .gitignore - I will make the ignore statements a bit more specific to only ignore cache and some other items in the tmp dir and give it another go.

Boom :tada: - That was the answer @kurt - Looks like local builds are now working.

Now that we have a better understanding, we might think about moving to Dockerfile implementation as the build time is pretty slow when running remote builds, this might eat up time on circle - unless we can start the build and then drop the local process?

1 Like

The trick to good build times on Rails is to do it in two parts and cache docker images. We have a an asset + Rubygems build stage (since our JS and CSS changes less frequently than everything else) and then a leaner, Rails only build stage that works on top of that.

If you get to a good Dockerfile + cache, the flyctl deploy will be quick. You can also build the Docker image separately and then deploy it with -i for slightly more control.

Either way, getting CircleCI to cache Docker image layers is the best first step. Our remote builder doesn’t cache layers so it’s always worst case.

Makes sense, this rails app is only configured to be an API so not sure how much would cache on our end would do as we really do not have much assets to compile, etc.

My question would be what is different from when the image is created on Fly vs Heroku - On Heroku this happens in ~10 seconds.

Thanks

Oh let’s get you a Dockerfile then. Rails is easy if you’re not using assets.

See if this works:

######################
# Stage: Gem Builder
FROM ruby:2.6.5 as Builder

ARG BUNDLE_WITHOUT="development test" 
ARG RAILS_ENV="production"

ENV BUNDLE_WITHOUT=${BUNDLE_WITHOUT}

RUN apt-get update && apt-get install -y \
  build-essential \
  postgresql-client \
  git \
  npm \
  tzdata \
  brotli \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Install gems
ADD Gemfile* /app/
RUN bundle config --global frozen 1 \
  && bundle install -j4 --retry 3 \
  # Remove unneeded files (cached *.gem, *.o, *.c)
  && rm -rf /usr/local/bundle/cache/*.gem \
  && find /usr/local/bundle/gems/ -name "*.c" -delete \
  && find /usr/local/bundle/gems/ -name "*.o" -delete

###############################
# Stage Final
FROM ruby:2.6.5

RUN apt-get update && apt-get install -y \
  file \
  postgresql-client \
  tzdata \
  && rm -rf /var/lib/apt/lists/*

# Add user
RUN groupadd -r app && useradd -r -g app app

USER app

# Copy app with gems from former build stage
COPY --from=Builder /usr/local/bundle/ /usr/local/bundle/

USER root
RUN mkdir -p /app/tmp/pids && chown -R app:app /app/tmp
ADD --chown=app:app . /app

# Set Rails env
ENV RAILS_LOG_TO_STDOUT true
ENV RAILS_ENV production

USER app
WORKDIR /app

# Start up
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
2 Likes