A fork from alpine-labs/ruby-dev.
The main goal of this project is to have a single Docker image to develop all your Ruby projects, with all dependencies contained inside Docker (like gems, NPM packages or even Ruby itself, that won't pollute your host environment) and without anything specific to the project in the Docker image (the codebase is mounted directly from the host filesystem into the container, thus you'll never have to build the image when you add a gem or change some code).
The Docker container also provides developer-friendly tools and behaviours like persisted Ruby console history (IRB and Pry), shell history, or even auto-installing dependencies (that's right: simply change your Gemfile or package.json and bundle install or yarn install will be run automatically for you and only when necessary). It also provides a few CLI tools to get your hands dirty, but as least as possible: vim, nano, heroku.
The default command (when you just docker-compose up) is to run foreman start, thus starting whatever you put in your Procfile (except the release process, if you have one: it's a reserved process name by Heroku/Dokku). All commands are run inside the container as the same user that owns your codebase (thus probably your host user), which means that any file generated inside the container (think of rails generate, yarn init, or even log files) will be owned by yourself (not by root, like they would with a default Docker configuration).
We try to use sane default conventions so you don't have to think about it, but this image also allows some configuration (e.g. Heroku CLI or Git authentication) and customisation (install extra software inside the container). See the recipes book for more details.
TL;DR 🙄
- your codebase is 2-way-mounted from your host to
/appinside the container- Yarn is configured to store installed modules in
/app/node_modules- Bundler is configured to store installed gems in
/bundle- Rubygems is configured to store installed gems (using
gem install …directly) in/bundle/global- everything ran inside the container is done with your host user UID and GID
bundle installis run before any command, only if necessaryyarn installis run before any command, only if necessary- you can customise the image with extra software
The latest versions built by the Docker Build workflow
are available from Docker Hub and GitHub Container Registry (GHCR). Size badges show
the compressed download size for linux/amd64 from Docker Hub.
Simply create a docker-compose.yml file in your project root directory like this:
version: "3"
volumes:
bundle: { driver: local }
node_modules: { driver: local }
config: { driver: local }
services:
app:
image: hnhs/ruby-dev
ports: ["5000:5000"]
volumes:
- .:/app
- bundle:/bundle
- node_modules:/app/node_modules
- config:/config💡 Feel free to use
hnhs/ruby-dev:<ruby-version>: we support multiple Ruby versions as Docker tags and Alpine Linux variants (append-alpineto image tag).
If you're on MacOS, you'll very likely want to use Docker Sync too.
⚠️ Use your actual application name suffixed with-syncinstead ofyour_app-syncto prevent conflicts between your projects.
-
install it with
gem install docker-sync -
add a
docker-sync.ymlfile:version: "3" syncs: your_app-sync: src: ./ sync_excludes: [log, tmp, .git, .bundle, .idea, node_modules]
-
add the sync container as external container in
docker-compose.yml:volumes: your_app-sync: { external: true }
-
use it by replacing
- ./:/appwith- your_app-sync:/app:nocopyindocker-compose.yml -
start the sync with
docker-sync start
You can now start your project with:
docker-compose upOr run any command (like rake, bash, or whatever else) with:
docker-compose run app [rake|bash|...]💡 Note that you don't need to prefix commands with
bundle exec.
You can even bypass dependencies check/auto-install before the command is run by setting the DISABLE_AUTO_INSTALL_DEPS environment variable from the command-line:
docker-compose run -e DISABLE_AUTO_INSTALL_DEPS=1 app bashYou can customise this image by building your own image based on this one (or any of its tags, by appending them to the FROM step of the Dockerfile), and install additional software on top of it:
-
create a
Dockerfilein your project root folder, and add a build step that installs the APT package you need (other installation methods work too, but it's out of the scope of this documentation):FROM alpinelab/ruby-dev RUN apt-get update \ && apt-get install --assume-yes --no-install-recommends --no-install-suggests \ <INSERT APT PACKAGE NAME HERE> \ && rm -rf /var/lib/apt/lists/*
-
change your
docker-compose.ymlto use thisDockerfile(rather than an upstream image) and to build it on-demand:-
change this line:
image: alpinelab/ruby-dev
-
into this line:
build: .
-
ℹ️ To temporarily install a package inside the container (e.g. for a one-time debugging session), you can simply:
Run a shell as root in the container (notice the empty entrypoint):
docker-compose run --entrypoint= app bashInstall the pakckage using APT (any change will be undone when you close the shell):
apt-get update && apt-get install <your_package>
A wild node_modules directory owned by root may appear in your codebase directory. This is due to Docker creating the destination mount point for the bind mount. It should be solved when we will be able to reliably configure Yarn to use an absolute directory (instead of relative node_modules) outside of the codebase, like we do with Bundler.
The following Ruby versions are not maintained anymore:
- Ruby 2.2 (EOL'd)
The official ruby images do not have a fixed publication time. Docker Library's
version-update jobs
check every six hours (H H/6 * * *, with Jenkins assigning the actual times).
Images are published after changes are reviewed and built, and are also
rebuilt when their parent images change.
The publication pipeline
has several stages, so a source commit or CI completion does not mean an image is available yet.
Our Docker Build workflow checks the published
Ruby tags in its matrix every day at 00:37, 06:37, 12:37, and 18:37 UTC. Each
scheduled run builds only variants whose linux/amd64 base-image digest or source
revision differs from the labels on the corresponding ruby-dev image in either
Docker Hub or GHCR. Changes only to other architectures, index annotations, or
attestation manifests do not trigger builds. Missing images, missing labels, or
an unverifiable downstream image trigger a build; an upstream lookup failure
fails the job instead of reporting an unchanged image.
Builds use the exact base digest checked and record it in the
org.opencontainers.image.base.digest label, alongside
org.opencontainers.image.revision. Comparing both registries also retries
partially published builds. Existing images without these labels rebuild once.
Pushes to main and manual workflow runs always build, so manual runs can also
refresh development packages when the Ruby base is unchanged. Ruby patch versions
remain explicitly selected in the workflow matrix.
Run the change-detection tests with:
python3 -m unittest discover -s .github/scripts -p 'test_*.py'Contributions are indeed warmly welcome as pull requests, or issues ❤️
This project is developed by Alpine Lab and released under the terms of the MIT license.
