> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slipway.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy a Rails app

> Deploy a Rails app with Postgres, migrations and a Sidekiq worker.

Rails runs on slipway as a `web` service on the Ruby runtime, with a managed Postgres
alongside it.

## Prerequisites

A Rails 7 or 8 app in a GitHub or GitLab repository.

## Configure the app

Rails blocks requests whose `Host` header is not permitted, which shows up as a 403 on a
deploy that otherwise looks healthy.

```ruby config/environments/production.rb theme={"system"}
config.hosts << ENV["APP_HOST"] if ENV["APP_HOST"].present?
config.force_ssl = true
config.assume_ssl = true
```

`assume_ssl` matters because TLS terminates at slipway's edge, so Rails sees plain HTTP from
the proxy and would otherwise redirect in a loop.

## Add slipway.yaml

```yaml slipway.yaml theme={"system"}
version: 1

databases:
  - name: db
    engine: postgres
    version: "18"
    diskSizeGB: 10

services:
  - name: web
    runtime: ruby
    runtimeVersion: "3.4"
    buildCommand: bundle install && bundle exec rails assets:precompile
    command: ["bundle", "exec", "puma", "-b", "tcp://0.0.0.0:3000"]
    preDeploy: ["bundle", "exec", "rails", "db:migrate"]
    dependsOn: [db]
    ports:
      - port: 3000
        public: true
    healthCheckPath: /up
    env:
      DATABASE_URL: ${service.db.url}
      APP_HOST: ${service.web.host}
      RAILS_ENV: production
      RAILS_MASTER_KEY: ${secret.RAILS_MASTER_KEY}
      RAILS_LOG_TO_STDOUT: "1"
      RAILS_SERVE_STATIC_FILES: "1"
```

Rails 7.1 and later ship `/up` as a health endpoint. For an older app, add one.

## Set the master key

Add `RAILS_MASTER_KEY` under the project's **Settings → Variables & secrets** before the
first deploy, using the value from `config/master.key`. Never commit that file.

## Add Sidekiq

```yaml slipway.yaml theme={"system"}
databases:
  - name: queue
    engine: redis

services:
  - name: worker
    type: worker
    runtime: ruby
    runtimeVersion: "3.4"
    buildCommand: bundle install
    command: ["bundle", "exec", "sidekiq"]
    maxShutdownDelaySeconds: 60
    env:
      DATABASE_URL: ${service.db.url}
      REDIS_URL: ${service.queue.url}
      RAILS_ENV: production
      RAILS_MASTER_KEY: ${secret.RAILS_MASTER_KEY}
```

`maxShutdownDelaySeconds` gives Sidekiq time to finish an in-flight job before the container
is killed.

Set the Redis maxmemory policy to `noeviction` for a queue, so entries are never silently
dropped. See [Redis](/data/redis).

## Active Storage

Use [object storage](/data/object-storage) rather than local disk, which is lost on every
deploy:

```yaml slipway.yaml theme={"system"}
    env:
      AWS_ACCESS_KEY_ID: ${service.files.access_key_id}
      AWS_SECRET_ACCESS_KEY: ${service.files.secret_access_key}
      AWS_ENDPOINT: ${service.files.endpoint}
      AWS_BUCKET: ${service.files.bucket}
      AWS_REGION: auto
```

Set `force_path_style: true` in your Active Storage config.

## Next steps

> **[Scaling](/services/scaling)**
>
> Run several Puma processes behind the load balancer.

> **[Troubleshooting](/deploys/troubleshooting)**
>
> What to check when a deploy goes wrong.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.