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

# Ruby on Rails

> Deploy a Ruby on Rails application to Magic Containers

This guide walks you through building and deploying a Ruby on Rails application to Magic Containers with GitHub Container Registry. You'll need:

* A GitHub account for source code and container registry
* Ruby 3.2+ installed locally

<Tip>
  Don't have a bunny.net account yet? [Sign up](https://dash.bunny.net/auth/register) and enable Magic Containers to get started.
</Tip>

## Create the Rails app

<Tabs>
  <Tab title="Full-stack">
    Create a new Rails application with views and assets:

    ```bash theme={null}
    rails new app-rails
    cd app-rails
    ```

    Generate a controller with a view:

    ```bash theme={null}
    bin/rails generate controller Home index --skip-routes
    ```

    Add the route in `config/routes.rb`:

    ```ruby config/routes.rb theme={null}
    Rails.application.routes.draw do
      root "home#index"
      get "up", to: "rails/health#show", as: :rails_health_check
    end
    ```

    Update the view in `app/views/home/index.html.erb`:

    ```erb app/views/home/index.html.erb theme={null}
    <h1>Hello from Bunny 🐰</h1>
    ```
  </Tab>

  <Tab title="API only">
    Create a new Rails API application:

    ```bash theme={null}
    rails new app-rails --api
    cd app-rails
    ```

    Generate a controller with a simple endpoint:

    ```bash theme={null}
    bin/rails generate controller Api::Health index --skip-routes
    ```

    Add the route in `config/routes.rb`:

    ```ruby config/routes.rb theme={null}
    Rails.application.routes.draw do
      get "/", to: "api/health#index"
      get "up", to: "rails/health#show", as: :rails_health_check
    end
    ```

    Update the controller:

    ```ruby app/controllers/api/health_controller.rb theme={null}
    class Api::HealthController < ApplicationController
      def index
        render json: { message: "Hello from Bunny 🐰" }
      end
    end
    ```
  </Tab>
</Tabs>

## Run locally

Start the Rails development server:

```bash theme={null}
bin/rails server
```

Test the app at [http://localhost:3000](http://localhost:3000), or with curl:

```bash theme={null}
curl http://localhost:3000/
```

## Prepare for production

Rails 7.1+ automatically generates a production-ready `Dockerfile` in your project root.

Before building, prepare the app:

```bash theme={null}
bin/rails db:migrate
rm config/credentials.yml.enc
```

The credentials file is removed since we'll use environment variables instead.

<Info>
  If you're using an older Rails version, you can generate a Dockerfile with:
  `bash bin/rails generate dockerfile `
</Info>

## Review the Dockerfile

Rails 7.1+ generates a production-ready, multi-stage Dockerfile that includes:

* **jemalloc** for reduced memory usage
* **Bootsnap** precompilation for faster boot times
* **Non-root user** for security
* **Asset precompilation** (full-stack apps only)
* **Thruster** HTTP/2 proxy (full-stack apps only)

## Build and push to GitHub Container Registry

<Tabs>
  <Tab title="GitHub Actions">
    Create `.github/workflows/build.yml` to automatically build and push on every commit to `main`:

    ```yaml .github/workflows/build.yml theme={null}
    name: Build and Push

    on:
      push:
        branches: [main]

    env:
      REGISTRY: ghcr.io
      IMAGE_NAME: ${{ github.repository }}

    jobs:
      build-and-push:
        runs-on: ubuntu-latest
        permissions:
          contents: read
          packages: write

        steps:
          - uses: actions/checkout@v4

          - name: Log in to GitHub Container Registry
            uses: docker/login-action@v3
            with:
              registry: ${{ env.REGISTRY }}
              username: ${{ github.actor }}
              password: ${{ secrets.GITHUB_TOKEN }}

          - name: Build and push
            uses: docker/build-push-action@v5
            with:
              context: .
              push: true
              tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

          - name: Update container image on Magic Containers
            uses: BunnyWay/actions/container-update-image@main
            with:
              app_id: ${{ vars.APP_ID }}
              api_key: ${{ secrets.BUNNYNET_API_KEY }}
              container: app
              image_tag: "${{ github.sha }}"
    ```

    Push your code to trigger the workflow:

    ```bash theme={null}
    git init
    git add .
    git commit -m "Initial commit"
    git remote add origin https://github.com/YOUR_USERNAME/app-rails.git
    git push -u origin main
    ```
  </Tab>

  <Tab title="Docker CLI">
    Build and push manually from your local machine.

    <Steps>
      <Step title="Create a Personal Access Token">
        Go to [GitHub Settings > Developer settings > Personal access tokens](https://github.com/settings/tokens) and create a token with `write:packages` scope.
      </Step>

      <Step title="Log in to GitHub Container Registry">
        ```bash theme={null}
        export CR_PAT=your_personal_access_token
        echo $CR_PAT | docker login ghcr.io -u YOUR_USERNAME --password-stdin
        ```
      </Step>

      <Step title="Build the image">
        ```bash theme={null}
        docker build --platform linux/amd64 -t ghcr.io/YOUR_USERNAME/app-rails:latest .
        ```

        <Info>
          Magic Containers only supports images built for the **linux/amd64** architecture. The `--platform` flag ensures compatibility regardless of your local machine's architecture.
        </Info>
      </Step>

      <Step title="Push to registry">
        ```bash theme={null}
        docker push ghcr.io/YOUR_USERNAME/app-rails:latest
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

<Info>
  If your package is private, set the visibility to **Public** in GitHub or
  [configure Magic Containers with registry
  credentials](/magic-containers/image-registries).
</Info>

## Deploy to Magic Containers

<Steps>
  <Step title="Create a new app">
    In the bunny.net dashboard, go to **Magic Containers** and click **Add
    App**. Enter a name and select your deployment option.
  </Step>

  <Step title="Add a container">
    Click **Add Container**, then configure:

    | Field    | Value                     |
    | -------- | ------------------------- |
    | Registry | GitHub Container Registry |
    | Image    | `YOUR_USERNAME/app-rails` |
    | Tag      | `latest`                  |
  </Step>

  <Step title="Configure environment variables">
    Go to the **Environment Variables** tab and add:

    | Variable          | Value                            |
    | ----------------- | -------------------------------- |
    | `SECRET_KEY_BASE` | Generate with `bin/rails secret` |
  </Step>

  <Step title="Add an endpoint">
    Go to the **Endpoints** tab, click **Add New Endpoint**, and set the
    container port to `80`.
  </Step>

  <Step title="Deploy">
    Click **Add Container**, then **Next Step**, and **Confirm and Create**.
  </Step>
</Steps>

For more details, see the [quickstart guide](/magic-containers/quickstart).

## Test your app

```bash theme={null}
curl https://mc-xxx.bunny.run/
```

```json Response theme={null}
{ "message": "Hello from Bunny 🐰" }
```

<Info>
  You can [add a custom hostname](/magic-containers/endpoints) from the **Endpoints** section in your app settings.
</Info>

## Add persistent storage

Magic Containers are ephemeral, so data stored locally is lost on restarts. For Rails apps that need to persist data (Active Storage uploads, etc.), attach a [Persistent Volume](/magic-containers/persistent-volumes).

<Steps>
  <Step title="Add a volume">
    Go to your app's **Volumes** tab and click **Add Volume**. Set the mount path to `/rails/storage` and choose an initial size.
  </Step>

  <Step title="Configure Rails storage">
    Update `config/environments/production.rb` to use the volume for Active Storage:

    ```ruby config/environments/production.rb theme={null}
    config.active_storage.service = :local
    ```
  </Step>

  <Step title="Configure SQLite (optional)">
    To persist your SQLite database, update `config/database.yml`:

    ```yaml config/database.yml theme={null}
    production:
      <<: *default
      database: /rails/storage/production.sqlite3
    ```
  </Step>
</Steps>

See the [Persistent Volumes documentation](/magic-containers/persistent-volumes) for more details.

## Next steps

1. [Automate deploys with GitHub Actions](/magic-containers/deploy-with-github-actions)
2. [Add a custom hostname](/magic-containers/endpoints)
