Skip to content

How to deploy a Ruby on Rails app on Ownkube

Deploy a Ruby on Rails app on Ownkube with Puma, a managed Postgres database, and migrations that run as a one-off job. Step by step.

Abhishek Jaiswal

6 min

On this page 10 sections

A Rails app needs three things from a host: a place to run Puma, a Postgres database it can reach, and a way to run db:migrate before traffic hits the new code. Ownkube covers all three without a cloud account or hand-written YAML.

This walks through deploying a standard Rails app on Ownkube Compute: configuring Puma, wiring up a managed Postgres, setting RAILS_MASTER_KEY and SECRET_KEY_BASE, precompiling assets, and running migrations as a job.

TL;DR

  • Push a Rails app with a Dockerfile, and Ownkube builds and deploys it behind automatic TLS.
  • Add an Ownkube-managed Postgres database and set DATABASE_URL from its connection string.
  • Set RAILS_MASTER_KEY (or SECRET_KEY_BASE) and RAILS_ENV=production as environment variables in the dashboard, not in the repo.
  • Precompile assets in the Docker build step, and run db:migrate as a one-off job, so it runs once per release instead of on every container start.
  • Metered billing fits Rails well: Puma sits mostly idle between requests, so the CPU line barely moves outside real traffic.

1. Prepare the app for production

Rails is opinionated enough that most of the setup is standard Rails. Confirm the basics before you deploy:

  • config/database.yml reads its production connection from ENV["DATABASE_URL"], which is the Rails default.
  • config/environments/production.rb has config.assets.compile = false and config.public_file_server.enabled set to match your asset pipeline (Propshaft or Sprockets, either works).
  • Gemfile includes pg for the Postgres adapter and puma as the app server.
config/database.yml
production:
<<: *default
url: <%= ENV["DATABASE_URL"] %>

2. Write a Dockerfile

Ownkube deploys from a Dockerfile. A multi-stage build keeps the final image small and lets you precompile assets during the build:

FROM ruby:3.3-slim AS build
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev git curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle config set without 'development test' \
&& bundle install --jobs 4 --retry 3
COPY . .
# Precompile assets. SECRET_KEY_BASE_DUMMY lets this run without real secrets at build time.
RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile
FROM ruby:3.3-slim
RUN apt-get update -qq && apt-get install -y libpq-dev curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /app /app
ENV RAILS_ENV=production
ENV RAILS_LOG_TO_STDOUT=true
ENV RAILS_SERVE_STATIC_FILES=true
EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Puma’s default config/puma.rb reads the port and worker count from environment variables already, so leave it as Rails generated it:

config/puma.rb
port ENV.fetch("PORT", 3000)
workers ENV.fetch("WEB_CONCURRENCY", 2)
threads_count = ENV.fetch("RAILS_MAX_THREADS", 5).to_i
threads threads_count, threads_count

3. Push the repo and create the app

Connect the repository from the Ownkube dashboard (app.ownkube.io) to deploy on every push, or deploy the current directory with the okctl CLI:

Terminal window
okctl login
okctl up --name rails-app --port 3000

Ownkube builds the image from your Dockerfile, allocates a hostname on *.ownkube.app, and issues TLS automatically, so you have a working HTTPS URL the moment it deploys. Serving the app on a domain you own is available today when it runs on a Starter cluster in your own AWS account; custom domains on Ownkube Compute are on the roadmap.

4. Add a managed Postgres database

Create a database box from the dashboard: New > Database > Postgres. It provisions a private (in-region) Postgres box starting at $4 a month plus storage at $0.25 per GB, with backups and point-in-time recovery available on Personal and Team.

The database runs as a single private instance by default; opt-in public access and, on Team, high availability are per-database options when you need them.

Link the database to the Rails app. Ownkube writes its connection string into the app as a secret DATABASE_URL variable and redeploys it:

Terminal window
okctl deploy link rails-app <database-id>

Because config/database.yml already reads from ENV["DATABASE_URL"], no further Rails configuration is needed.

5. Set your secrets

Rails needs its master key or secret key base to decrypt credentials and sign sessions. Set it as a secret environment variable in the dashboard and keep config/master.key out of the repository:

Terminal window
okctl vars set rails-app RAILS_MASTER_KEY="$(cat config/master.key)" --secret
okctl vars set rails-app RAILS_ENV=production

If you use SECRET_KEY_BASE directly instead of the encrypted credentials file, set that instead:

Terminal window
okctl vars set rails-app SECRET_KEY_BASE="$(bundle exec rails secret)" --secret

Environment variables set through Ownkube are encrypted at rest and only injected into the running container.

6. Run migrations as a job

Run db:migrate as a one-off job instead of on every container start. Create a Job deployment from the same repository, set its command to bundle exec rails db:migrate, and leave the schedule empty. Variables are set per deployment, so give the job the same RAILS_MASTER_KEY and RAILS_ENV and link the same database to it.

Then trigger a run whenever a release includes new migrations:

Terminal window
okctl deploy link rails-migrate <database-id>
okctl deploy job-runs trigger rails-migrate
okctl deploy job-runs list rails-migrate

The run’s output is on the job’s Logs tab. The web app and the job deploy independently, so keep each migration safe for the version already running: add columns and tables in one release, and drop old ones in a later one.

7. Scale and watch it run

A fresh Rails app usually starts fine on a small metered ceiling, for example a 500m CPU limit and 1 GiB of memory, set on the sliders under Settings → Resources.

Turn on horizontal autoscaling if traffic is uneven, so Ownkube adds Puma instances under load and scales back down when it is quiet. On Ownkube Compute you turn it on through your coding agent (the update_deployment MCP tool) or the API, with a minimum and maximum replica count and a CPU target such as 70%.

Logs and metrics for the app and the database box are both in the dashboard, so a slow migration or a Puma worker that keeps restarting shows up in one place.

Moving to your own AWS later

If a customer contract requires the app to run in a named AWS account, your own or the customer’s, the same Dockerfile and Rails app move there with no rewrite. Ownkube provisions the instance, wires up the same deploy flow, and brings the logs, metrics, and health checks with it.

Railway, by contrast, only runs on Railway’s infrastructure. Our Railway alternatives guide compares Railway with the other options.

FAQ

Does Ownkube support Rails credentials and Active Storage?

Yes. Encrypted credentials work through RAILS_MASTER_KEY as shown above. Active Storage works against any S3-compatible bucket you configure; Ownkube does not provide managed object storage directly, so point it at your own bucket.

Do I need a Dockerfile, or can Ownkube detect Rails automatically?

A Dockerfile gives you full control over the Ruby version, native gems, and the asset precompile step, and is the most reliable path for Rails. Simpler apps without native dependencies can also deploy straight from source.

Can I run Sidekiq or another background worker alongside the web app?

Yes. Deploy the worker as a second Ownkube app pointed at the same Postgres connection string (and a Valkey cache box from $2 a month if you need Redis for the queue), so the web app and worker scale independently.

What happens if my wallet balance runs out mid-month?

The app pauses and its data, including the Postgres box, is kept. Adding credit or the next monthly plan load resumes it. Unused wallet credit rolls over and never expires, so credit from a quiet month carries into the next.

Can I roll back a bad deploy?

Yes. Ownkube keeps prior image versions and supports zero-downtime rollback from the dashboard or okctl, so a code issue does not require a manual recovery. A rollback restores the previous image, and a migration that already ran stays applied, so keep migrations additive.

For the always-current version of this walkthrough, see the Deploy Ruby on Rails guide in the docs.

Where Ownkube fits

Rails wants a real database, a way to run migrations, and an app server that survives restarts, and Ownkube gives you all three without provisioning a cloud account first. Push the Dockerfile, attach a managed Postgres, set your secrets, and run migrations as a job. See Ownkube pricing for the per-minute wallet model, from $5 a month. Deploy my first app.

Abhishek Jaiswal · Founder and Engineer

Abhishek Jaiswal is the founder and engineer behind Ownkube, a cloud for personal software. He builds the platform and writes here about cloud cost, migrations, and running production infrastructure in your own AWS account without a DevOps team.

Share

More posts

All posts

Ship my first app in the next five minutes.

Push a repo and get a live URL, with no cloud account or config.

Top up from $5, billed per minute with no subscription.