BLOG.siposdani87

Create alpine based Docker image for Rails 7

Dockerize Ruby on Rails app with optimized image size
By: Dániel Sipos on

This guide shows you the advanced dockerization steps for your Rails app. If you do not have enough knowledge about it, please read the next article from Docker.com: Quick start: Compose and Rails

I use this Dockerfile’s more complex version for my hobby project TrophyMap, so it is a working copy of that.

If you prefer to use Puma or something else, this shouldn’t be an issue, but you can change command script.

Here is the minimal setup for Dockerfile to test on your rail app:

FROM --platform=$BUILDPLATFORM ruby:3.0.4-alpine3.16 AS builder

ARG RAKE_ENV
ENV RAKE_ENV=$RAKE_ENV
ARG RAILS_ENV
ENV RAILS_ENV=$RAILS_ENV
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV
ARG NPM_CONFIG_PRODUCTION
ENV NPM_CONFIG_PRODUCTION=$NPM_CONFIG_PRODUCTION

# Install dependencies:
# - build-base git curl: To ensure certain gems can be compiled
# - bash libstdc++ zlib pkgconfig autoconf automake libtool nasm: Additional build
# - tzdata: Rails 7
# - nodejs npm openjdk8: image optimalization asset install
# - libxml2 libxslt1 gcompat: Nokogiri
# - imagemagick: CarrierWave::MiniMagick
# - gifsicle jpegoptim libjpeg-turbo libjpeg-turbo-utils optipng pngcrush pngquant advancecomp jhead: image_optim
RUN apk update --no-cache 
    && apk add build-base git curl --no-cache 
    && apk add bash libstdc++ zlib pkgconfig autoconf automake libtool nasm --no-cache 
    && apk add tzdata nodejs npm openjdk8 libxml2 libxslt gcompat imagemagick --no-cache 
    && apk add gifsicle jpegoptim libpng libjpeg-turbo libjpeg-turbo-utils optipng pngcrush pngquant --no-cache 
    && apk add advancecomp --no-cache 
        --repository http://dl-cdn.alpinelinux.org/alpine/edge/community 
    && apk add jhead --no-cache 
        --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing 
    && npm install -g --force --unsafe-perm=true --allow-root svgo advpng-bin

ENV INSTALL_PATH /app
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH

COPY .gemrc $INSTALL_PATH
COPY . $INSTALL_PATH

RUN npm install

# RUN bundle config --global frozen 1
RUN bundle config set --local path '/usr/local/bundle'
RUN bundle config set --local with "${RAILS_ENV}"
RUN bundle config set --local without 'development test'
RUN bundle install --jobs=4 --retry=5

RUN bundle exec rake RAILS_ENV=${RAILS_ENV} assets:precompile

FROM ruby:3.0.4-alpine3.16 as bundler

ARG RAKE_ENV
ENV RAKE_ENV=$RAKE_ENV
ARG RAILS_ENV
ENV RAILS_ENV=$RAILS_ENV
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV
ARG NPM_CONFIG_PRODUCTION
ENV NPM_CONFIG_PRODUCTION=$NPM_CONFIG_PRODUCTION

RUN apk update --no-cache 
    && apk add build-base git curl --no-cache 
    && apk add bash libstdc++ zlib pkgconfig autoconf automake libtool nasm --no-cache 
    && apk add tzdata nodejs libxml2 libxslt gcompat imagemagick --no-cache

ENV INSTALL_PATH /app
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH

COPY --from=builder $INSTALL_PATH/.gemrc $INSTALL_PATH/Gemfile $INSTALL_PATH/

RUN bundle config set --local path '/usr/local/bundle'
RUN bundle config set --local with "${RAILS_ENV}"
RUN bundle config set --local without 'development test'
RUN bundle install --jobs=4 --retry=5

RUN rm -rf /usr/local/bundle/cache/*.gem 
    && find /usr/local/bundle/ -name "*.c" -delete 
    && find /usr/local/bundle/ -name "*.o" -delete

FROM ruby:3.0.4-alpine3.16 as runner

ARG RAKE_ENV
ENV RAKE_ENV=$RAKE_ENV
ARG RAILS_ENV
ENV RAILS_ENV=$RAILS_ENV
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV
ARG NPM_CONFIG_PRODUCTION
ENV NPM_CONFIG_PRODUCTION=$NPM_CONFIG_PRODUCTION

RUN apk update --no-cache 
    && apk add bash tzdata nodejs libxml2 libxslt curl imagemagick --no-cache

RUN rm -rf 
       /var/cache/apk/* 
       /var/lib/cache/* 
       /var/lib/log/* 
       /var/tmp/* 
       /tmp/*

ENV INSTALL_PATH /app
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH

COPY --from=bundler /usr/local/bundle /usr/local/bundle

COPY --from=builder $INSTALL_PATH/app $INSTALL_PATH/app
COPY --from=builder $INSTALL_PATH/bin $INSTALL_PATH/bin
COPY --from=builder $INSTALL_PATH/cable $INSTALL_PATH/cable
COPY --from=builder $INSTALL_PATH/config $INSTALL_PATH/config
COPY --from=builder $INSTALL_PATH/db $INSTALL_PATH/db
COPY --from=builder $INSTALL_PATH/lib $INSTALL_PATH/lib
COPY --from=builder $INSTALL_PATH/public $INSTALL_PATH/public
COPY --from=builder $INSTALL_PATH/vendor $INSTALL_PATH/vendor
COPY --from=builder $INSTALL_PATH/config.ru $INSTALL_PATH/Gemfile $INSTALL_PATH/Gemfile.lock $INSTALL_PATH/Rakefile $INSTALL_PATH/

VOLUME ["$INSTALL_PATH/public"]

EXPOSE 3000

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Conclusion

The final size of Docker image is significantly huge about 630Mb, so it is not comparable with modern go/dart app image sizes. The size depends on your app and ruby gems, but the base image and the environment is small and performant.

Share with your friends

Related posts