Skip to content

Instantly share code, notes, and snippets.

@zealot128
Created April 22, 2025 08:20
Show Gist options
  • Select an option

  • Save zealot128/3d4cf52406964aec86834ef096c70356 to your computer and use it in GitHub Desktop.

Select an option

Save zealot128/3d4cf52406964aec86834ef096c70356 to your computer and use it in GitHub Desktop.

Revisions

  1. zealot128 created this gist Apr 22, 2025.
    323 changes: 323 additions & 0 deletions apply.diff
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,323 @@
    diff --git a/.dockerignore b/.dockerignore
    new file mode 100644
    index 0000000..325bfc0
    --- /dev/null
    +++ b/.dockerignore
    @@ -0,0 +1,51 @@
    +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
    +
    +# Ignore git directory.
    +/.git/
    +/.gitignore
    +
    +# Ignore bundler config.
    +/.bundle
    +
    +# Ignore all environment files.
    +/.env*
    +
    +# Ignore all default key files.
    +/config/master.key
    +/config/credentials/*.key
    +
    +# Ignore all logfiles and tempfiles.
    +/log/*
    +/tmp/*
    +!/log/.keep
    +!/tmp/.keep
    +
    +# Ignore pidfiles, but keep the directory.
    +/tmp/pids/*
    +!/tmp/pids/.keep
    +
    +# Ignore storage (uploaded files in development and any SQLite databases).
    +/storage/*
    +!/storage/.keep
    +/tmp/storage/*
    +!/tmp/storage/.keep
    +
    +# Ignore assets.
    +/node_modules/
    +/app/assets/builds/*
    +!/app/assets/builds/.keep
    +/public/assets
    +
    +# Ignore CI service files.
    +/.github
    +
    +# Ignore Kamal files.
    +/config/deploy*.yml
    +/.kamal
    +
    +# Ignore development files
    +/.devcontainer
    +
    +# Ignore Docker-related files
    +/.dockerignore
    +/Dockerfile*
    diff --git a/Dockerfile b/Dockerfile
    new file mode 100644
    index 0000000..f2f62d8
    --- /dev/null
    +++ b/Dockerfile
    @@ -0,0 +1,72 @@
    +# 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 testapp .
    +# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name testapp testapp
    +
    +# 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.3.0
    +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
    +
    +# Rails app lives here
    +WORKDIR /rails
    +
    +# Install base packages
    +RUN apt-get update -qq && \
    + apt-get install --no-install-recommends -y curl libjemalloc2 libvips postgresql-client && \
    + rm -rf /var/lib/apt/lists /var/cache/apt/archives
    +
    +# Set production environment
    +ENV RAILS_ENV="production" \
    + BUNDLE_DEPLOYMENT="1" \
    + BUNDLE_PATH="/usr/local/bundle" \
    + BUNDLE_WITHOUT="development"
    +
    +# Throw-away build stage to reduce size of final image
    +FROM base AS build
    +
    +# Install packages needed to build gems
    +RUN apt-get update -qq && \
    + apt-get install --no-install-recommends -y build-essential git libpq-dev libyaml-dev pkg-config && \
    + rm -rf /var/lib/apt/lists /var/cache/apt/archives
    +
    +# Install application gems
    +COPY Gemfile Gemfile.lock ./
    +RUN bundle install && \
    + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    + bundle exec bootsnap precompile --gemfile
    +
    +# 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
    +
    +
    +
    +
    +# Final stage for app image
    +FROM base
    +
    +# Copy built artifacts: gems, application
    +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
    +COPY --from=build /rails /rails
    +
    +# Run and own only the runtime files as a non-root user for security
    +RUN groupadd --system --gid 1000 rails && \
    + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    + chown -R rails:rails db log storage tmp
    +USER 1000:1000
    +
    +# 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"]
    diff --git a/Gemfile b/Gemfile
    index 0f4d642..7c975ac 100644
    --- a/Gemfile
    +++ b/Gemfile
    @@ -28,6 +28,12 @@ gem "solid_queue"
    # Reduces boot times through caching; required in config/boot.rb
    gem "bootsnap", require: false

    +# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
    +gem "kamal", require: false
    +
    +# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
    +gem "thruster", require: false
    +
    # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
    # gem "image_processing", "~> 1.2"

    diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint
    new file mode 100755
    index 0000000..57567d6
    --- /dev/null
    +++ b/bin/docker-entrypoint
    @@ -0,0 +1,14 @@
    +#!/bin/bash -e
    +
    +# Enable jemalloc for reduced memory usage and latency.
    +if [ -z "${LD_PRELOAD+x}" ]; then
    + LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
    + export LD_PRELOAD
    +fi
    +
    +# If running the rails server then create or migrate existing database
    +if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then
    + ./bin/rails db:prepare
    +fi
    +
    +exec "${@}"
    diff --git a/bin/thrust b/bin/thrust
    new file mode 100755
    index 0000000..36bde2d
    --- /dev/null
    +++ b/bin/thrust
    @@ -0,0 +1,5 @@
    +#!/usr/bin/env ruby
    +require "rubygems"
    +require "bundler/setup"
    +
    +load Gem.bin_path("thruster", "thrust")
    diff --git a/config/deploy.yml b/config/deploy.yml
    new file mode 100644
    index 0000000..e278caa
    --- /dev/null
    +++ b/config/deploy.yml
    @@ -0,0 +1,97 @@
    +# Name of your application. Used to uniquely configure containers.
    +service: my-app
    +
    +# Name of the container image.
    +image: my-user/my-app
    +
    +# Deploy to these servers.
    +servers:
    + web:
    + - 192.168.0.1
    + # job:
    + # hosts:
    + # - 192.168.0.1
    + # cmd: bin/jobs
    +
    +# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
    +# If using something like Cloudflare, it is recommended to set encryption mode
    +# in Cloudflare's SSL/TLS setting to "Full" to enable end-to-end encryption.
    +proxy:
    + ssl: true
    + host: app.example.com
    + # kamal-proxy connects to your container over port 80, use `app_port` to specify a different port.
    + # app_port: 3000
    +
    +# Credentials for your image host.
    +registry:
    + # Specify the registry server, if you're not using Docker Hub
    + # server: registry.digitalocean.com / ghcr.io / ...
    + username: my-user
    +
    + # Always use an access token rather than real password (pulled from .kamal/secrets).
    + password:
    + - KAMAL_REGISTRY_PASSWORD
    +
    +# Configure builder setup.
    +builder:
    + arch: amd64
    +
    +# Inject ENV variables into containers (secrets come from .kamal/secrets).
    +#
    +# env:
    +# clear:
    +# DB_HOST: 192.168.0.2
    +# secret:
    +# - RAILS_MASTER_KEY
    +
    +# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
    +# "bin/kamal logs -r job" will tail logs from the first server in the job section.
    +#
    +# aliases:
    +# shell: app exec --interactive --reuse "bash"
    +
    +# Use a different ssh user than root
    +#
    +# ssh:
    +# user: app
    +
    +# Use a persistent storage volume.
    +#
    +# volumes:
    +# - "app_storage:/app/storage"
    +
    +# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
    +# hitting 404 on in-flight requests. Combines all files from new and old
    +# version inside the asset_path.
    +#
    +# asset_path: /app/public/assets
    +
    +# Configure rolling deploys by setting a wait time between batches of restarts.
    +#
    +# boot:
    +# limit: 10 # Can also specify as a percentage of total hosts, such as "25%"
    +# wait: 2
    +
    +# Use accessory services (secrets come from .kamal/secrets).
    +#
    +# accessories:
    +# db:
    +# image: mysql:8.0
    +# host: 192.168.0.2
    +# port: 3306
    +# env:
    +# clear:
    +# MYSQL_ROOT_HOST: '%'
    +# secret:
    +# - MYSQL_ROOT_PASSWORD
    +# files:
    +# - config/mysql/production.cnf:/etc/mysql/my.cnf
    +# - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
    +# directories:
    +# - data:/var/lib/mysql
    +# redis:
    +# image: redis:7.0
    +# host: 192.168.0.2
    +# port: 6379
    +# directories:
    +# - data:/data
    diff --git a/config/environments/production.rb b/config/environments/production.rb
    index 559671c..1749607 100644
    --- a/config/environments/production.rb
    +++ b/config/environments/production.rb
    @@ -24,6 +24,9 @@ Rails.application.configure do
    # Store uploaded files on the local file system (see config/storage.yml for options).
    config.active_storage.service = :local

    + # Assume all access to the app is happening through a SSL-terminating reverse proxy.
    + config.assume_ssl = true
    +
    # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
    config.force_ssl = true

    @@ -37,6 +40,9 @@ Rails.application.configure do
    # Change to "debug" to log everything (including potentially personally-identifiable information!)
    config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")

    + # Prevent health checks from clogging up the logs.
    + config.silence_healthcheck_path = "/up"
    +
    # Don't log any deprecations.
    config.active_support.report_deprecations = false

    diff --git a/config/routes.rb b/config/routes.rb
    index ebedc25..48254e8 100644
    --- a/config/routes.rb
    +++ b/config/routes.rb
    @@ -3,6 +3,7 @@ Rails.application.routes.draw do

    # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
    # Can be used by load balancers and uptime monitors to verify that the app is live.
    + get "up" => "rails/health#show", as: :rails_health_check

    # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
    # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
    --