Deploy Rails applications to Railway. Handles first-time setup and re-deploys idempotently using Railway CLI. Trigger on: "deploy", "deploy to railway", "railway deploy", "发布", "部署", "上线".
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Deploy the current Rails project to Railway using the Railway CLI. Works for both first-time deploys and re-deploys.
Before starting, verify:
# 1. Railway CLI installed?
railway --version
# 2. Logged in?
railway whoamiIf not logged in, instruct the user:
Please run: railway login
Then retry deployment.Railway runs on Linux. Ensure Gemfile.lock includes the linux platform:
bundle lock --add-platform x86_64-linuxIf the project uses a Dockerfile builder (check railway.toml for builder = "DOCKERFILE"), no Procfile is needed — skip creating one.
Only create a Procfile if there is no Dockerfile:
web: bundle exec puma -C config/puma.rbFirst: check if already linked:
railway status 2>&1If output contains Project: → project is already linked.
Skip Steps 2–5 entirely and jump to Step 6 (Deploy).
If output contains "not linked" or an error → not linked yet. Try linking to an existing project first — list available projects:
railway list 2>&1 | grep -i "<app-name>"If a matching project is found, link it:
railway link --project <project-name> --service <service-name> 2>&1Only if no existing project is found, init a new one:
railway init -n <app-name>The app-name should match the current directory name (e.g., my-rails-app).
⚠️ NEVER run railway init when already linked or when an existing project exists.
It silently creates a brand-new Railway project. If this happens by mistake:
railway listrailway link --project <project-name> --service <service-name>Set required Rails production variables. Use --skip-deploys to avoid triggering premature deploys:
# Generate a secret key base
SECRET_KEY_BASE=$(bundle exec rails secret)
railway variable set SECRET_KEY_BASE=$SECRET_KEY_BASE --skip-deploys
railway variable set RAILS_ENV=production --skip-deploys
railway variable set RAILS_LOG_TO_STDOUT=true --skip-deploys
railway variable set RAILS_SERVE_STATIC_FILES=true --skip-deploysIf the project uses any other env vars (check .env.example or config/application.yml.example if they exist), prompt the user to provide values and set them too.
If the project uses config/application.yml (Figaro gem), read it and set all values as Railway variables:
# Read application.yml and set each key=value pair
ruby -ryaml -e "
data = YAML.safe_load(File.read('config/application.yml')) || {}
data.each { |k, v| puts %(railway variable set #{k}=#{v} --skip-deploys) unless v.to_s.empty? }
" | bashCheck if Postgres already exists:
railway status --jsonParse the JSON output. If a service with type postgres or name containing postgres/Postgres is already found, skip with: ✅ PostgreSQL already provisioned
⚠️ IMPORTANT: railway add --database postgres has a known CLI bug that always returns Unauthorized.
Do NOT attempt to run this command. Instead, instruct the user to add PostgreSQL manually via the Railway Web UI:
https://railway.com/project/<project-id>
(Get the project ID from the Railway dashboard or cat .railway/config.json)After Postgres is provisioned, set the DATABASE_URL variable:
railway variable set DATABASE_URL='${{Postgres.DATABASE_URL}}' --skip-deploysCheck if a domain is already set:
railway domain --jsonIf no domain exists yet:
railway domainCapture and display the domain URL to the user. Also set it as PUBLIC_HOST:
railway variable set PUBLIC_HOST=<domain-without-https> --skip-deploysCheck if the project uses S3-compatible storage by reading config/storage.yml. If it contains an amazon or s3 service section, storage bucket configuration is required.
⚠️ Storage bucket requires Railway Hobby plan ($5/month minimum). Confirm with the user before proceeding.
Check if bucket env vars are already set:
railway variables --json | grep STORAGE_BUCKETIf not set, create a bucket and configure the variables:
# Create bucket (choose region: iad=US East, sjc=US West, ams=EU, sin=Asia)
railway bucket create <app-name>-storage --region iad --json
# Get credentials
railway bucket credentials --bucket <app-name>-storage --jsonThe credentials JSON will contain: accessKeyId, secretAccessKey, region, endpoint, bucketName.
Set them as environment variables:
railway variables set \
STORAGE_BUCKET_ACCESS_KEY_ID=<accessKeyId> \
STORAGE_BUCKET_SECRET_ACCESS_KEY=<secretAccessKey> \
STORAGE_BUCKET_REGION=<region> \
STORAGE_BUCKET_NAME=<bucketName> \
STORAGE_BUCKET_ENDPOINT=<endpoint> \
--skip-deploys⚠️ Missing these variables will cause a hard crash at boot (Aws::Errors::MissingRegionError) because the AWS SDK initializes at startup, not lazily.
If the project does not use S3 storage, skip this step entirely.
Upload and deploy the project:
railway up --detachShow the user the deployment is in progress and they can monitor it with:
railway logsNo manual migration needed. The bin/docker-entrypoint script runs rails db:prepare automatically on container startup. Just wait for the deployment to complete.
After deployment completes (wait ~30 seconds), verify the app is running:
# Should return 200
curl -s -o /dev/null -w "%{http_code}" https://<domain>/If it returns 200, deployment is successful. If not, check logs:
railway logs --tail 50Print a summary:
✅ Deployment complete!
🌐 Platform URL: https://<domain>
📋 Monitor: railway logs
🔄 Re-deploy: just run deploy againrailway status to check if already linked — it's more reliable than checking .railway/config.json (works across machines and fresh clones).SECRET_KEY_BASE if not already set (check with railway variable list).bin/docker-entrypoint via rails db:prepare — never run railway run bundle exec rails db:migrate as Railway's internal DB IP is not accessible from local machine.railway add --database postgres always fails with Unauthorized — always use the Web UI instead.clacky-ai/openclacky
e7b898f
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.