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.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
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"
manifest: skills/data/async-falcon-rails/SKILL.md
source content

Async 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:

  1. The project is a Rails application (check for
    Gemfile
    ,
    config/application.rb
    )
  2. The project structure includes
    config/environments/
    directory
  3. Bundle is available (
    bundle
    command works)
  4. 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"
  • gem "redis"
    (provides
    Async::Redis::Endpoint
    for endpoint parsing)

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:

  1. At the top of the file, after
    require "rails/all"
    , add:
require "async/cable"
  1. Inside the
    class Application < Rails::Application
    block, after
    config.load_defaults
    , add:
# 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.rb
  • app/channels/application_cable/connection.rb
  • app/channels/base_channel.rb
  • test/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:

  • init: true
    : Skips health checks for the job server (avoids 30-second deployment wait)
  • 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
    "127.0.0.1:6379:6379"
    prevents public exposure (localhost only)
  • Volume name
    redis_data
    prevents conflicts with other services (e.g., PostgreSQL)

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:

  1. Gemfile: Check that Puma is removed and all async gems are added
  2. Dockerfile: Verify
    libssl-dev
    is included in the apt-get install line
  3. Initializers: Verify
    config/initializers/async_job.rb
    exists and includes endpoint configuration (
    endpoint: redis_endpoint
    )
  4. Environments: Confirm
    async_job
    queue adapter in development.rb and production.rb
  5. Application: Verify
    async/cable
    require and fiber isolation config in application.rb
  6. Cable: Check that Action Cable is mounted in routes.rb
  7. Cable Config: Confirm cable.yml uses Redis for development and production
  8. Kamal (if applicable): Verify job server with
    init: true
    , Redis accessory, REDIS_URL, and multi-arch build config in deploy.yml

Important Notes

Redis/Valkey Dependency

This stack requires Redis-compatible database to be running:

  • Development: Start Redis locally with
    redis-server
    or use Docker
  • 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
    redis://localhost:6379/1
    (configured in cable.yml)
  • 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
    ,
    postgres_data
    , etc.

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
    libssl-dev
    system dependency in Dockerfile
  • Solution: Add
    libssl-dev
    to the apt-get install line in Dockerfile (see Step 2)
  • Verification: Check that the Dockerfile includes
    libssl-dev
    in the package list alongside
    build-essential
    ,
    git
    ,
    libyaml-dev
    , and
    pkg-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
    bundle update
    to resolve dependency issues
  • Verify network connectivity to rubygems.org

Redis Connection Errors

If the application cannot connect to Redis:

  • Development: Ensure Redis is running locally (
    redis-cli ping
    should return
    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
    options:
    with
    init: true
    to the job server configuration in deploy.yml (see Step 11.1)
  • Why it works:
    init: true
    tells Kamal to skip health checks for this service

References

For detailed configuration templates and examples, see:

  • references/configuration-templates.md
    - Complete file templates and patterns