← Back to kaminari/kaminari

How to Deploy & Use kaminari/kaminari

Kaminari Deployment & Usage Guide

A complete guide for integrating the Kaminari pagination library into Ruby web applications and deploying to production.

1. Prerequisites

Before integrating Kaminari, ensure your environment meets the following requirements:

Runtime Requirements:

  • Ruby: 2.1 or higher (3.0+ recommended)
  • Rails: 4.1 or higher (6.0+ recommended) OR Sinatra 1.4+/Grape
  • Bundler: Latest stable version
  • Database: PostgreSQL, MySQL, or SQLite (via ActiveRecord) OR MongoDB (via Mongoid/MongoMapper)

Optional Dependencies:

  • Template Engine: ERB (default), Haml 3+, or Slim
  • Frontend Framework: Bootstrap or Tailwind (for custom theme views)

2. Installation

Rails Application Integration

Add Kaminari to your Rails application's Gemfile:

gem 'kaminari'

For specific ORM support (if not using ActiveRecord), add the appropriate gem:

# For Mongoid
gem 'kaminari-mongoid'

# For MongoMapper  
gem 'kaminari-mongo_mapper'

# For DataMapper
gem 'kaminari-data_mapper'

Install the gem:

bundle install

Generate Configuration (Optional)

Create a configuration initializer to customize default settings:

rails generate kaminari:config

This creates config/initializers/kaminari.rb:

Kaminari.configure do |config|
  config.default_per_page = 25
  config.max_per_page = 100
  config.max_pages = nil
  config.window = 4
  config.outer_window = 0
  config.left = 0
  config.right = 0
  config.page_method_name = :page
  config.param_name = :page
  config.params_on_first_page = false
end

3. Configuration

Model-Level Configuration

Set default pagination limits per model:

class User < ApplicationRecord
  paginates_per 50           # Default items per page
  max_paginates_per 100      # Maximum allowed per page
end

Controller Usage

Implement pagination in your controllers:

class UsersController < ApplicationController
  def index
    # Basic pagination
    @users = User.order(:name).page(params[:page])
    
    # With custom per-page limit (from query param or default)
    @users = User.order(:name).page(params[:page]).per(params[:per] || 20)
    
    # With padding (skip first N records)
    @users = User.order(:name).page(2).per(25).padding(3)
  end
end

View Helpers

Add pagination controls to your views:

<%= paginate @users %>

<!-- With custom options -->
<%= paginate @users, window: 2, outer_window: 1 %>

<!-- Entry information -->
<%= page_entries_info @users, entry_name: 'item' %>

<!-- Navigation links -->
<%= link_to_next_page @users, 'Next Page', class: 'btn' %>
<%= link_to_previous_page @users, 'Previous Page', class: 'btn' %>

Customizing Views

Generate view templates for customization:

# Default theme
rails generate kaminari:views default

# With specific template engine
rails generate kaminari:views default -e haml

# With views prefix (for admin namespace, etc.)
rails generate kaminari:views default --views_prefix admin

# Download community themes (requires internet)
rails generate kaminari:views bootstrap4
rails generate kaminari:views bulma
rails generate kaminari:views materialize

Override specific partials in app/views/kaminari/:

  • _paginator.html.erb - Main container
  • _page.html.erb - Individual page link
  • _gap.html.erb - Ellipsis/truncated pages
  • _prev_page.html.erb / _next_page.html.erb - Navigation links

I18n Configuration

Add pagination translations to config/locales/en.yml:

en:
  views:
    pagination:
      first: "&laquo; First"
      last: "Last &raquo;"
      previous: "&lsaquo; Prev"
      next: "Next &rsaquo;"
      truncate: "&hellip;"
  helpers:
    page_entries_info:
      one_page:
        display_entries:
          zero: "No %{entry_name} found"
          one: "Displaying <b>1</b> %{entry_name}"
          other: "Displaying <b>all %{count}</b> %{entry_name}"
      more_pages:
        display_entries: "Displaying %{entry_name} <b>%{first}&nbsp;-&nbsp;%{last}</b> of <b>%{total}</b> in total"

4. Build & Run

Local Development

Start your Rails development server:

rails server

Visit paginated routes:

http://localhost:3000/users?page=2&per=50

Testing Pagination

In Rails console:

# Check pagination metadata
users = User.page(2).per(25)
users.total_pages      # => 20
users.current_page     # => 2
users.next_page        # => 3
users.prev_page        # => 1
users.first_page?      # => false
users.last_page?       # => false
users.out_of_range?    # => false
users.total_count      # => 500

Performance Optimization

For large datasets, avoid count queries:

# Use total_count sparingly in high-traffic areas
# Cache the count if possible
Rails.cache.fetch("users_count", expires_in: 5.minutes) do
  User.count
end

Remove pagination scopes when exporting:

# Export all records (unpaginated)
@users = User.order(:name).page(params[:page])
@all_users = @users.except(:limit, :offset)  # Removes pagination

5. Deployment

Platform Options

Kaminari runs entirely server-side with no JavaScript dependencies, making it compatible with any Rails hosting platform:

Recommended Platforms:

  • Heroku: Standard Rails deployment
  • AWS Elastic Beanstalk: For auto-scaling applications
  • DigitalOcean App Platform: Cost-effective VPS hosting
  • Render: Modern PaaS with automatic deploys
  • Traditional VPS: Docker containers or bare metal with Capistrano

Deployment Checklist

Before deploying to production:

  1. Database Indices: Ensure paginated columns are indexed

    add_index :users, :created_at
    
  2. Max Per Page Limits: Set max_paginates_per to prevent abuse:

    class ApplicationController < ActionController::Base
      def pagination_limit
        (params[:per] || 25).to_i.clamp(1, 100)
      end
    end
    
  3. CDN/Asset Pipeline: If using custom Kaminari views with CSS, ensure styles are precompiled:

    RAILS_ENV=production bundle exec rails assets:precompile
    
  4. Database Connection Pool: Ensure sufficient connections for count queries:

    # config/database.yml
    production:
      pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
    

Docker Deployment

Example Dockerfile excerpt:

FROM ruby:3.2
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
RUN bundle exec rails assets:precompile
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

6. Troubleshooting

Common Issues

Problem: undefined method 'page' for #<Class:0x...>
Solution: Ensure Kaminari is in the Gemfile (not just :development or :test groups) and run bundle install. Restart the Rails server.

Problem: Count queries are slow on large tables
Solution:

  • Add database indices on sorted columns
  • Use approximate counts for very large tables:
    # In model
    def self.approximate_count
      connection.execute("SELECT reltuples::BIGINT AS estimate FROM pg_class WHERE relname = 'users'").first['estimate']
    end
    
  • Implement cursor-based pagination for infinite scroll scenarios

Problem: Pagination links styled incorrectly
Solution: Generate themed views:

rails generate kaminari:views bootstrap4
# Or manually edit app/views/kaminari/_paginator.html.erb
# The default output uses HTML5 <nav> and <span> tags with class names:
# .pagination, .page, .current, .prev, .next, .first, .last, .gap

Problem: Conflicts with existing page method
Solution: Rename Kaminari's page method in the initializer:

Kaminari.configure do |config|
  config.page_method_name = :paginate
end
# Now use: User.paginate(1)

Problem: per scope not working
Solution: Ensure you're calling .page() before .per():

# Wrong
User.per(50).page(1)

# Correct  
User.page(1).per(50)

Problem: I18n translations not appearing
Solution: Restart the server after adding translations. Ensure config.i18n.fallbacks = true is set in production.

Problem: Memory bloat with large per_page values
Solution: Set hard limits in the model:

class User < ApplicationRecord
  max_paginates_per 100
end

Debug Mode

Enable SQL logging to see pagination queries:

# In controller or model
User.page(2).per(25).to_sql
# => "SELECT \"users\".* FROM \"users\" LIMIT 25 OFFSET 50"

Check for N+1 queries and eager load associations:

@users = User.includes(:posts, :comments).page(params[:page])