Getting Started with Ruby on Rails Web App
A live example is available at https://rails.nullstone.dev.
This quickstart launches a Ruby on Rails Web App to AWS via Nullstone. It also configures a local development environment using Docker that works identical to production, but with debugging enabled.
This quickstart contains a walkthrough for generating a Rails app. A working example is available to fork at nullstone-io/rails6-webapp-quickstart.
TIP
This quickstart is based off the official Getting Started with Rails guide
Create Rails app
Generate app
In this example, we are going to create a rails web app in the current directory. In your repository root, run this command.
rails new --database=postgresql .
This will create the following files and directories:
.
├── app
├── bin
├── config
├── db
├── lib
├── log
├── public
├── storage
├── tmp
├── vendor
├── .browserslistrc
├── .gitattributes
├── babel.config.js
├── config.ru
├── Gemfile
├── Gemfile.lock
├── package.json
├── postcss.config.js
├── Rakefile
└── yarn.lock
Configure Datastores
A Rails app is not very interesting without a database. In this section, you will configure additional datastores.
Postgresql
In order for your Rails app to connect to postgresql, you need to change the postgresql configuration.
- Open
config/database.yml
and update thedefault
section to useDB_ADAPTER
andDATABASE_URL
environment variables.
default: &default
adapter: <%= ENV.fetch("DB_ADAPTER") { 'postgresql' } %>
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
url: <%= ENV['DATABASE_URL'] %>
- Keep
config/database.yml
open and change theproduction
section to use alldefault
settings.
production:
<<: *default
# database: rails6_webapp_quickstart_production
# username: rails6_webapp_quickstart
# password: <%= ENV['RAILS6_WEBAPP_QUICKSTART_DATABASE_PASSWORD'] %>
Prepare for Local
Configure logging
When running apps in a container, it's recommended that you emit logs to stdout. To set this up, we're going to log to stdout if RAILS_LOG_TO_STDOUT
environment variable is present.
The nullstone/rails
images set this environment variable. By default, the Rails production environment handles this, but the development environment does not.
To configure, open ./config/environments/development.rb
and add the following anywhere inside the configuration block.
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
if ENV["RAILS_LOG_TO_STDOUT"].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
Configure docker locally
TIP
Nullstone provides a docker image nullstone/rails:local
that is configured for local development. The source for the docker image is on GitHub at nullstone-io/docker-rails.
Now, create a docker-compose.yml
to run locally using Docker with an attached postgres Docker container.
version: "3.8"
services:
app:
image: nullstone/rails:webapp-local
volumes:
- .:/app
- gems:/usr/local/bundle
- node_modules:/app/node_modules
ports:
- "9000:9000"
environment:
- NULLSTONE_ENV=local
- POSTGRES_URL=postgres://postgres:postgres@db:5432/app
depends_on:
- db
db:
image: "postgres:13"
ports:
- "5432:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=app
volumes:
gems: {}
node_modules: {}
Let's start our app locally.
docker compose up
Visit http://localhost:9000.
TIP
Even though this is running in docker, debugging and hot-reloading is enabled similar to your local environment. When you make changes to code, there is no need to rebuild/restart your docker container.
Update dependencies
As you add dependencies to your application, ensure that there is an entry for the dependency in Gemfile
. Then, restart your docker container with docker compose up
or docker compose restart
. The local docker image will install dependencies on boot.
Prepare for Production
Before deploying a Rails app to production, we need to dockerize the app. In this section, we will create a simple Dockerfile
.
Create Dockerfile
# syntax=docker/dockerfile:1
FROM nullstone/rails:webapp
# Install packages
COPY Gemfile* .
COPY package.json .
COPY yarn.lock .
RUN gem install bundler
RUN bundle install
RUN yarn install
# Copy in code, precompile static assets
COPY . .
RUN SECRET_KEY_BASE=1 bundle exec rake assets:precompile
Launch to Nullstone
Create app
When launching to Nullstone, we're going to create an app in the Nullstone UI and attach capabilities that automatically configure our app. Follow these steps in the Nullstone UI.
- Create an application.
- Name: In this example, we're naming our app
rails-webapp-quickstart
- Framework:
rails
- App Type:
Container
- Name: In this example, we're naming our app
- From the Domains tab for the application, add a subdomain. (This will automatically attach a load balancer capability)
- From the Capabilities tab for the application, add a capability named
SECRET_KEY for Rails Cookies
. - From the Capabilities tab for the application, add a capability named
Nginx Sidecar for Fargate Service
.
Create postgresql datastore
- Create a datastore -
RDS Postgres Cluster
- Visit your application created in the previous step.
- From the Datastores tab, add the datastore you just created.
Provision
Our application is ready to launch. Click "Launch" through the UI or issue up
through the CLI.
nullstone up --wait --app=rails-webapp-quickstart --env=dev
Build
Once your application is provisioned, you may build and deploy your app.
You can name your image whatever you want, just remember this image name for the deploy step. In this example, we are using an image name of rails-webapp
.
docker build -t rails-webapp .
Deploy
Now, issue launch
to push your docker image and deploy the service with a new version.
nullstone launch --source=rails-webapp --app=rails-webapp-quickstart --env=dev
Troubleshooting
Auto-versioning
When pushing your image, Nullstone performs auto-versioning if you are in a git-tracked directory. Nullstone selects the short commit SHA (a unique 8-character token) from the git repository to tag the docker image.
To use a manual version, issue launch with --version
(this example uses 1.0.0
).
nullstone launch --source=rails-webapp --app=rails-webapp-quickstart --env=dev --version=1.0.0
Version conflicts
Nullstone enforces version/image tag immutability for security reasons.
If you repeatedly push a new docker image without committing anything to git, you will receive an error message like this:
error pushing artifact: error pushing image: tag invalid: The image tag 'c3c7cd83' already exists in the 'periwinkle-louse-fkslv' repository and cannot be overwritten because the repository is immutable.
The easiest way to resolve this is to launch with an indexed version. The following uses the same commit sha, but with a -2
suffix to distinguish the image tag.
nullstone launch --source=rails-webapp --app=rails-webapp-quickstart --env=dev --version=c3c7cd83-2