diff --git a/Dockerfile.production b/Dockerfile.production new file mode 100644 index 0000000..819dcb8 --- /dev/null +++ b/Dockerfile.production @@ -0,0 +1,99 @@ +# 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"] \ No newline at end of file