Deploying Rails Apps to Cloudron
3 min read

Deploying Rails Apps to Cloudron

I use Cloudron to host my personal infrastructure. It has a great selection of self-hosted apps that you can deploy in a few clicks, and I would highly recommend it to others (in fact, here's a referral link).

I also occasionally write small Ruby on Rails apps like pSquare or Enki and want a place to host them, so I spent some timing learning how to deploy them on Cloudron. This post will take you through the steps needed to deploy a Rails app onto Cloudron from scratch.

This assumes you're using Postgres and Redis with your app, though you can use other databases similarly by adapting the steps below.  This also assumes some knowledge of bash scripts and Docker.

Step 1 - Dockerize the Rails app

Add the following files to your app's root directory:

FROM ruby:3.0.0-rc1
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client && \
  curl -sL https://deb.nodesource.com/setup_10.x | bash - && \
  curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update && apt-get install -y nodejs yarn

RUN yarn config set cache-folder /tmp/yarn

RUN bundle config --global frozen 1

WORKDIR /run/psquare
COPY Gemfile /run/app/Gemfile
COPY Gemfile.lock /run/app/Gemfile.lock
RUN bundle install
COPY . /run/app

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

ENV RAIS_ENV production
ENV RAILS_SERVE_STATIC_FILES true 

RUN bundle exec rails assets:precompile

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Dockerfile
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /run/app/tmp/pids/server.pid

# Since we're sort of using the asset pipeline, compile assets
# bundle exec rails assets:precompile

# Migrate database if necessary 
bundle exec rails db:migrate

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
entrypoint.sh

Step 2 - Update application config

Add the following two lines to application.rb to enable the app to find Cloudron's Postgres and Redis:

ENV['DATABASE_URL'] = ENV['CLOUDRON_POSTGRESQL_URL'] if ENV['CLOUDRON_POSTGRESQL_URL'].present?
ENV['REDIS_URL'] = ENV['CLOUDRON_REDIS_URL'] if ENV['CLOUDRON_REDIS_URL'].present?
application.rb

Add the following line to production.rb to set the host to whatever app subdomain you are using in Cloudron.

config.hosts << ENV['CLOUDRON_APP_DOMAIN'] if ENV['CLOUDRON_APP_DOMAIN'].present?
production.rb

Step 3 - Create a CloudronManifest.json file

This manifest file defines the app's metadata in Cloudron along with any addons (in this case we're using Postgres and Redis). Place it in your app's root directory. You may need to modify the memory limit etc based on your app's needs.

{
  "id": "com.morochena.psquare",
  "title": "pSquare",
  "author": "Marcus Orochena <marcus@orochena.net>",
  "description": "pSquare is a simple, fast, and powerful priorization method for projects or stories. It allows you to rank projects by effort and impact, and then prioritize them in a way that is easy to understand.",
  "tagline": "A great beginning",
  "version": "1.0.6",
  "healthCheckPath": "/",
  "httpPort": 3000,
  "memoryLimit": 1048576000,
  "addons": {
    "postgresql": {},
    "redis": {}
  },
  "manifestVersion": 2,
  "website": "https://www.orochena.net",
  "contactEmail": "marcus@orochena.net",
  "tags": [ "project", "project prioritization" ],
  "mediaLinks": [ "https://images.rapgenius.com/fd0175ef780e2feefb30055be9f2e022.520x343x1.jpg" ]
}
CloudronManifest.json

Step 4 - Build and push an image to Docker

Run the following command to build and push an image to Docker, replacing the items in the brackets.

docker build -t {YOUR DOCKER USERNAME}/{YOUR APP NAME}:{VERSION} . && docker push {YOUR DOCKER USERNAME}/{YOUR APP NAME}:{VERSION}

Step 5 - Create your app in Cloudron, deploy it

npm install -g cloudron
cloudron login my.example.com
cloudron install --image {YOUR DOCKER USERNAME}/{YOUR APP NAME}:{VERSION}
cloudron env set --app {YOUR APP NAME}.example.com RAILS_ENV=production RAIlS_SERVE_STATIC_ASSETS=true RAILS_LOG_TO_STDOUT=true`

At this point, you should see your app running at the subdomain you specified.

Step 6 - Update your app

You can run the following commands to update your app as you make changes to it. I put this in an update.sh file in my app's root folder so I can easily deploy changes with a single command.

VERSION={VERSION}
docker build -t {YOUR DOCKER USERNAME}/{YOUR APP NAME}:{VERSION} . && docker push {YOUR DOCKER USERNAME}/{YOUR APP NAME}:{VERSION}
cloudron update --app={YOUR APP NAME} --image={YOUR DOCKER USERNAME}/{YOUR APP NAME}:{VERSION}

That's it!