Claude-skill-registry async-falcon-rails
Transform a Rails application to use Falcon web server with async job processing (async-job), async Action Cable, and Redis-compatible database (Valkey for production). Use when the user wants to add async/Falcon stack to a Rails project, migrate from Puma to Falcon, or set up async job processing with Redis for both development and production environments including Kamal deployment.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/async-falcon-rails" ~/.claude/skills/majiayu000-claude-skill-registry-async-falcon-rails && rm -rf "$T"
skills/data/async-falcon-rails/SKILL.mdAsync Falcon Rails
Overview
Transform a Rails application to use the Falcon web server with async job processing, async Action Cable, and Redis-compatible database (Valkey for production). This skill handles the complete migration from Puma to Falcon, configures async job adapters, sets up Redis/Valkey for Action Cable and job queues, and configures Kamal deployment for production.
When to Use
Use this skill when the user:
- Wants to add async/Falcon stack to an existing Rails project
- Needs to migrate from Puma to Falcon web server
- Requests async job processing setup with Redis
- Wants to configure async Action Cable
- Needs Kamal deployment configuration for the async stack
Prerequisites
Before applying this skill, verify:
- The project is a Rails application (check for
,Gemfile
)config/application.rb - The project structure includes
directoryconfig/environments/ - Bundle is available (
command works)bundle - If Kamal deployment is needed, check for
config/deploy.yml
Workflow
Follow these steps in order to transform a Rails application to use the async/Falcon stack:
Step 1: Update Gemfile Dependencies
Replace Puma with Falcon and add async dependencies:
bundle remove puma bundle add falcon bundle add async-job-processor-redis bundle add async-job-adapter-active_job bundle add async-cable bundle add redis
After running these commands, verify the Gemfile includes:
gem "falcon"gem "async-job-processor-redis"gem "async-job-adapter-active_job"gem "async-cable"
(providesgem "redis"
for endpoint parsing)Async::Redis::Endpoint
Step 2: Update Dockerfile for SSL Dependencies
CRITICAL: The async/Falcon stack requires OpenSSL development libraries to build properly. Without this, Docker builds will fail.
Edit the
Dockerfile and add libssl-dev to the system dependencies.
Find the line that installs build packages (usually around line 40):
RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives
Update it to include
libssl-dev:
RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential git libyaml-dev libssl-dev pkg-config && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives
Why this is needed: The Falcon web server and async gems depend on native extensions that require OpenSSL headers to compile. Without
libssl-dev, the Docker build will fail with compilation errors.
Step 3: Create Async Job Configuration
Create
config/initializers/async_job.rb with the following content:
require "async/job" require "async/job/processor/aggregate" require "async/job/processor/redis" require "async/job/processor/inline" require "async/redis/endpoint" Rails.application.configure do # Resolve Redis endpoint from REDIS_URL; fallback to localhost for dev/test. redis_endpoint = Async::Redis::Endpoint.parse( ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } ) config.async_job.define_queue "default" do enqueue Async::Job::Processor::Aggregate # Ensure the job runner connects to the accessory container (or localhost in dev). dequeue Async::Job::Processor::Redis, endpoint: redis_endpoint end config.async_job.define_queue "local" do dequeue Async::Job::Processor::Inline end end
This configuration:
- Sets up a "default" queue using Redis for job processing
- Parses the REDIS_URL environment variable to create a proper Redis endpoint
- Passes the endpoint to the Redis processor for both development and production
- Sets up a "local" queue for inline processing
- Uses Aggregate processor for enqueuing and Redis for dequeuing
Step 4: Update Procfile for Development
Update
Procfile.dev to include the async job processor:
Add this line to the existing Procfile.dev:
jobs: bundle exec async-job-adapter-active_job-server
The complete Procfile.dev should include at minimum:
web: bin/rails s jobs: bundle exec async-job-adapter-active_job-server
If the project uses Vite or other frontend build tools, keep those lines as well.
Step 5: Configure Development Environment
Edit
config/environments/development.rb to use async_job queue adapter.
Find the section about ActiveJob (usually near
config.active_job.verbose_enqueue_logs) and add:
config.active_job.queue_adapter = :async_job
Step 6: Configure Production Environment
Edit
config/environments/production.rb to configure both async_job and Redis cache:
Find and update or add these configurations:
# For cache store (usually around line 50) config.cache_store = :redis_cache_store, { url: ENV["REDIS_URL"] } # For queue adapter (usually around line 53) config.active_job.queue_adapter = :async_job
Step 7: Configure Application for Async/Cable
Edit
config/application.rb to add async/cable support:
- At the top of the file, after
, add:require "rails/all"
require "async/cable"
- Inside the
block, afterclass Application < Rails::Application
, add:config.load_defaults
# Configure async/fiber support config.active_record.permanent_connection_checkout = :disallowed config.active_support.isolation_level = :fiber
These settings enable fiber-based isolation for async operations.
Step 8: Generate Action Cable Base Channel
Run the Rails generator to create the base Action Cable structure:
bin/rails generate channel BaseChannel
This creates:
app/channels/application_cable/channel.rbapp/channels/application_cable/connection.rbapp/channels/base_channel.rbtest/channels/base_channel_test.rb
Step 9: Mount Action Cable in Routes
Edit
config/routes.rb to mount the Action Cable server.
Add this line at the top of the
Rails.application.routes.draw block:
mount ActionCable.server => '/cable'
Step 10: Configure Cable to Use Redis
Edit
config/cable.yml to use Redis for all environments:
Update the configuration to:
development: adapter: redis url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> channel_prefix: PROJECT_NAME_production test: adapter: test production: adapter: redis url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> channel_prefix: PROJECT_NAME_production
Replace
PROJECT_NAME with the actual Rails application name (found in config/application.rb as the module name).
Step 11: Configure Kamal Deployment (If Applicable)
If the project uses Kamal for deployment (check for
config/deploy.yml), update the deployment configuration:
11.1: Add Job Server
In the
servers: section, add or uncomment the job server configuration:
servers: web: - 192.168.0.1 job: hosts: - 192.168.0.1 cmd: bundle exec async-job-adapter-active_job-server options: init: true
Key configuration:
: Skips health checks for the job server (avoids 30-second deployment wait)init: true- Job servers don't expose HTTP endpoints, so health checks would timeout unnecessarily
11.2: Configure Redis/Valkey Accessory
In the
accessories: section (create if it doesn't exist), add Redis/Valkey configuration:
accessories: redis: image: valkey/valkey:9 host: 192.168.0.1 port: "127.0.0.1:6379:6379" directories: - redis_data:/data
Key considerations:
- Important: Use Valkey instead of Redis due to Redis licensing changes. Valkey is a Redis-compatible fork maintained by the Linux Foundation
- Use Valkey 9 or latest stable version
- Port binding
prevents public exposure (localhost only)"127.0.0.1:6379:6379" - Volume name
prevents conflicts with other services (e.g., PostgreSQL)redis_data
11.3: Add REDIS_URL Environment Variable
In the
env: section under clear:, add:
env: clear: REDIS_URL: redis://PROJECT_NAME-redis:6379/1
Replace
PROJECT_NAME with the service name from the top of deploy.yml. The format follows Kamal's Docker naming convention: {service_name}-{accessory_name}.
11.4: Configure Multi-Architecture Builds
Update the
builder: section to support multiple architectures:
builder: arch: - amd64 - arm64
This enables building Docker images for:
- amd64: Intel/AMD processors (Windows, Linux, older Macs)
- arm64: Apple Silicon (M1/M2/M3 Macs), ARM-based Linux servers
Verification Steps
After completing the workflow, verify the setup:
- Gemfile: Check that Puma is removed and all async gems are added
- Dockerfile: Verify
is included in the apt-get install linelibssl-dev - Initializers: Verify
exists and includes endpoint configuration (config/initializers/async_job.rb
)endpoint: redis_endpoint - Environments: Confirm
queue adapter in development.rb and production.rbasync_job - Application: Verify
require and fiber isolation config in application.rbasync/cable - Cable: Check that Action Cable is mounted in routes.rb
- Cable Config: Confirm cable.yml uses Redis for development and production
- Kamal (if applicable): Verify job server with
, Redis accessory, REDIS_URL, and multi-arch build config in deploy.ymlinit: true
Important Notes
Redis/Valkey Dependency
This stack requires Redis-compatible database to be running:
- Development: Start Redis locally with
or use Dockerredis-server - Production: Valkey (Redis-compatible) is deployed as a Kamal accessory (configured in deploy.yml)
- Note: We use Valkey instead of Redis in production due to Redis licensing changes. Valkey is a fully Redis-compatible fork maintained by the Linux Foundation
Environment Variables
The
REDIS_URL environment variable must be set:
- Development: Defaults to
(configured in cable.yml)redis://localhost:6379/1 - Production: Set via Kamal deploy.yml or environment configuration
Kamal Naming Conventions
When using Kamal:
- Redis accessory will be named:
{service_name}-redis - Use this name in REDIS_URL:
redis://{service_name}-redis:6379/1 - Volume names should be descriptive:
,redis_data
, etc.postgres_data
Port Binding Security
The Redis port binding
"127.0.0.1:6379:6379" ensures:
- Redis is accessible to containers on the same Docker network
- Redis is NOT exposed to the public internet
- Only localhost connections are allowed on the host
Troubleshooting
Docker Build Errors
If Docker build fails with compilation errors about OpenSSL or missing headers:
- Symptom: Build fails during gem installation with errors like "openssl/ssl.h: No such file or directory"
- Cause: Missing
system dependency in Dockerfilelibssl-dev - Solution: Add
to the apt-get install line in Dockerfile (see Step 2)libssl-dev - Verification: Check that the Dockerfile includes
in the package list alongsidelibssl-dev
,build-essential
,git
, andlibyaml-devpkg-config
This is a critical dependency for Falcon and async gems - without it, the Docker image cannot be built.
Bundle Errors
If
bundle add commands fail:
- Check that Gemfile is not locked with version conflicts
- Try
to resolve dependency issuesbundle update - Verify network connectivity to rubygems.org
Redis Connection Errors
If the application cannot connect to Redis:
- Development: Ensure Redis is running locally (
should returnredis-cli ping
)PONG - Production: Check that REDIS_URL environment variable is set correctly
- Verify cable.yml configuration matches the REDIS_URL format
Kamal Deployment Issues
If Kamal deployment fails:
- Verify all placeholders (PROJECT_NAME, IP addresses) are replaced with actual values
- Check that Redis accessory is running:
kamal accessory details redis - Ensure REDIS_URL matches the Kamal service naming convention
Deployment Timeouts
If Kamal deployment hangs for 30 seconds when deploying the job server:
- Symptom: Deployment waits and then shows "Container not ready" for job server
- Cause: Job server doesn't expose HTTP endpoints, so health checks timeout
- Solution: Add
withoptions:
to the job server configuration in deploy.yml (see Step 11.1)init: true - Why it works:
tells Kamal to skip health checks for this serviceinit: true
References
For detailed configuration templates and examples, see:
- Complete file templates and patternsreferences/configuration-templates.md