Deploy Rails on Bahriya

Ruby on Rails is a mature, convention-driven web framework. In production it runs behind the Puma application server, which listens on port 3000 by default. This guide packages a Rails application as a container image and deploys it as an HTTP container.

Updated 3 Aug 20263 min read

Ruby on Rails is a mature, convention-driven web framework. In production it runs behind the Puma application server, which listens on port 3000 by default. This guide packages a Rails application as a container image and deploys it as an HTTP container.

Only two things change between stacks on Bahriya: the Dockerfile that builds your image, and the port your application listens on. For Rails served by Puma, that port is 3000.

Dockerfile

This installs your gems, precompiles assets, and starts Puma bound to 0.0.0.0:3000 so the platform can route requests to it.

FROM ruby:3.3-slim
 
# Build tools and libraries Rails commonly needs
RUN apt-get update -qq \
    && apt-get install -y --no-install-recommends build-essential libpq-dev nodejs \
    && rm -rf /var/lib/apt/lists/*
 
ENV RAILS_ENV=production \
    BUNDLE_DEPLOYMENT=1 \
    BUNDLE_WITHOUT=development:test
 
WORKDIR /app
 
# Install gems first so they cache across builds
COPY Gemfile Gemfile.lock ./
RUN bundle install
 
COPY . .
 
# Precompile assets at build time
RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile
 
EXPOSE 3000
 
CMD ["bundle", "exec", "puma", "-b", "tcp://0.0.0.0:3000"]

The SECRET_KEY_BASE_DUMMY=1 flag lets asset precompilation run at build time without your real secret. Rails logs to standard output when RAILS_LOG_TO_STDOUT is set, which is what the platform captures.

Application settings

  • RAILS_ENV=production — set in the Dockerfile above and reinforced as an environment variable so every process runs in production mode.
  • SECRET_KEY_BASE — Rails needs this at runtime to sign cookies and other values. Supply it as a secret rather than committing it.
  • RAILS_LOG_TO_STDOUT — set to 1 so logs stream to standard output.
  • RAILS_SERVE_STATIC_FILES — set to 1 if Puma should serve precompiled assets directly.
  • Assets — precompiled at build time by the Dockerfile so they ship inside the image.

Deploy from the Console

  1. Create (or open) a project, then create a container.
  2. Set the image to your pushed image reference, and the port to 3000.
  3. Set a health check path the app answers with a 2xx, such as Rails' built-in /up.
  4. Choose your regions, set CPU and memory, and attach any secrets.
  5. Create the container and wait for it to reach a running state.

See Deploy your first container for the full walkthrough.

Deploy with the Reis CLI

reis container:create \
  --type http \
  --name "Rails App" \
  --handle rails-app \
  --image ghcr.io/myorg/rails-app:v1.0.0 \
  --project my-project \
  --port 3000 \
  --healthcheck /up \
  --cpu 500 \
  --memory 512 \
  --regions falkenstein-1 \
  --env RAILS_ENV=production \
  --env RAILS_LOG_TO_STDOUT=1 \
  --secrets SECRET_KEY_BASE=rails-secret-key-base \
  --secrets DATABASE_URL=database-url

See Deploy an HTTP container with flags for the full flag reference.

Configuration, secrets, and databases

  • Non-sensitive configurationRAILS_ENV, RAILS_LOG_TO_STDOUT, and similar flags go in as plain environment variables.
  • Sensitive valuesSECRET_KEY_BASE, database credentials, and API keys belong in secrets, encrypted at rest and injected as environment variables at runtime. Read them with ENV as usual.
  • Database connections — Bahriya does not host your database. Point Rails at your own managed database with a DATABASE_URL or discrete credentials supplied through secrets.

Running migrations

Run bundle exec rails db:migrate as a one-off before or after a deploy rather than inside the web process. The same image with a migrate command run as a short-lived init job is a clean way to do this. See HTTP containers for how container types relate.

Next steps