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: "« First"
last: "Last »"
previous: "‹ Prev"
next: "Next ›"
truncate: "…"
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} - %{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:
-
Database Indices: Ensure paginated columns are indexed
add_index :users, :created_at -
Max Per Page Limits: Set
max_paginates_perto prevent abuse:class ApplicationController < ActionController::Base def pagination_limit (params[:per] || 25).to_i.clamp(1, 100) end end -
CDN/Asset Pipeline: If using custom Kaminari views with CSS, ensure styles are precompiled:
RAILS_ENV=production bundle exec rails assets:precompile -
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])