Excerpt

If you are writing a rails app and using docker to deploy your app. This post is for you.
Here I am sharing the production-ready configuration files that will help you to accelerate your deployment process.
# Setup
Create a folder name docker in your project root directory, Now create two more directories inside it app and web .
```plain text
-app_name
-app
-db
-config
-database.yml
...
-docker
-app
-DockerFile
-web
-DockerFile
-nginx.conf
-docker-compose.yml
```
> The folder structure we have created is just to keep our files in modular way, you can keep it anywhere you want.
# Dockerize your Rails app
Put DockerFile of rails app inside app folder.
```plain text
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs # Set an environment variable where the Rails app is installed to inside of Docker imageENV RAILS_ROOT /var/www/app_name
RUN mkdir -p $RAILS_ROOT # Set working directory
WORKDIR $RAILS_ROOT# Setting env up
ENV RAILS_ENV='production'
ENV RACK_ENV='production' # Adding gems
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lockRUN bundle install --jobs 20 --retry 5 --without development test # Adding project files
COPY . .
RUN bundle exec rake assets:precompile
```
These configurations will install essential system requirements, copy your project to docker container, install gems, precompile your assets.
# Proxy your web requests
We need a reverse proxy, in our case the Nginx web server, to proxy requests to Puma
## DockerFile for Nginx
Put DockerFile of Nginx inside web folder
```plain text
# Base image
FROM nginx# Install dependencies
RUN apt-get update -qq && apt-get -y install apache2-utils# establish where Nginx should look for files
ENV RAILS_ROOT /var/www/app_name# Set our working directory inside the image
WORKDIR $RAILS_ROOT# create log directory
RUN mkdir log# copy over static assets
COPY public public/# Copy Nginx config template
COPY docker/web/nginx.conf /tmp/docker.nginx# substitute variable references in the Nginx config template for real values from the environment
# put the final config in its place# Use the "exec" form of CMD so Nginx shuts down gracefully on SIGTERM (i.e. `docker stop`)
CMD [ "nginx", "-g", "daemon off;" ]
```
## Nginx configuration file
Put nginx.conf inside web folder
```plain text
upstream rails_app {
server app:3000;
} server {
# define your domain
server_name www.example.com; # define the public application root
root $RAILS_ROOT/public;
index index.html; # define where Nginx should write its logs
access_log $RAILS_ROOT/log/nginx.access.log;
error_log $RAILS_ROOT/log/nginx.error.log;
# deny requests for files that should never be accessed
location ~ /\. {
deny all;
} location ~* ^.+\.(rb|log)$ {
deny all;
}
# serve static (compiled) assets directly if they exist (for rails production)
location ~ ^/(assets|images|javascripts|stylesheets|swfs|system)/ {
try_files $uri @rails;
access_log off;
gzip_static on; # to serve pre-gzipped version
expires max;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
break;
}
# send non-static file requests to the app server
location / {
try_files $uri @rails;
} location @rails {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://rails_app;
}
}
```
# Introduce Docker Compose
Since our application will be running across multiple containers it would be nice to control them all as one. That is what Docker Compose does for us. To get our app started with Docker Compose create a file docker-compose.yml in the root of your Rails app.
```plain text
volumes:
postgres_data: {} services:
app:
build:
context: .
dockerfile: ./docker/app/DockerFile
depends_on:
- db db:
image: postgres
volumes:
- postgres_data:/var/lib/postgresql/data web:
build:
context: .
dockerfile: ./docker/web/DockerFile
depends_on:
- app
ports:
- 80:80
```
# Containerize your database
Reference docker container named “db” that we are using to run our Postgres database. you’ll need to update your database.yml similar to this
```plain text
default: &default
adapter: postgresql
encoding: unicode
username: postgres
password:
pool: 5
host: db production:
<<: *default
database: app_name_production
```
At this point, you should be able to build all containers with docker-compose build
Once built you can initialize your DB with docker-compose run app rake db:create RAILS_ENV=production and then populate it using docker-compose run app rake db:migrate db:seed RAILS_ENV=production.
> If you would like your container to run the same executable every time, then you should consider using entrypoint in combination with CMD. See ENTRYPOINT
Now we can finally run the application with docker-compose up -d.
To verify that all three containers are up and running execute docker ps
# Conclusion
With the right Docker setup, a software deployment process can be faster than ever before. Also, you can ensure a consistent environment no matter where the application will run.
# Important Note
If you have any issues regarding the implementation don’t forget to check the below comments Maybe I have already answered your question.
(Help others to find my article on Medium by giving it 👏🏽 below. )