Deploy Rails with PostgreSQL and Sidekiq

How to deploy a Rails application with the PostgreSQL database and Sidekiq workers

Goal

In this tutorial we will deploy a typical Rails 6 application, using PostgreSQL as a database and Sidekiq as an ActiveJob backend for background tasks.

Prepare your Rails application

  1. Web application Dockerfile

    Add a Dockerfile file at the root of your application with the following content:

    FROM ruby:3.0.2-alpine3.13 AS builder
    # Minimal requirements to run a Rails app
    RUN apk add --no-cache --update build-base \
    linux-headers \
    git \
    postgresql-dev=~13 \
    # Rails SQL schema format requires `pg_dump(1)` and `psql(1)`
    postgresql=~13 \
    # Install same version of pg_dump
    postgresql-client=~13 \
    nodejs \
    yarn \
    # Needed for nodejs / node-gyp
    python2 \
    tzdata
    ENV BUNDLER_VERSION 2.2.24
    ENV BUNDLE_JOBS 8
    ENV BUNDLE_RETRY 5
    ENV BUNDLE_WITHOUT development:test
    ENV BUNDLE_CACHE_ALL true
    ENV RAILS_ENV production
    ENV RACK_ENV production
    ENV NODE_ENV production
    ENV APP_PATH /work
    WORKDIR $APP_PATH
    # Gems installation
    COPY Gemfile Gemfile.lock ./
    RUN gem install bundler -v $BUNDLER_VERSION
    RUN bundle config --global frozen 1 && \
    bundle install && \
    rm -rf /usr/local/bundle/cache/*.gem && \
    find /usr/local/bundle/gems/ -name "*.c" -delete && \
    find /usr/local/bundle/gems/ -name "*.o" -delete
    # NPM packages installation
    COPY package.json yarn.lock ./
    RUN yarn install --frozen-lockfile --non-interactive --production
    ADD . $APP_PATH
    RUN SECRET_KEY_BASE=`bin/rake secret` rails assets:precompile --trace && \
    yarn cache clean && \
    rm -rf node_modules tmp/cache vendor/assets test
    FROM ruby:3.0.2-alpine3.13
    RUN mkdir -p /work
    WORKDIR /work
    ENV RAILS_ENV production
    ENV NODE_ENV production
    ENV RAILS_SERVE_STATIC_FILES true
    # Some native extensions required by gems such as pg or mysql2.
    COPY --from=builder /usr/lib /usr/lib
    # Timezone data is required at runtime
    COPY --from=builder /usr/share/zoneinfo/ /usr/share/zoneinfo/
    # Ruby gems
    COPY --from=builder /usr/local/bundle /usr/local/bundle
    COPY --from=builder /work /work
    COPY docker-entrypoint.sh ./
    ENTRYPOINT ["./docker-entrypoint.sh"]
    EXPOSE 3000
    CMD ["rails", "server", "-p", "3000", "-b", "0.0.0.0"]
  2. Sidekiq Dockerfile

    We'll use a similar Dockerfile for our Sidekiq worker. Create a Dockerfile.sidekiq at the root of your repository with the following content:

    FROM ruby:3.0.2-alpine3.13 AS builder
    LABEL maintener='[email protected]'
    # Minimal requirements to run a Rails app
    RUN apk add --no-cache --update build-base \
    linux-headers \
    git \
    postgresql-dev=~13 \
    # Rails SQL schema format requires `pg_dump(1)` and `psql(1)`
    postgresql=~13 \
    # Install same version of pg_dump
    postgresql-client=~13 \
    nodejs \
    yarn \
    # Needed for nodejs / node-gyp
    python2 \
    tzdata
    ENV BUNDLER_VERSION 2.2.24
    ENV BUNDLE_JOBS 8
    ENV BUNDLE_RETRY 5
    ENV BUNDLE_WITHOUT development:test
    ENV BUNDLE_CACHE_ALL true
    ENV RAILS_ENV production
    ENV RACK_ENV production
    ENV NODE_ENV production
    ENV APP_PATH /work
    WORKDIR $APP_PATH
    # Gems installation
    COPY Gemfile Gemfile.lock ./
    RUN gem install bundler -v $BUNDLER_VERSION
    RUN bundle config --global frozen 1 && \
    bundle install && \
    rm -rf /usr/local/bundle/cache/*.gem && \
    find /usr/local/bundle/gems/ -name "*.c" -delete && \
    find /usr/local/bundle/gems/ -name "*.o" -delete
    # NPM packages installation
    COPY package.json yarn.lock ./
    RUN yarn install --frozen-lockfile --non-interactive --production
    ADD . $APP_PATH
    RUN SECRET_KEY_BASE=`bin/rake secret` rails assets:precompile --trace && \
    yarn cache clean && \
    rm -rf node_modules tmp/cache vendor/assets test
    FROM ruby:3.0.2-alpine3.13
    RUN mkdir -p /work
    WORKDIR /work
    ENV RAILS_ENV production
    ENV NODE_ENV production
    ENV RAILS_SERVE_STATIC_FILES true
    # Some native extensions required by gems such as pg or mysql2.
    COPY --from=builder /usr/lib /usr/lib
    # Timezone data is required at runtime
    COPY --from=builder /usr/share/zoneinfo/ /usr/share/zoneinfo/
    # Ruby gems
    COPY --from=builder /usr/local/bundle /usr/local/bundle
    COPY --from=builder /work /work
    COPY docker-entrypoint.sh ./
    CMD ["bundle", "exec", "sidekiq"]
  3. Dockerignore

    In order to avoid unneeded files being copied to your Docker image, you can add a .dockerignore file to the root of your project, with the following content:

    # See https://help.github.com/articles/ignoring-files for more about ignoring files.
    #
    # If you find yourself ignoring temporary files generated by your text editor
    # or operating system, you probably want to add a global ignore instead:
    # git config --global core.excludesfile '~/.gitignore_global'
    # Ignore bundler config.
    /.bundle
    # Ignore all logfiles and tempfiles.
    /log/*
    /tmp/*
    !/log/.keep
    !/tmp/.keep
    # Ignore pidfiles, but keep the directory.
    /tmp/pids/*
    !/tmp/pids/
    !/tmp/pids/.keep
    # Ignore uploaded files in development.
    /storage/*
    !/storage/.keep
    /public/assets
    .byebug_history
    # Ignore master key for decrypting credentials and more.
    /config/master.key
    /public/packs
    /public/packs-test
    /node_modules
    /yarn-error.log
    yarn-debug.log*
    .yarn-integrity
  4. Docker entrypoint

    Finally we will add an entrypoint script that will be called at the start of the application. We'll use it to run the database setup and migration commands.

    You can read more about why this entrypoint is needed here.

    Add a docker-entrypoint.sh file at the root of your project with the following content:

    #! /bin/sh
    bundle exec rake db:migrate
    if [[ $? != 0 ]]; then
    echo
    echo "== Failed to migrate. Running setup first."
    echo
    bundle exec rake db:setup
    fi
    # Execute the given or default command:
    exec "$@"

    Make this script executable:

    chmod +x docker-entrypoint.sh

Deploy your application to Qovery

  1. Create a project

    Now that your Rails application is ready to be dockerized, we can create a project on the Qovery console:

    Qovery console

  2. Create an environment

    Now we'll create an environment. Let's start with our staging environment:

    Qovery console

    Qovery console

  3. Add your Rails app

    We'll now add our Rails app to the environment:

    Qovery console

    On the form you'll need to enter the following information:

    • The app name: it can be whatever you want. Here web.
    • Pick your Git privider, then the repository for your application
    • The branch you want to deploy for this application. We chose main
    • The Root application path. In case your application is not at the root of your repository (e.g. you have a monorepo), otherwise it will be /.
    • For the Build mode, pick Dockerfile.
    • Enter the path to your Dockerfile.

    Qovery console

    Qovery console

    You can then click Create. You'll be redirected to your application dashboard.

    Qovery console

  4. Add a PostgreSQL database

    Our application will use a PostgreSQL database. Let's add one to our environment:

    Click on ADD, then Database

    Qovery console

    • Give a name to your database.
    • For the Type, select POSTGRESQL.
    • For the Mode, we'll pick CONTAINER.
    • Chose the Version you need.

    Qovery console

    You can then click Create

  5. Add a Redis database

    Since we're using Sidekiq, we'll also need a Redis database as a backend.

    If you didn't close the Database modal, you can click the ADD button, then in the dropbox for Database 2 click Create database.

    Qovery console

    Fill the form the same way you did for PostgreSQL:

    Qovery console

    Click Create and close the Databases modal.

    Qovery console

  6. Configure your application ENV variables

    Go back to your environment view:

    Qovery console

    Then click on your application:

    Qovery console

    On your application dashboard, go to Environment variables:

    Qovery console

    Here you can add any environment variable your application needs.

    We'll now configure a few secrets for our application. Click on the Secret variables tab:

    Qovery console

    First since our Demo application uses the Rails Encrypted Secrets, we'll add the RAILS_MASTER_KEY secret Click on CREATE SECRET, then fill the form:

    • Variable: enter the variable name, RAILS_MASTER_KEY.
    • Value: enter the actual value for your RAILS_MASTER_KEY.
    • Scope: chose ENVIRONMENT since the secret will be used by our Sidekiq worker too.

    Qovery console

    Now we'll need to add the DATABASE_URL and REDIS_URL, that Rails will use to connect to PostgreSQL and Redis. Those are secrets as well, since the URLs contain passwords.

    But instead of creating new secrets like we did for the RAILS_MASTER_KEY, we'll use aliases. Aliases are just a way of giving a different name to an existing ENV variable or secret. Since Qovery provides us with the secrets corresponding to the two databases we created earlier, we can alias them.

    First, create an alias to the QOVERY_POSTGRESQL_ZXXXXXXXX_DATABASE_URL_INTERNAL:

    Qovery console

    In the form, chose DATABASE_URL for the alias name and set it at the ENVIRONMENT level:

    Qovery console

    Click Create then do the same thing with a REDIS_URL alias to the QOVERY_REDIS_ZXXXXXXXX_DATABASE_URL_INTERNAL.

    You should see your two aliases created:

    Qovery console

  7. Deploy the environment

    Go back to the staging environment view and deploy it:

    Qovery console

    You should see it switch to the DEPLOYING status. Wait until the status turns to RUNNING.

    Qovery console

    Once your environment is RUNNING, open the web application to see if it works. It will open a new tab showing your application.

    Qovery console

  8. Add the Sidekiq worker

    The last step is to add your Sidekiq Worker. We'll follow the same steps as in the Add your Rails app section with a few differences:

    Add a new application:

    Qovery console

    The settigs are the same as for the Rails application, except:

    • We use the Dockerfile.sidekiq Dockerfile this time
    • We don't declare a port since our worker is not a web service but communicates with our application through Redis.

    Qovery console

    Qovery console

    Click Create.

    If we check the ENV variables and secrets, we notice that it directly inherited the ones we set at the Environment level. So we don't need to do the configuration again.

    Qovery console

    You can now deploy your worker application:

    Qovery console

    Wait for it to switch to the RUNNING status.

Conclusion

You now have a Rails application with PostgreSQL and Sidekiq running on Qovery.