Skip to content

Instantly share code, notes, and snippets.

@Melrt
Last active November 14, 2025 14:19
Show Gist options
  • Select an option

  • Save Melrt/a3dc3d55db2c225b643032f1fefea031 to your computer and use it in GitHub Desktop.

Select an option

Save Melrt/a3dc3d55db2c225b643032f1fefea031 to your computer and use it in GitHub Desktop.
# syntax=docker/dockerfile:1.7-labs
ARG RUBY_VERSION
ARG DISTRO_NAME=bullseye
FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME AS base
SHELL ["/bin/bash", "-c"]
RUN echo "IRB.conf[:HISTORY_FILE] = ENV['IRB_HISTFILE']" >> ~/.irbrc
ARG DISTRO_NAME
# Common dependencies
# Using --mount to speed up build with caching, see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#run---mount
ARG PACKAGES_TO_INSTALL=""
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
rm -f /etc/apt/apt.conf.d/docker-clean; \
apt-get update -qq && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
curl \
file \
libjemalloc2 \
libpq5 \
shared-mime-info \
$(echo $PACKAGES_TO_INSTALL | xargs -n1 | sort -u | xargs) \
&& rm -rf /var/lib/apt/lists/*
ARG RAILS_ENV
# Ruby/Rails env configuration
ENV RAILS_ENV=$RAILS_ENV \
NODE_ENV=production \
BUNDLE_PATH=/usr/local/bundle \
PATH="/home/my_user/app/bin:${PATH}" \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
LD_PRELOAD=libjemalloc.so.2 \
MALLOC_CONF=narenas:2 \
# Upgrade RubyGems if possible
RUN gem update --system --no-document;
FROM base AS production-builder
# Rails app lives here
WORKDIR /rails
# buildr dependencies
# Using --mount to speed up build with caching, see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#run---mount
ARG PACKAGES_TO_INSTALL=""
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
rm -f /etc/apt/apt.conf.d/docker-clean; \
apt-get update -qq && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
build-essential \
libpq-dev \
libyaml-dev \
libssl-dev \
libz-dev \
&& rm -rf /var/lib/apt/lists/*
# Install NodeJS and NPM from Nodesource
ARG NODE_MAJOR=21
RUN echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \
&& echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \
&& echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences \
&& curl -sSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g yarn
# Then, we re-configure Bundler
ENV RAILS_ENV=$RAILS_ENV \
BUNDLE_JOBS=4 \
BUNDLE_RETRY=3
# Copy code
COPY Gemfile Gemfile.lock ./
RUN mkdir -p $BUNDLE_PATH \
&& bundle config --local path "${BUNDLE_PATH}" \
&& bundle config set clean "true" \
&& bundle config set no-cache "true" \
&& bundle config --local without "development:test" \
&& bundle install --jobs=${BUNDLE_JOBS} \
&& rm -rf $BUNDLE_PATH/.bundle $BUNDLE_PATH/ruby/*/cache/* $BUNDLE_PATH/ruby/*/bundler/gems/*/.git
# Copy application code
COPY . .
# Copy database config to avoid human errors
COPY .dockerdev/database.yml.template config/database.yml
# Install JS packages
RUN yarn install --check-files
# Precompile assets
# NOTE: The app may require some environment variables (e.g., SECRET_KEY_BASE). All known mandatory variables
# are listed with fake values in .dockerdev/env_variables.sh script and exported below
RUN ( \
source .dockerdev/.env \
&& env \
&& env_file_name=./config/environments/${RAILS_ENV}.rb \
&& echo 'Rails.application.config.require_master_key = false' >> $env_file_name \
&& bundle exec rake assets:clobber assets:precompile \
&& sed -i '$ d' $env_file_name \
)
# Finally, our production image definition
FROM base AS production
SHELL ["/bin/bash", "-c"]
WORKDIR "/home/my_user/app"
# Install NodeJS and NPM from Nodesource
ARG NODE_MAJOR=21
ARG FORCE_NODE=""
RUN test -n "$FORCE_NODE" \
&& echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \
&& echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \
&& echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences \
&& curl -sSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g yarn \
|| echo "nodejs not mandatory for production environment"
# Copy application code
COPY . .
# Configure Rails to allow public file server served by app server
RUN sed -i 's/\(.*public_file_server\.enabled.*= \).*/\1true/' config/environments/${RAILS_ENV}.rb
# Copy database config to avoid human errors
COPY .dockerdev/database.yml.template /home/my_user/app/config/database.yml
# Copy artifacts
# 1) Installed gems
COPY --from=production-builder $BUNDLE_PATH $BUNDLE_PATH
# 2) Compiled assets
COPY --from=production-builder /rails/publi[c]/asset[s]/ ./public/assets
# pack[s] is on purpose. Using glob pattern to avoid error if folder does not exists
COPY --from=production-builder /rails/publi[c]/pack[s]/ ./public/packs
# 3) We can even copy the Bootsnap cache to speed up our Rails server load!
COPY --from=production-builder /rails/tm[p]/cach[e]/bootsnap* ./tmp/cache/bootsnap
# Force creation of tmp and log folders if not exists
RUN mkdir -p /home/my_user/app/tmp
# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 my_user && \
useradd my_user --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
chown -R my_user:my_user /home/my_user
USER 1000:1000
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment