This is an example and tutorial to build and run your Rails app on Docker.
Replace `pwd`
with %CD%
in your Command Prompt or PowerShell.
Otherwise you would see an error like following:
docker: Error response from daemon: create `pwd`/app: “
pwd
/app” includes invalid characters for a local volume name, only “[a-zA-Z0-9][a-zA-Z0-9_.-]” are allowed. If you intended to pass a host directory, use absolute path.
You need followings:
- Docker installed
Clone this repository.
$ git clone git@github.com:ginpei/docker-rails-example.git
$ cd docker-rails-example/
Up Docker container.
$ docker-compose up
Open another console and run this to initialize database.
$ cd docker-rails-example/
$ docker-compose exec rails rake db:create db:migrate db:seed
Open in browser.
To stop, in the console where docker-compose
is running, hit Ctrl + C
and wait.
Gracefully stopping... (press Ctrl+C again to force)
Stopping docker-rails-example_rails_1 ... done
Stopping docker-rails-example_db_1 ... done
$
- Prepare a directory for the app
- Run
rails new
in Docker - Describe a Docker image for your app where gems are installed
- Database configuration
- Prepare Docker component file with database specification
- Ignore database files
- Up them
- Stop them (once)
- Open another console
- Initialize database
- Open in browser
We name the app "my-great-app". Please do not forget replace this with your great app's name.
Directory structure would be this:
my-great-app/
+ app/
| - (Rails app files like `config.ru`)
+ db/
| - (Database files)
+ .gitignore
+ docker-compose.yml
+ Dockerfile
+ README.md
$ mkdir my-great-app
$ cd my-great-app
We will be working here.
This step can be broken down into smaller steps.
- Start docker container
rails new
- Create empty
Gemfile.lock
- Expose created files to your host computer
- Exit from docker container
First of all, run a docker container:
$ mkdir app
$ docker run --rm -ti -v `pwd`/app:/app rails:5.0.1 bash
5.0.1
is a tag of the image, as Rails version. Find available tags here:
This should take long time when you try at the first time since Docker downloads a Docker image of Rails. From next time, it would up soon.
Then you'll see you are in docker container. You may want to check the rails version:
root@b4227fbcb3b1:/# rails --version
Rails 5.0.1
root@b4227fbcb3b1:/#
Note that, inside a Docker container, you cannot use Ctrl-P
. This is a special key for Docker.
In the container, run rails new
with some options.
root@b4227fbcb3b1:/# rails new my-great-app --skip-bundle --database=mysql
create
create README.md
create Rakefile
create config.ru
...
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.keep
remove config/initializers/cors.rb
root@b4227fbcb3b1:/#
--skip-bundle
option, as you read, skips bundle install
.
The installation will happen later so you don't do that for now.
--database
option is up to you.
In this tutorial, we chose MySQL as the DBMS for our Rails app.
Since we skipped bundle install
, we need empty lock file for after operation.
$ touch /my-great-app/Gemfile.lock
To expose result to you host computer, move the generated files to the shared volume. And exit.
root@b4227fbcb3b1:/# cp -rT my-great-app/* /app
root@b4227fbcb3b1:/# exit
-T
option of cp
is to copy dot files (.gitignore
).
(The reason we didn't generate files directly in /app
is Rails generates files using the directory name with template. Please let me know if you know the way to do that directly.)
Check the files exist on your computer outside Docker container.
$ ls -a app/
. .gitignore Gemfile.lock Rakefile bin config.ru lib public tmp
.. Gemfile README.md app config db log test vendor
Create a new file named Dockerfile
, which has no extension:
FROM rails:5.0.1
RUN mkdir /app
WORKDIR /app
COPY ./app/Gemfile /app/Gemfile
COPY ./app/Gemfile.lock /app/Gemfile.lock
RUN bundle install
CMD rm /app/tmp/pids/server.pid ; rails s
Although we haven't got database ready yet, modify rails configuration a little.
Change host: localhost
to host: db
in app/config/database.yml
:
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password:
host: db
This db
will be used in the next step as a service name in docker-compose.yml
.
Create a new file named docker-compose.yml
as following:
version: "3"
services:
rails:
build: ./
ports:
- "3000:3000"
volumes:
- ./app:/app
depends_on:
- db
db:
image: mysql
volumes:
- ./db:/var/lib/mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
This prepares a database for root user without password. It's unsafe in some cases but you know this is only for development.
If you use git, create .gitignore
and ignore database files:
/db/
$ docker-compose up
This takes longer time for the first time too.
First, it builds an image from your Dockerfile
for your project. This process includes bundle install
.
Second, after starting, MySQL creates database files in db
.
Take your time until your console gets calm down.
Try to stop the containers by holding Ctrl-C
in the console. Closing process may take time. Be patient.
Gracefully stopping... (press Ctrl+C again to force)
Stopping docker-rails-example_rails_1 ... done
Stopping docker-rails-example_db_1 ... done
$
If you get how to up and end it, up again and move on to the next.
Keep the Docker containers running.
While they are running, you need to open a new console to work.
In the 2nd console just you opened, type following line to create database while running docker-compose up
in the 1st one.
$ docker-compose exec rails rake db:create
Created database 'my-great-app_development'
Created database 'my-great-app_test'
Open http://localhost:3000/
and look at what you have done! Yay! You’re on Rails!
Now it's simple.
$ docker-compose up
And open http://localhost:3000/
.
You may need the 2nd console to run commands like rails g scaffold
.
You may want to run rails
, rake
or any other commands. For those cases, the most basic idea is running docker-compose exec rails xxx
.
Let's say you want to echo
here. You would use this:
$ docker-compose exec rails echo Hello from Docker container!
Please remember you have to type this command into your 2nd console, next the 1st one where your docker-compose up
is running.
$ docker-compose exec rails rails g scaffold post title:string body:text
invoke active_record
create db/migrate/20180806212720_create_posts.rb
create /models/post.rb
...
create /assets/stylesheets/posts.scss
invoke scss
create /assets/stylesheets/scaffolds.scss
Don't forget migration! (See next chapter.)
$ docker-compose exec rails rake db:migrate
== 20180806212720 CreatePosts: migrating ======================================
-- create_table(:posts)
-> 0.0766s
== 20180806212720 CreatePosts: migrated (0.0769s) =============================
$ docker-compose exec rails rake test
Run options: --seed 52703
# Running:
.......
Finished in 1.302024s, 5.3762 runs/s, 6.9123 assertions/s.
7 runs, 9 assertions, 0 failures, 0 errors, 0 skips
When you update Gemfile
, you used to run bundle install
.
With Docker, you need to rebuild your image instead. This process includes bundle install
and updates Gemfile.lock
.
To rebuild, give --force-recreate
option.
$ docker-compose.exe up --build
Otherwise you would see an error like this (you could miss it because the log is too long to read):
rails_1 | /usr/local/lib/ruby/gems/2.3.0/gems/bundler-1.13.7/lib/bundler/resolver.rb:366:in `block in verify_gemfile_dependencies_are_found!': Could not find gem 'carrierwave' in any of the gem sources listed in your Gemfile or available on this machine. (Bundler::GemNotFound)
rails_1 | from /usr/local/lib/ruby/gems/2.3.0/gems/bundler-1.13.7/lib/bundler/resolver.rb:341:in `each'
...
rails_1 | from /usr/local/bundle/bin/rails:15:in `<main>'
These steps should be described in the project's README documentation.
- Clone the repository
- Up Docker containers
- Initialize database
- Start your job
$ git clone ...
$ cd xxx
$ docker-compose up
Hold Ctrl-C
to stop.
Open another console and run:
$ docker-compose exec rails rake db:create db:migrate db:seed
Open http://localhost:3000/
(and another console maybe) and start your job.
Let's rock!