agentsclimarketplace

Vibe deploy

Skill aakashdhar/vibe-skill/vibe-deploy

vibe-* is a collection of Claude Code CLI skills. Each skill is a structured instruction set that tells Claude Code exactly what to do at a specific moment in a project — how to plan it, build it, review it, test it, deploy it, and hand it off.

Install
npx -y skills add aakashdhar/vibe-skill --skill vibe-deploy

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Prepares a vibe-* project for deployment to 7 platforms: Railway, Render, Fly.io, Heroku, Vercel, Netlify, GitHub Pages. Detects stack automatically (FastAPI, Express, Next.js, Django, React, Vue, static). Patches source files (port binding to $PORT, adds /health endpoint, production CORS). Generates platform config files scoped per service — backend, web, worker. Inlines non-secret env vars directly in config files per service. Links database connections using platform-native syntax. Generates ENV_SETUP.md with per-service CLI commands for secrets only. Handles background workers and cron services if the project requires them. Optionally generates GitHub Actions CI/CD workflow. Produces DEPLOY.md step-by-step checklist. Triggers: deploy: railway, deploy: render, deploy: fly, deploy: heroku, deploy: vercel, deploy: netlify, deploy: github-pages.

SKILL.md

22.8 KB, as published. Nobody here has run it

Vibe Deploy Skill

Prepares your project for deployment — config files, source patches, env vars, database provisioning, worker services, and a human checklist. Deployment itself is manual. Everything before that is automated.


Supported platforms

TriggerPlatformBest forDatabase
deploy: railwayRailwayFull-stack, multi-service, APIsPostgreSQL plugin
deploy: renderRenderFull-stack, background workersPostgreSQL service
deploy: flyFly.ioContainers, globally distributedFly Postgres
deploy: herokuHerokuFull-stack, legacy projectsHeroku Postgres add-on
deploy: vercelVercelNext.js, frontend, serverlessExternal only
deploy: netlifyNetlifyStatic, JAMstack, serverless functionsExternal only
deploy: github-pagesGitHub PagesStatic sites onlyNone

Step 0 — Detect stack and services

Read the project to understand what is being deployed:

# Detect package managers and frameworks
cat package.json 2>/dev/null
cat requirements.txt 2>/dev/null
cat pyproject.toml 2>/dev/null
cat Dockerfile 2>/dev/null

# Detect framework-specific files
ls next.config.js next.config.ts 2>/dev/null
ls manage.py 2>/dev/null
ls main.py app.py 2>/dev/null
ls vite.config.js vite.config.ts 2>/dev/null

# Detect service directories
ls -la 2>/dev/null

# Read vibe context if available
cat vibe/ARCHITECTURE.md 2>/dev/null
cat CLAUDE.md 2>/dev/null

# Check for existing deploy configs
ls railway.json render.yaml fly.toml Procfile vercel.json netlify.toml 2>/dev/null
ls .github/workflows/ 2>/dev/null

Identify:

  • Services present — backend API, frontend web, background worker, cron job, static site
  • Stack per service:
    • Python: FastAPI, Django, Flask — check for uvicorn, gunicorn, manage.py
    • Node: Express, Next.js, Fastify — check package.json scripts and dependencies
    • Frontend: Next.js (SSR), React/Vite (static), Vue, plain HTML
    • Worker: Celery, Bull, custom scripts — check for worker entry points
  • Database in use — PostgreSQL, MySQL, SQLite, Redis
  • Port usage — is the app already binding to $PORT or hardcoded?
  • Health endpoint — does GET /health exist?
  • CORS config — is it set to * or localhost?
  • Existing migration system — Alembic, Prisma, Knex, Django migrations

Step 1 — Ask one question

Ask the user once before proceeding:

"Do you want a GitHub Actions workflow for automatic deployment to [platform] on push to main? · Yes — generates .github/workflows/deploy-[platform].yml · No — manual deployment only"

Wait for answer. Then proceed with full generation.


Step 2 — Patch source files

Patch existing source files to be production-ready. Do not rewrite files — surgical edits only. Report every file changed and what was changed.

2a — Port binding

FastAPI / Python: Find uvicorn.run(...) or the startup command and ensure it uses $PORT:

# Before
uvicorn.run("main:app", host="0.0.0.0", port=8000)

# After
import os
port = int(os.environ.get("PORT", 8000))
uvicorn.run("main:app", host="0.0.0.0", port=port)

If using a Procfile or start command (not uvicorn.run in code), patch the command instead:

web: uvicorn main:app --host 0.0.0.0 --port $PORT

Express / Node:

// Before
app.listen(3000)

// After
const PORT = process.env.PORT || 3000
app.listen(PORT)

Next.js: Next.js reads PORT automatically — no patch needed.

Django:

# Procfile / start command
web: gunicorn myapp.wsgi --bind 0.0.0.0:$PORT

2b — Health endpoint

If GET /health does not exist, add it to the main app file.

FastAPI:

@app.get("/health")
async def health():
    return {"status": "ok"}

Express:

app.get('/health', (req, res) => res.json({ status: 'ok' }))

Django: Add to urls.py:

from django.http import JsonResponse
path('health/', lambda r: JsonResponse({'status': 'ok'})),

Next.js: Create app/api/health/route.ts:

export async function GET() {
  return Response.json({ status: 'ok' })
}

2c — Production CORS

Replace hardcoded * or localhost with FRONTEND_URL env var.

FastAPI:

# Before
origins = ["*"]
# or
origins = ["http://localhost:3000"]

# After
import os
origins = [os.environ.get("FRONTEND_URL", "http://localhost:3000")]

Express:

// Before
app.use(cors({ origin: '*' }))

// After
app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:3000' }))

Django: Update CORS_ALLOWED_ORIGINS in settings:

CORS_ALLOWED_ORIGINS = [os.environ.get("FRONTEND_URL", "http://localhost:3000")]

2d — Database migration at startup

Append migration to the start command so it runs automatically on each deploy.

Alembic (FastAPI/Python):

web: alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port $PORT

Django:

web: python manage.py migrate && gunicorn myapp.wsgi --bind 0.0.0.0:$PORT

Prisma (Node):

web: npx prisma migrate deploy && node server.js

Knex (Node):

web: npx knex migrate:latest && node server.js

2e — Worker / cron service

If a worker or cron service is detected, identify the entry point and add it as a separate service in the platform config. Do not patch the worker source — just configure it correctly in the deployment files.


Step 3 — Generate platform config

Railway — railway.json

Single file at repo root. Covers all services. Non-secret env vars inlined per service. Secrets get placeholder comments. DATABASE_URL linked via Railway's internal reference syntax.

{
  "$schema": "https://railway.app/railway.schema.json",
  "build": {
    "builder": "NIXPACKS"
  },
  "services": {
    "backend": {
      "source": {
        "repo": ".",
        "rootDirectory": "backend"
      },
      "build": {
        "buildCommand": "pip install -r requirements.txt"
      },
      "deploy": {
        "startCommand": "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port $PORT",
        "healthcheckPath": "/health",
        "healthcheckTimeout": 30,
        "restartPolicyType": "ON_FAILURE",
        "restartPolicyMaxRetries": 3
      },
      "variables": {
        "ENVIRONMENT": "production",
        "FRONTEND_URL": "${{web.RAILWAY_PUBLIC_DOMAIN}}",
        "DATABASE_URL": "${{Postgres.DATABASE_URL}}",
        "PORT": "8000"
      }
    },
    "web": {
      "source": {
        "repo": ".",
        "rootDirectory": "web"
      },
      "build": {
        "buildCommand": "npm install && npm run build"
      },
      "deploy": {
        "startCommand": "npm run start",
        "healthcheckPath": "/api/health",
        "healthcheckTimeout": 30
      },
      "variables": {
        "NODE_ENV": "production",
        "NEXT_PUBLIC_API_URL": "${{backend.RAILWAY_PUBLIC_DOMAIN}}"
      }
    }
  }
}

Worker service (add if detected):

"worker": {
  "source": { "repo": ".", "rootDirectory": "backend" },
  "deploy": {
    "startCommand": "celery -A app.celery worker --loglevel=info"
  },
  "variables": {
    "DATABASE_URL": "${{Postgres.DATABASE_URL}}"
  }
}

Cron service (add if detected):

"cron": {
  "source": { "repo": ".", "rootDirectory": "backend" },
  "deploy": {
    "startCommand": "python -m app.cron",
    "cronSchedule": "0 * * * *"
  },
  "variables": {
    "DATABASE_URL": "${{Postgres.DATABASE_URL}}",
    "CRON_SECRET": "set-in-dashboard"
  }
}

Render — render.yaml

Single file at repo root. Multi-service blueprint. fromDatabase links DATABASE_URL automatically. generateValue: true for auto-generated secrets.

databases:
  - name: [project]-db
    databaseName: [project]
    user: [project]
    plan: free

services:
  - type: web
    name: [project]-backend
    runtime: python
    rootDir: backend
    buildCommand: pip install -r requirements.txt
    startCommand: alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port $PORT
    healthCheckPath: /health
    envVars:
      - key: ENVIRONMENT
        value: production
      - key: DATABASE_URL
        fromDatabase:
          name: [project]-db
          property: connectionString
      - key: JWT_SECRET
        generateValue: true
      - key: CRON_SECRET
        generateValue: true
      - key: FRONTEND_URL
        sync: false   # set in dashboard — Railway web URL not known at config time
      - key: GEMINI_API_KEY
        sync: false   # set in dashboard
      - key: OPENWEATHER_API_KEY
        sync: false   # set in dashboard

  - type: web
    name: [project]-web
    runtime: node
    rootDir: web
    buildCommand: npm install && npm run build
    startCommand: npm run start
    healthCheckPath: /api/health
    envVars:
      - key: NODE_ENV
        value: production
      - key: NEXT_PUBLIC_API_URL
        sync: false   # set to backend URL after first deploy

  - type: worker
    name: [project]-worker
    runtime: python
    rootDir: backend
    buildCommand: pip install -r requirements.txt
    startCommand: celery -A app.celery worker --loglevel=info
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: [project]-db
          property: connectionString

Fly.io — fly.toml per service

Fly deploys one app per toml. Generate one per service directory.

backend/fly.toml:

app = "[project]-backend"
primary_region = "bom"  # Mumbai — closest to India

[build]

[env]
  ENVIRONMENT = "production"
  PORT = "8080"

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0

  [[http_service.checks]]
    grace_period = "10s"
    interval = "30s"
    method = "GET"
    path = "/health"
    timeout = "5s"

[[vm]]
  cpu_kind = "shared"
  cpus = 1
  memory_mb = 512

[deploy]
  release_command = "alembic upgrade head"

web/fly.toml:

app = "[project]-web"
primary_region = "bom"

[build]

[env]
  NODE_ENV = "production"

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0

  [[http_service.checks]]
    grace_period = "10s"
    interval = "30s"
    method = "GET"
    path = "/api/health"
    timeout = "5s"

[[vm]]
  cpu_kind = "shared"
  cpus = 1
  memory_mb = 512

Dockerfile — generate if not present. Detect stack and generate minimal production Dockerfile:

FastAPI example:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

Heroku — Procfile + app.json

Procfile (root):

web: alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port $PORT
worker: celery -A app.celery worker --loglevel=info

app.json (root):

{
  "name": "[project]",
  "description": "[project description from BRIEF.md]",
  "repository": "",
  "addons": [
    {
      "plan": "heroku-postgresql:mini"
    }
  ],
  "env": {
    "ENVIRONMENT": {
      "value": "production"
    },
    "JWT_SECRET": {
      "description": "Secret key for JWT signing",
      "generator": "secret"
    },
    "FRONTEND_URL": {
      "description": "URL of the frontend web app",
      "required": true
    },
    "GEMINI_API_KEY": {
      "description": "Google Gemini API key — get from aistudio.google.com",
      "required": false
    }
  },
  "formation": {
    "web": { "quantity": 1, "size": "eco" },
    "worker": { "quantity": 1, "size": "eco" }
  },
  "buildpacks": [
    { "url": "heroku/python" }
  ]
}

Vercel — vercel.json

Frontend only. Generated at web service root or repo root.

{
  "framework": "nextjs",
  "buildCommand": "npm run build",
  "outputDirectory": ".next",
  "installCommand": "npm install",
  "rewrites": [
    {
      "source": "/api/:path*",
      "destination": "https://[backend-url]/api/:path*"
    }
  ],
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-XSS-Protection", "value": "1; mode=block" }
      ]
    }
  ],
  "env": {
    "NEXT_PUBLIC_API_URL": "@next_public_api_url"
  }
}

For static React/Vite (no SSR):

{
  "buildCommand": "npm run build",
  "outputDirectory": "dist",
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}

Netlify — netlify.toml

[build]
  command = "npm run build"
  publish = "dist"         # or "out" for Next.js static export
  functions = "netlify/functions"

[build.environment]
  NODE_VERSION = "20"
  NODE_ENV = "production"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"
    X-Content-Type-Options = "nosniff"
    Cache-Control = "public, max-age=0, must-revalidate"

[[headers]]
  for = "/assets/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

GitHub Pages — .github/workflows/deploy.yml

Static export only. Generates the workflow even if user said no to GitHub Actions (GitHub Pages requires a workflow — it's how it deploys).

name: Deploy to GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm install
      - run: npm run build
        env:
          NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
      - uses: actions/upload-pages-artifact@v3
        with:
          path: ./out   # or dist for Vite

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/deploy-pages@v4
        id: deployment

Step 4 — GitHub Actions CI/CD (if user said yes)

Generate .github/workflows/deploy-[platform].yml. Skip for Vercel and Netlify (they handle CI/CD natively — a workflow is unnecessary). For GitHub Pages, the workflow is always generated regardless of this answer (it's required).

Railway

name: Deploy to Railway

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install -g @railway/cli
      - run: railway up --service backend
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
      - run: railway up --service web
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

Render

name: Deploy to Render

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Render Deploy — backend
        run: |
          curl -X POST "${{ secrets.RENDER_BACKEND_DEPLOY_HOOK }}"
      - name: Trigger Render Deploy — web
        run: |
          curl -X POST "${{ secrets.RENDER_WEB_DEPLOY_HOOK }}"

Fly.io

name: Deploy to Fly.io

on:
  push:
    branches: [main]

jobs:
  deploy-backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy --config backend/fly.toml --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

  deploy-web:
    runs-on: ubuntu-latest
    needs: deploy-backend
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy --config web/fly.toml --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Heroku

name: Deploy to Heroku

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: akhileshns/[email protected]
        with:
          heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
          heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
          heroku_email: ${{ secrets.HEROKU_EMAIL }}

Step 5 — Generate ENV_SETUP.md

Secrets only — values that cannot be inlined in config files. Group by service. Include exact CLI commands per platform.

Railway example:

# [Project] — Railway Secrets Setup

Run these after `railway link` connects your local project to Railway.
Non-secret env vars are already set in railway.json.

## Backend service secrets

railway variables set JWT_SECRET="$(openssl rand -hex 32)" --service backend
railway variables set GEMINI_API_KEY="get from aistudio.google.com/app/apikey" --service backend
railway variables set OPENWEATHER_API_KEY="get from openweathermap.org/api_keys" --service backend
railway variables set CRON_SECRET="$(openssl rand -hex 32)" --service backend

## Web service secrets

railway variables set NEXTAUTH_SECRET="$(openssl rand -hex 32)" --service web

## After first deploy — update with real URLs

railway variables set FRONTEND_URL="https://[your-web-url].up.railway.app" --service backend
railway variables set NEXT_PUBLIC_API_URL="https://[your-backend-url].up.railway.app" --service web
railway variables set NEXT_PUBLIC_WS_URL="wss://[your-backend-url].up.railway.app" --service web

Render — fewer secrets because generateValue: true handles JWT_SECRET and CRON_SECRET automatically. Only external API keys need manual setting.

Fly.io:

# Backend secrets
fly secrets set JWT_SECRET="$(openssl rand -hex 32)" --app [project]-backend
fly secrets set GEMINI_API_KEY="your-key" --app [project]-backend
fly secrets set DATABASE_URL="your-fly-postgres-url" --app [project]-backend

# Web secrets
fly secrets set NEXT_PUBLIC_API_URL="https://[project]-backend.fly.dev" --app [project]-web

Step 6 — Generate DEPLOY.md

Step-by-step human checklist, platform-specific. Every step is binary — done or not done. Nothing assumed. Links to official docs where helpful.

Railway DEPLOY.md structure:

# [Project] — Deploy to Railway

## Prerequisites
- [ ] Railway account — railway.app
- [ ] Railway CLI installed — `npm install -g @railway/cli`
- [ ] GitHub repository connected to Railway

## Step 1 — Create Railway project
1. Go to railway.app → New Project → Deploy from GitHub repo
2. Select your repository
3. Railway will detect the services from railway.json automatically

## Step 2 — Provision PostgreSQL
1. In Railway dashboard → Add Service → Database → PostgreSQL
2. The DATABASE_URL is already wired in railway.json — no manual linking needed

## Step 3 — Set secrets
Run the commands in ENV_SETUP.md exactly as written.
Do not skip any — missing secrets will cause the deploy to fail silently.

## Step 4 — Deploy
Railway auto-deploys on push to main.
Or: railway up --service backend && railway up --service web

## Step 5 — Verify
- [ ] Backend health: https://[backend-url].up.railway.app/health → {"status":"ok"}
- [ ] Web loads: https://[web-url].up.railway.app
- [ ] Database migrations ran: check Railway logs for "alembic upgrade head" success
- [ ] Login flow works end to end

## Step 6 — Update cross-service URLs
After first deploy, real URLs are known. Update:
railway variables set FRONTEND_URL="https://[web-url].up.railway.app" --service backend
railway variables set NEXT_PUBLIC_API_URL="https://[backend-url].up.railway.app" --service web
Then redeploy: railway up --service backend && railway up --service web

## Step 7 — Set admin user (if applicable)
[Any platform-specific admin setup from the project]

Step 7 — Confirm to user

Present a complete summary:

DEPLOY PACKAGE — [Project Name]
Platform: [platform]
Services detected: [backend / web / worker / cron]
GitHub Actions: [yes / no]

Files generated:
  ✅ [config file] — [description]
  ✅ ENV_SETUP.md — secrets setup commands per service
  ✅ DEPLOY.md — step-by-step checklist

Source files patched:
  ✅ [file] — [what was changed]
  ✅ [file] — [what was changed]

⚠️ Action required before deploying:
  · Run ENV_SETUP.md commands to set secrets
  · [any platform-specific manual steps]
  [· CREDENTIALS.md — fill values if handoff package also present]

After first deploy — update cross-service URLs:
  · [exact commands]

Absolute rules

Never write actual secret values. ENV_SETUP.md contains placeholder instructions only. Commands use $(openssl rand -hex 32) generators where possible. External API keys always say "get from [source]" — never invent values.

Surgical source patches only. Never rewrite a file — only add or modify the specific lines needed. Always report what was changed and why.

Stack detection before generation. Never assume the stack — always read the files first. If the stack cannot be determined, ask before generating.

Worker/cron services only if present. Detect them from the codebase — don't add them if they don't exist. A non-existent worker service in a config file will fail the deploy.

Platform compatibility warnings. If the user runs deploy: github-pages on a server-side app, warn: "GitHub Pages only supports static sites. This project has a backend service that cannot be deployed to GitHub Pages. Consider Railway, Render, or Fly.io for this project."

Cross-service URL note always included. After first deploy, cross-service URLs (FRONTEND_URL, NEXT_PUBLIC_API_URL) must be updated with real values. Always include this as a step in DEPLOY.md and in the confirmation summary.

Regenerate portal if vibe-handoff was run. If vibe/handoff/ exists, remind the user to regenerate the handoff portal after updating DEPLOY.md with real URLs post-deploy.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.