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.
Best use case
async-falcon-rails is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
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.
Teams using async-falcon-rails should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/async-falcon-rails/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How async-falcon-rails Compares
| Feature / Agent | async-falcon-rails | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
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.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# 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:
```bash
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):
```dockerfile
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`:
```dockerfile
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:
```ruby
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:
```ruby
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:
```ruby
# 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:
```ruby
require "async/cable"
```
2. Inside the `class Application < Rails::Application` block, after `config.load_defaults`, add:
```ruby
# 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:
```bash
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:
```ruby
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:
```yaml
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:
```yaml
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:
```yaml
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:
```yaml
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:
```yaml
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 patternsRelated Skills
asynchronous-programming-preference
Favors the use of async and await for asynchronous programming in Python.
async-operations
Specifies the preferred syntax for asynchronous operations using async/await and onMount for component initialization. This results in cleaner and more readable asynchronous code.
37signals-rails-style
Apply 37signals/DHH Rails conventions when writing Ruby on Rails code. Use when building Rails applications, reviewing Rails code, or making architectural decisions. Covers various aspects of Rails application architecture, design and dependencies.
async-repl-protocol
Async REPL Protocol
skill-rails-upgrade
Analyze Rails apps and provide upgrade assessments
asyncredux-undo-redo
Implement undo/redo functionality using state observers. Covers recording state history with stateObserver, creating a RecoverStateAction, implementing undo for the full state or partial state, and managing history limits.
asyncredux-observers
Set up observers for debugging and monitoring. Covers implementing actionObservers for dispatch logging, stateObserver for state change tracking, combining observers with globalWrapError, and using observers for analytics.
asyncredux-nonreentrant-mixin
Add the NonReentrant mixin to prevent an action from dispatching while already in progress. Covers preventing duplicate form submissions, avoiding race conditions, and protecting long-running operations.
asyncredux-async-actions
Creates AsyncRedux (Flutter) asynchronous actions for API calls, database operations, and other async work.
asyncpg-detection
This skill should be used when the user asks to "detect asyncpg usage", "find asyncpg patterns", "scan for asyncpg imports", or "identify asyncpg database code in FastAPI projects". It automatically scans Python files to identify asyncpg imports, connection patterns, and query execution methods that need conversion to SQLAlchemy.
asyncio
Python asyncio - Modern concurrent programming with async/await, event loops, tasks, coroutines, primitives, aiohttp, and FastAPI async patterns
asyncapi-docs
AsyncAPI specification handling for event-driven API documentation. Parse, validate, and generate documentation for message-based APIs including Kafka, MQTT, WebSocket, and AMQP systems.