This skill should be used when the user asks about deployment, background jobs, caching, WebSockets, security, performance, or Rails 8 infrastructure components. Trigger phrases include "deployment", "Kamal", "Solid Queue", "Solid Cache", "Solid Cable", "background jobs", "caching", "WebSockets", "Action Cable", "security", "performance", "scaling", "production", "Docker", "credentials".
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/deploy.ymlexamples/solid-queue.ymlreferences/kamal-guide.mdreferences/security-checklist.mdreferences/solid-stack.mdExpertise in Solid Queue, Solid Cache, Solid Cable, Kamal deployment, security best practices, and performance optimization for Rails 8 applications.
Rails 8 ships with everything you need for production. Solid Queue replaces Sidekiq. Solid Cache replaces Redis for caching. Solid Cable handles WebSockets. Don't reach for external dependencies when Rails has you covered.
Database-backed job processing. No Redis required.
bin/rails generate solid_queue:install
# config/application.rb
config.active_job.queue_adapter = :solid_queue
# app/jobs/welcome_email_job.rb
class WelcomeEmailJob < ApplicationJob
queue_as :default
def perform(user)
UserMailer.welcome(user).deliver_now
end
end
# Enqueue
WelcomeEmailJob.perform_later(user)
# config/solid_queue.yml
production:
dispatchers:
- polling_interval: 1
batch_size: 500
workers:
- queues: "*"
threads: 5
polling_interval: 0.1
class ImportantJob < ApplicationJob
queue_as :critical
end
class BackgroundJob < ApplicationJob
queue_as :low
end
# config/solid_queue.yml
workers:
- queues: [critical, default, low]
threads: 5
Database-backed caching. Replaces Redis/Memcached.
bin/rails generate solid_cache:install
# config/environments/production.rb
config.cache_store = :solid_cache_store
# Usage - same as always
Rails.cache.fetch("user_#{user.id}_stats", expires_in: 1.hour) do
user.calculate_stats
end
<% cache @article do %>
<article>
<h1><%= @article.title %></h1>
<%= @article.formatted_body %>
</article>
<% end %>
<% cache @article do %>
<article>
<h1><%= @article.title %></h1>
<% @article.comments.each do |comment| %>
<% cache comment do %>
<%= render comment %>
<% end %>
<% end %>
</article>
<% end %>
Database-backed Action Cable adapter.
# config/cable.yml
production:
adapter: solid_cable
polling_interval: 0.1.seconds
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room_id]}"
end
def speak(data)
Message.create!(
content: data["content"],
user: current_user,
room_id: params[:room_id]
)
end
end
# Broadcasting from model
class Message < ApplicationRecord
after_create_commit { broadcast_append_to room }
end
Docker-based deployment tool from 37signals. Zero-downtime deploys to any server.
gem install kamal
kamal init
# config/deploy.yml
service: myapp
image: myuser/myapp
servers:
web:
- 192.168.1.1
- 192.168.1.2
job:
hosts:
- 192.168.1.3
cmd: bin/jobs
registry:
username: myuser
password:
- KAMAL_REGISTRY_PASSWORD
env:
secret:
- RAILS_MASTER_KEY
- DATABASE_URL
accessories:
db:
image: postgres:16
host: 192.168.1.4
env:
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
kamal setup # First deploy
kamal deploy # Update deployment
kamal app logs # View logs
kamal app exec "bin/rails console" # Rails console
kamal rollback # Revert to previous version
# config/deploy.yml
healthcheck:
path: /up
port: 3000
interval: 1s
# config/routes.rb
get "up" => "rails/health#show", as: :rails_health_check
bin/rails generate authentication
Generates secure session-based authentication:
has_secure_password with bcryptbin/rails credentials:edit
bin/rails credentials:edit --environment production
# config/credentials.yml.enc
aws:
access_key_id: xxx
secret_access_key: xxx
stripe:
secret_key: xxx
Rails.application.credentials.aws[:access_key_id]
Rails.application.credentials.stripe[:secret_key]
# config/initializers/content_security_policy.rb
Rails.application.configure do
config.content_security_policy do |policy|
policy.default_src :self
policy.script_src :self
policy.style_src :self, :unsafe_inline
policy.img_src :self, :data, "https:"
policy.connect_src :self
end
end
# BAD
User.where("name = '#{params[:name]}'")
# GOOD
User.where(name: params[:name])
User.where("name = ?", params[:name])
<%# BAD - renders raw HTML %>
<%= raw user_input %>
<%= user_input.html_safe %>
<%# GOOD - escaped by default %>
<%= user_input %>
<%# For intentional HTML %>
<%= sanitize user_content, tags: %w[p br strong em] %>
# Strong parameters protect against this
def user_params
params.require(:user).permit(:name, :email)
# admin: true is NOT permitted
end
# config/initializers/rack_attack.rb
class Rack::Attack
throttle("req/ip", limit: 300, period: 5.minutes) do |req|
req.ip
end
throttle("logins/ip", limit: 5, period: 20.seconds) do |req|
req.ip if req.path == "/login" && req.post?
end
end
# Add indexes for foreign keys and common queries
add_index :articles, :user_id
add_index :articles, [:user_id, :published_at]
add_index :users, :email, unique: true
# Use counter caches
belongs_to :user, counter_cache: true
# Avoid N+1 with includes
Article.includes(:user, :comments).published
# DON'T block the request
def create
@user = User.create!(user_params)
@user.send_welcome_email # Slow!
end
# DO use background jobs
def create
@user = User.create!(user_params)
WelcomeEmailJob.perform_later(@user)
end
# config/environments/production.rb
config.assets.css_compressor = :sass
config.assets.js_compressor = :terser
# Use select to limit columns
User.select(:id, :name).where(active: true)
# Use pluck for simple arrays
User.where(role: :admin).pluck(:email)
# Use exists? instead of any?
User.exists?(email: params[:email])
# Batch processing
User.find_each(batch_size: 1000) do |user|
UserSyncJob.perform_later(user)
end
# config/environments/production.rb
config.log_level = :info
config.log_tags = [:request_id]
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
# Already included in Rails 8
# GET /up returns 200 if app is healthy
Integrate with your preferred service:
# config/initializers/error_tracking.rb
Rails.error.subscribe(ErrorTracker.new) do |error, handled:, severity:, context:, source:|
ErrorTracker.report(error, context: context, severity: severity)
end
references/solid-stack.md - Deep dive on Solid componentsreferences/kamal-guide.md - Complete Kamal deploymentreferences/security-checklist.md - Production securityexamples/deploy.yml - Sample Kamal configexamples/solid-queue.yml - Queue configuration