Any feedback on our Dockerfile to speed up our build time?

Hi guys,

We are currently moving out of Heroku so we need to migrate from buildpack to Docker.
We created a Dockerfile for our Rails app, based on that one: docker-rails-example/Dockerfile at main · nickjj/docker-rails-example · GitHub

Do you have any feedback to improve our build time?
Current build time: ~40min

Here’s our Dockerfile:

FROM ruby:3.1.2-slim-bullseye AS assets

WORKDIR /app

ARG UID=1000
ARG GID=1000

RUN bash -c "set -o pipefail && apt-get update \
  && apt-get install -y --no-install-recommends build-essential curl git libpq-dev cmake pkg-config \
  && curl -sSL https://deb.nodesource.com/setup_19.x | bash - \
  && curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo 'deb https://dl.yarnpkg.com/debian/ stable main' | tee /etc/apt/sources.list.d/yarn.list \
  && apt-get update && apt-get install -y --no-install-recommends nodejs yarn \
  && rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man \
  && apt-get clean \
  && groupadd -g \"${GID}\" ruby \
  && useradd --create-home --no-log-init -u \"${UID}\" -g \"${GID}\" ruby \
  && mkdir /node_modules && chown ruby:ruby -R /node_modules /app"

USER ruby

ARG RACK_ENV
ARG RAILS_ENV
ARG NODE_ENV
ARG NODE_OPTIONS
ARG APPLICATION_NAME
ARG ASSET_HOST
ARG CUSTOM_DOMAINS
ARG DOMAIN
ARG DOMAIN_PORT
ARG RAILS_MASTER_KEY
ARG REACT_APP_HOST_WWW
ARG SUBDOMAIN_ADMIN
ARG SUBDOMAIN_WWW
ARG TZ

ENV RACK_ENV="${RACK_ENV}" \
    RAILS_ENV="${RAILS_ENV}" \
    NODE_ENV="${NODE_ENV}" \
    NODE_OPTIONS="${NODE_OPTIONS}" \
    APPLICATION_NAME="${APPLICATION_NAME}" \
    ASSET_HOST="${ASSET_HOST}" \
    CUSTOM_DOMAINS="${CUSTOM_DOMAINS}" \
    DOMAIN="${DOMAIN}" \
    DOMAIN_PORT="${DOMAIN_PORT}" \
    RAILS_MASTER_KEY="${RAILS_MASTER_KEY}" \
    REACT_APP_HOST_WWW="${REACT_APP_HOST_WWW}" \
    SUBDOMAIN_ADMIN="${SUBDOMAIN_ADMIN}" \
    SUBDOMAIN_WWW="${SUBDOMAIN_WWW}" \
    TZ="${TZ}" \
    PATH="${PATH}:/home/ruby/.local/bin:/node_modules/.bin" \
    USER="ruby"

COPY --chown=ruby:ruby Gemfile* ./
RUN bundle install

COPY --chown=ruby:ruby package.json *yarn* ./
RUN yarn install

COPY --chown=ruby:ruby . .

ENV RACK_ENV=production
ENV NODE_OPTIONS=--max-old-space-size=4096
RUN SECRET_KEY_BASE=`bin/rake secret` bundle exec rails assets:precompile --trace && \
  yarn cache clean && \
  rm -rf node_modules tmp/cache vendor/assets test

###############################################################################

FROM ruby:3.1.2-slim-bullseye

WORKDIR /app

ARG UID=1000
ARG GID=1000

RUN apt-get update \
  && apt-get install -y --no-install-recommends build-essential curl libpq-dev nodejs npm \
  && rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man \
  && apt-get clean \
  && groupadd -g "${GID}" ruby \
  && useradd --create-home --no-log-init -u "${UID}" -g "${GID}" ruby \
  && chown ruby:ruby -R /app

RUN npm install -g mjml
RUN which mjml

USER ruby

COPY --chown=ruby:ruby bin/ ./bin
RUN chmod 0755 bin/*

ARG RACK_ENV
ARG RAILS_ENV
ARG NODE_ENV
ARG APPLICATION_NAME
ARG ASSET_HOST
ARG CORS_ALLOW_ORIGINS
ARG CUSTOM_DOMAINS
ARG DOMAIN
ARG DOMAIN_PORT
ARG RAILS_MASTER_KEY
ARG REACT_APP_HOST_WWW
ARG REACT_FEATURE_TOKEN_BUDGET_SPACE_SLUG
ARG SUBDOMAIN_ADMIN
ARG SUBDOMAIN_WWW
ARG TZ
ARG WEB_CONCURRENCY

ENV RACK_ENV="${RACK_ENV}" \
    RAILS_ENV="${RAILS_ENV}" \
    NODE_ENV="${NODE_ENV}" \
    APPLICATION_NAME="${APPLICATION_NAME}" \
    ASSET_HOST="${ASSET_HOST}" \
    CORS_ALLOW_ORIGINS="${CORS_ALLOW_ORIGINS}" \
    CUSTOM_DOMAINS="${CUSTOM_DOMAINS}" \
    DOMAIN="${DOMAIN}" \
    DOMAIN_PORT="${DOMAIN_PORT}" \
    RAILS_MASTER_KEY="${RAILS_MASTER_KEY}" \
    REACT_APP_HOST_WWW="${REACT_APP_HOST_WWW}" \
    REACT_FEATURE_TOKEN_BUDGET_SPACE_SLUG="${REACT_FEATURE_TOKEN_BUDGET_SPACE_SLUG}" \
    SUBDOMAIN_ADMIN="${SUBDOMAIN_ADMIN}" \
    SUBDOMAIN_WWW="${SUBDOMAIN_WWW}" \
    WEB_CONCURRENCY="${WEB_CONCURRENCY}" \
    TZ="${TZ}" \
    PATH="${PATH}:/home/ruby/.local/bin:/node_modules/.bin" \
    USER="ruby"

COPY --chown=ruby:ruby --from=assets /usr/local/bundle /usr/local/bundle
COPY --chown=ruby:ruby --from=assets /app/public /public
COPY --chown=ruby:ruby . .

ENTRYPOINT ["/app/bin/docker-entrypoint-web"]

EXPOSE 3000

CMD ["rails", "s"]

Thanks a lot for your help :pray:

1 Like

The updated version:

FROM ruby:3.2.2

WORKDIR /app

RUN bash -c "set -o pipefail \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
      build-essential \
      curl \
      git \
      libpq-dev \
      cmake \
      pkg-config \
      postgresql-client-13 \
      sudo \
      cron \
    && curl -sSL https://deb.nodesource.com/setup_19.x | bash - \
    && curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo 'deb https://dl.yarnpkg.com/debian/ stable main' | tee /etc/apt/sources.list.d/yarn.list \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
      yarn \
      nodejs"

# mjml needs to be installed globally
RUN npm install -g mjml
RUN which mjml

# Install Qovery CLI. Used to clone Staging database to preview environment
RUN curl -s https://get.qovery.com | bash
RUN qovery version

ARG RACK_ENV
ARG RAILS_ENV
ARG NODE_ENV
ARG ASSET_HOST
ARG CORS_ALLOW_ORIGINS
ARG CUSTOM_DOMAINS
ARG DOMAIN
ARG DOMAIN_PORT
ARG Q_CLI_ACCESS_TOKEN
ARG RAILS_MASTER_KEY
ARG REACT_APP_HOST_WWW
ARG REACT_FEATURE_TOKEN_BUDGET_SPACE_SLUG
ARG SUBDOMAIN_ADMIN
ARG SUBDOMAIN_WWW
ARG TZ
ARG WEB_CONCURRENCY

ENV RACK_ENV="${RACK_ENV}" \
    RAILS_ENV="${RAILS_ENV}" \
    NODE_ENV="${NODE_ENV}" \
    ASSET_HOST="${ASSET_HOST}" \
    CORS_ALLOW_ORIGINS="${CORS_ALLOW_ORIGINS}" \
    CUSTOM_DOMAINS="${CUSTOM_DOMAINS}" \
    DOMAIN="${DOMAIN}" \
    DOMAIN_PORT="${DOMAIN_PORT}" \
    Q_CLI_ACCESS_TOKEN="${Q_CLI_ACCESS_TOKEN}" \
    RAILS_MASTER_KEY="${RAILS_MASTER_KEY}" \
    REACT_APP_HOST_WWW="${REACT_APP_HOST_WWW}" \
    REACT_FEATURE_TOKEN_BUDGET_SPACE_SLUG="${REACT_FEATURE_TOKEN_BUDGET_SPACE_SLUG}" \
    SUBDOMAIN_ADMIN="${SUBDOMAIN_ADMIN}" \
    SUBDOMAIN_WWW="${SUBDOMAIN_WWW}" \
    WEB_CONCURRENCY="${WEB_CONCURRENCY}" \
    TZ="${TZ}"

COPY Gemfile* ./
RUN bundle install --without development test

COPY package.json *yarn* ./
RUN yarn install

COPY . .

# Precompile Rails assets
ENV NODE_OPTIONS=--max-old-space-size=4096
RUN RACK_ENV=production SECRET_KEY_BASE=`bin/rake secret` bundle exec rails assets:precompile --trace

EXPOSE 3000

CMD ["rails", "s"]

Thanks a lot :pray:

Hi,

This version looks ok. If you still encounter very long build, several options for you:

  1. Create an intermediate image you store somewhere. This version should contain an already built version of your code (even if not 100% up to date, you’ll save time avoiding compiling already built libs, etc…). This intermediate image should be used here instead of ruby:3.2.2̀. It’s not super convenient, I agree, but it works.
  2. Use faster CI like GitHub action or Circle CI, where you can build a faster image on it (because they have a fast cache system) and use the container directly with Qovery instead of the application.
  3. If you already have a CI with tests etc…you may already have built everything you need. At this stage, you have to copy already built things inside a container, push it somewhere and use (like point 2) to a repository, and use containers on Qovery instead of applications. You should save a lot of time here.

Those 3 solutions are already in use by our customers. There is no “best” solution, just pick up the one that best suits your needs :slight_smile:

Pierre

1 Like

Hey Pierre,

Thanks a lot for giving it a look and for your suggestions. 2. and 3. are already in application but suggestion 1. is a good idea I didn’t think about.

I’ll definitely give it a try!

Thanks a lot for your help :pray:

1 Like