CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

deploy

Deploy Rails applications to Railway. Handles first-time setup and re-deploys idempotently using Railway CLI. Trigger on: "deploy", "deploy to railway", "railway deploy", "发布", "部署", "上线".

Quality

85%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Deploy Rails App to Railway

Deploy the current Rails project to Railway using the Railway CLI. Works for both first-time deploys and re-deploys.

Prerequisites Check

Before starting, verify:

# 1. Railway CLI installed?
railway --version

# 2. Logged in?
railway whoami

If not logged in, instruct the user:

Please run: railway login
Then retry deployment.

Step 0: Prepare for Linux Build

Railway runs on Linux. Ensure Gemfile.lock includes the linux platform:

bundle lock --add-platform x86_64-linux

If 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.rb

Step 1: Check Link Status → Deploy Immediately if Already Linked

First: check if already linked:

railway status 2>&1

If 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>&1

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

  1. Find the correct project name from railway list
  2. Re-link: railway link --project <project-name> --service <service-name>

Step 2: Set Environment Variables

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-deploys

If 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? }
" | bash

Step 3: Ensure PostgreSQL Service (Idempotent)

Check if Postgres already exists:

railway status --json

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

  1. Open your Railway project: https://railway.com/project/<project-id> (Get the project ID from the Railway dashboard or cat .railway/config.json)
  2. Click "+ New" → "Database" → "PostgreSQL"
  3. Wait for the database to provision
  4. Come back and continue

After Postgres is provisioned, set the DATABASE_URL variable:

railway variable set DATABASE_URL='${{Postgres.DATABASE_URL}}' --skip-deploys

Step 4: Get Domain (Idempotent)

Check if a domain is already set:

railway domain --json

If no domain exists yet:

railway domain

Capture and display the domain URL to the user. Also set it as PUBLIC_HOST:

railway variable set PUBLIC_HOST=<domain-without-https> --skip-deploys

Step 5: Configure Storage Bucket (if needed)

Check 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_BUCKET

If 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 --json

The 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.

Step 6: Deploy

Upload and deploy the project:

railway up --detach

Show the user the deployment is in progress and they can monitor it with:

railway logs

No manual migration needed. The bin/docker-entrypoint script runs rails db:prepare automatically on container startup. Just wait for the deployment to complete.

Step 7: Verify Deployment

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 50

Step 8: Done

Print a summary:

✅ Deployment complete!
🌐 Platform URL: https://<domain>
📋 Monitor: railway logs
🔄 Re-deploy: just run deploy again

Notes

  • Idempotency: Running this skill multiple times is safe. Each step checks current state before acting.
  • Link detection: Use railway status to check if already linked — it's more reliable than checking .railway/config.json (works across machines and fresh clones).
  • Re-deploy: On subsequent runs, Steps 1–5 are all skipped or no-ops. Only Step 6 (upload) actually runs.
  • Secret key: Only set SECRET_KEY_BASE if not already set (check with railway variable list).
  • Database migrations: Handled automatically by 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.
  • PostgreSQL CLI bug: railway add --database postgres always fails with Unauthorized — always use the Web UI instead.
Repository

clacky-ai/openclacky

Path

lib/clacky/default_skills/deploy/SKILL.md

Last updated
First committed

Is this your skill?

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.