# syntax=docker/dockerfile:1 # check=error=true # This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: # docker build -t myapp . # docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name myapp myapp # For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html # Make sure RUBY_VERSION matches the Ruby version in .ruby-version ARG RUBY_VERSION=3.4.4 FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base # Rails app lives here WORKDIR /rails # Set production environment ENV RAILS_ENV="production" \ BUNDLE_DEPLOYMENT="1" \ BUNDLE_PATH="/usr/local/bundle" \ BUNDLE_WITHOUT="development" # Create non-root user early for security and to allow correct permissions in build stage RUN groupadd --system --gid 1000 rails && \ useradd --system --uid 1000 --gid 1000 --create-home --shell /bin/bash rails # Install base packages (runtime only in base image) RUN apt-get update -qq && \ apt-get install --no-install-recommends -y curl libjemalloc2 libvips42 mariadb-client && \ rm -rf /var/lib/apt/lists/* # Throw-away build stage to reduce size of final image FROM base AS build # Install packages needed to build gems and node modules RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential git libyaml-dev node-gyp pkg-config python-is-python3 libmariadb-dev && \ rm -rf /var/lib/apt/lists/* # Install rails gem in the build stage where build tools are available RUN gem install rails # Install JavaScript dependencies ARG NODE_VERSION=18.19.0 ARG YARN_VERSION=latest ENV NODE_PATH=/usr/local/node ENV PATH=/usr/local/node/bin:$PATH RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \ rm -rf /tmp/node-build-master RUN corepack enable && yarn set version $YARN_VERSION # Copy dependency files first (better caching) COPY Gemfile Gemfile.lock ./ RUN bundle config set --local frozen 'true' && \ bundle install && \ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ bundle exec bootsnap precompile --gemfile # Install node modules COPY package.json yarn.lock ./ RUN yarn install --immutable && \ yarn cache clean --all # Copy application code COPY . . # Precompile bootsnap code for faster boot times RUN bundle exec bootsnap precompile app/ lib/ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile # Clean up build-time dependencies and files RUN rm -rf node_modules tmp/cache # Final stage for app image FROM base # Copy built artifacts: gems and application COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" COPY --from=build /rails /rails COPY --from=build /usr/local/node /usr/local/node # Ensure proper permissions for runtime directories RUN mkdir -p /rails/db /rails/log /rails/storage /rails/tmp && \ chown -R rails:rails /rails/db /rails/log /rails/storage /rails/tmp USER rails # Configure jemalloc for better memory management ENV LD_PRELOAD=libjemalloc.so.2 # Entrypoint prepares the database. ENTRYPOINT ["/rails/bin/docker-entrypoint"] # Start server via Thruster by default, this can be overwritten at runtime EXPOSE 80 CMD ["./bin/thrust", "./bin/rails", "server", "-b", "0.0.0.0"]