Overview
Provides a Rails generator to produce Dockerfiles and related files. This is being proposed as the generator to be included in Rails 7.1, and a substantial number of pull requests along those lines have already been merged. This repository contains fixes and features beyond those pull requests. Highlights:
- Supports all Rails supported releases, not just Rails 7.1, and likely works with a number of previous releases.
- Can be customized using flags on the
generate dockerfile
command, and rerun to produce a custom tailored dockerfile based on detecting the actual features used by your application. - Will set
.node_version
,packageManager
and install gems if needed to deploy your application. - Can produce a
docker-compose.yml
file for locally testing your configuration before deploying.
For more background:
- Motivation - why this generator was created and what problems it is meant to solve
- Demos - scripts to copy and paste into an empty directory to launch demo apps
- Test Results - expected outputs for each test
Usage
Install from the root of your Rails project by running the following.
bundle add dockerfile-rails --optimistic --group development
bin/rails generate dockerfile
The --optimistic
flag will make sure you always get the latest dockerfile-rails
gem when you run bundle update && rails g dockerfile
.
General option:
--force
- overwrite existing files--skip
- keep existing files
If neither are specified, you will be prompted if a file exists with
different contents. If both are specified, --force
takes precedence.
Runtime Optimizations:
--alpine
- use alpine as base image (requires Alpine <= 3.18 OR Rails >= 8.0)--fullstaq
- use fullstaq images on quay.io--jemalloc
- use jemalloc memory allocator--swap=n
- allocate swap space. See falloc options for suffixes--yjit
- enable YJIT optimizing compiler
Build optimizations:
--cache
- use build caching to speed up builds--parallel
- use multi-stage builds to install gems and node modules in parallel
Add/remove a Feature:
--ci
- include test gems in deployed image--compose
- generate adocker-compose.yml
file--max-idle=n
- exit afer n seconds of inactivity. Supports iso 8601 and sleep syntaxes. Uses passenger for now, awaiting puma support.--nginx
- serve static files via nginx. May require--root
on some targets to access/dev/stdout
--thruster
- serve static files via thruster.--link
- add --link to COPY statements. Some tools (like at the moment, buildah) don't yet support this feature.--no-lock
- don't add linux platforms, setBUNDLE_DEPLOY
, or--frozen-lockfile
. May be needed at times to work around a rubygems bug.--sudo
- install and configure sudo to enablesudo -iu rails
access to full environment
Error Tracking & Alerting:
--rollbar
- install gem and a default initializer for Rollbar--sentry
- install gems and a default initializer for Sentry
Add a Database:
Generally the dockerfile generator will be able to determine what dependencies you are actually using. But should you be using DATABASE_URL, for example, at runtime additional support may be needed:
--litefs
- use LiteFS--mysql
- add mysql libraries--postgresql
- add postgresql libraries--redis
- add redis libraries--sqlite3
- add sqlite3 libraries--sqlserver
- add SQL Server libraries
Add a package/environment variable/build argument:
Not all of your needs can be determined by scanning your application. For example, I like to add vim and procps.
--add package...
- add one or more debian packages--arg=name:value
- add a build argument--env=name:value
- add an environment variable--remove package...
- remove package from "to be added" list
Args and environment variables can be tailored to a specific build phase by adding -base
, -build
, or -deploy
after the flag name (e.g --add-build freetds-dev --add-deploy freetds-bin
). If no such suffix is found, the default for arg is -base
, and the default for env is -deploy
. Removal of an arg or environment variable is done by leaving the value blank (e.g --env-build=PORT:
).
Configuration:
--bin-cd
- adjust binstubs to set current working directory autocrlf enabled or may not be able to set bin stubs as executable.--label=name:value
- specify docker label. Can be used multiple times. See LABEL for detail--no-prepare
- omitdb:prepare
. Useful for cloud platforms with release phases--passenger
- use Phusion Passenger under nginx--platform=s
- specify target platform. See FROM for details--variant=s
- dockerhub ruby variant, defaults toslim
. See docker official images for list.--precompile=defer
- may be needed when your configuration requires access to secrets that are not available at build time. Results in larger images and slower deployments.--root
- run application as root--windows
- make Dockerfile work for Windows users that may have setgit config --global core.autocrlf true
--private-gemserver-domain=gems.example.com
- set the domain name of your private gemserver. This is used to tell bundler for what domain to use the credentials of a private gemserver provided via a docker secret--no-precompiled-gems
- compile all gems instead of using precompiled versions
Advanced Customization:
There may be times where feature detection plus flags just aren't enough. As an example, you may wish to configure and run multiple processes.
--instructions=path
- a dockerfile fragment to be inserted into the final document.--migrate=cmd
- a replacement (generally a script) fordb:prepare
/db:migrate
.--no-gemfile-updates
- do not modify my gemfile.--procfile=path
- a Procfile to use in place of launching Rails directly.--registry=another.docker.registry.com
- use a different registry for sourcing Docker images (e.g. public.ecr.aws).
Like with environment variables, packages, and build args, --instructions
can be tailored to a specific build phase by adding -base
, -build
, or -deploy
after the flag name, with the default being -deploy
.
Additionally, if the instructions start with a shebang instead the file being treated as a Dockerfile fragment, the file is treated as a script and a RUN
statement is added to your Dockerfile instead.
Options are saved between runs into config/dockerfile.yml
. To invert a boolean options, add or remove a no-
prefix from the option name.
Testing
A single invocation of rake test:all
will run all of the tests defined. dockerfile-rails has are three types of tests:
rake test:rubocop
runs rubocop using the same options as the Rails codebase.rake test:system
creates a new esbuild application, generates a dockerfile, builds and runs it. As this is time consuming, only one application is tested this way at this time, and a--javascript
example was selected as it exercises a large portion of the features.rake test
runs integration tests, as described below
The current integration testing strategy is to run rails new
and generate dockerfile
with various configurations and compare the generated artifacts with expected results. ARG
values in Dockerfiles
are masked before comparison.
Running all integration tests, or even a single individual test can be done as follows:
rake test
bundle exec rake test TEST=test/test_minimal.rb
bundle exec ruby test/test_minimal.rb
To assist with this process, outputs of tests can be captured automatically. This is useful when adding new tests and when making a change that affects many tests. Be sure to inspect the output (e.g., by using git diff
) before committing.
rake test:capture
If you are running a single test, the following environment variables settings may be helpful:
RAILS_ENV=TEST
will match the environment used to produce the captured outputs.TEST_CAPTURE=1
will capture test results.TEST_KEEP=1
will leave the test app behind for inspection after the test completes.
Historical Links
The following links relate to the coordination between this package and Rails 7.1.
- Preparations for Rails 7.1 - Fly.io's plans and initial discussions with DHH
- Rails Dockerfile futures - rationale for a generator
- Fly Cookbooks - deeper dive into Dockerfile design choices
- app/templates/Dockerfile.tt - current Rails 7.1 template
- Fly.io Cut over to Rails Dockerfile Generator on Sunday 29 Jan 2023
- Fly.io FAQ
- DDH's target
Parallel efforts for Hanami: