From deploio
Day-to-day operations on a running Deploio app — config changes, routine log monitoring, console/exec access, and redeployments. This skill should be triggered for: "change env vars", "scale app", "add worker", "cron job", "scheduled job", "custom domain", "retry build", "deploy new version", "tail logs", "show me the logs", "open rails console", "run a rake task", "exec into app", "run a command in the app", "health probe", "basic auth", "check app status", "what apps do I have", "rollback deploy", "restart app", "previous version", "revert to last working". Do NOT use for first-time app creation (use deploio-deploy), diagnosing a crash or error (use deploio-debug), or provisioning databases/storage (use deploio-provision).
How this skill is triggered — by the user, by Claude, or both
Slash command
/deploio:deploio-manageThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Your role is coordinator. You never run commands yourself — you spawn `deploio-cli` agents with `mode: bypassPermissions` for all execution. Identify the operation, inspect current state if needed, confirm impactful changes, then execute.
Your role is coordinator. You never run commands yourself — you spawn deploio-cli agents with mode: bypassPermissions for all execution. Identify the operation, inspect current state if needed, confirm impactful changes, then execute.
Communication style: Speak to the user in plain language — describe what you'll do, never show raw nctl commands. Say "I'll add the Sidekiq worker to your app" not "I'll run nctl update app ... --worker-job-name=sidekiq". Agents manage the CLI entirely on the user's behalf.
Autoinfer app and project before asking. Run:
git remote get-url origin # https://github.com/acme/myapp → repo name only (not the org)
nctl auth whoami # → active organization (marked with *)
git branch --show-current # main → app=main
Derive: app = <branch> (e.g. main), org from the *-marked entry in nctl auth whoami (not the git URL), project = <org>-<repo> (e.g. renuotest-myapp — never just <repo>; nctl errors).
State your inference inline: "Using app main in project renuotest-myapp (org renuotest) — let me know if that's different." Proceed immediately. Only ask explicitly if there is no git remote configured, or if a subsequent nctl command fails because the organization doesn't exist.
From the conversation, also determine:
| User says | Operation | Go to |
|---|---|---|
| "show logs", "tail logs", "what do the logs say" | logs-app | → Logs |
| "build logs", "watch the build" | logs-build | → Logs |
| "rails console", "rails c", "open a shell", "run a command", "exec", "rake task" | exec | → Exec |
| "list apps", "what apps do I have", "check status", "is it running", "resource usage" | inspect | → Inspect |
| "basic auth credentials", "get the password" | basic-auth-credentials | → Inspect |
| "deploy new version", "change branch", "redeploy" | git-revision | → Execute |
| "retry build", "force rebuild" | retry-build | → Execute |
| "re-deploy", "retry release" | retry-release | → Execute |
| "add / change env var" | env | → Execute |
| "change build env", "change Ruby version", "change Node version" | build-env | → Execute |
| "scale", "more replicas" | replicas | → Execute |
| "upsize", "change size" | size | → Execute |
| "custom domain", "add host" | hosts | → Execute |
| "health check", "health probe" | health-probe | → Execute |
| "enable basic auth", "disable basic auth" | basic-auth | → Execute |
| "add worker", "Sidekiq", "good_job" | worker-job | → Execute |
| "remove worker", "delete worker" | delete-worker-job | → Execute |
| "add scheduled job", "add cron", "scheduled task" | scheduled-job | → Execute |
| "remove scheduled job", "remove cron" | delete-scheduled-job | → Execute |
| "run migrations on deploy", "deploy job" | deploy-job | → Execute |
| "disable deploy job", "remove deploy job" | delete-deploy-job | → Execute |
| "move repo", "change git URL" | git-url | → Execute |
| "rollback", "previous version", "revert deploy", "go back to last working" | rollback | → Execute |
| "restart app", "restart the app" | restart | → Execute |
| "pause app", "stop billing", "hibernate" | pause | → Execute |
| "resume app", "unpause", "start app" | resume | → Execute |
| "copy app", "duplicate app", "clone app" | copy | → Execute |
For rollback: explain the mechanism in plain terms: "I'll pull your release history, show you the available versions, then redeploy the one you choose."
Routine log monitoring during operations. No confirmation needed — read-only.
Spawn the deploio-cli agent with mode: bypassPermissions:
task: logs
app: <app-name>
project: <project>
type: app | worker_job | deploy_job | scheduled_job # "build" uses nctl logs build, not --type
follow: true | false
lines: <n>
since: <duration> # format: 1s | 1m | 1h | 48h
Log types for --type: app (default runtime output), worker_job, deploy_job, scheduled_job. Build logs use a separate nctl logs build command (see below).
# Follow live (most common)
nctl logs app <name> --project <project> -f
# Last N lines
nctl logs app <name> --project <project> -l 500
# Go back in time (1s, 1m, 1h, 48h)
nctl logs app <name> --project <project> -s 2h
# By type
nctl logs app <name> --project <project> -f --type worker_job
nctl logs app <name> --project <project> -f --type deploy_job
nctl logs app <name> --project <project> -f --type scheduled_job
# Clean output without metadata labels
nctl logs app <name> --project <project> -f --no-labels
Build logs live under nctl logs build, not nctl logs app --type build.
# Latest build for an app (-a = latest build for this app name)
nctl logs build <app-name> --project <project> -a
nctl logs build <app-name> --project <project> -a -l 5000
# Specific build by name
nctl get builds --project <project> # find the build name
nctl logs build <build-name> --project <project>
nctl logs build <build-name> --project <project> -f
nctl logs build <build-name> --project <project> -l 5000
Use
nctl logs build -awhen the user says "show me the build logs" — it fetches the latest automatically without needing to know the build name.
Run commands or open a shell inside a running app container. Routine operational use — Rails console, rake tasks, env inspection. For diagnosing a crash or error, use deploio-debug instead.
Always ask before running anything destructive (db:reset, db:drop, db:seed:replant).
Spawn the deploio-cli agent with mode: bypassPermissions:
task: exec
app: <app-name>
project: <project>
command: <command>
The agent runs nctl exec app <name> --project <project> -- <command>.
Database connections: When the user wants to connect to the database, the exec agent should:
- Attempt
rails dbconsole(orpsql) inside the container- If the connection is refused due to an IP restriction, automatically:
- Retrieve the user's current IP:
curl -s https://api.ipify.org- Find the database name from the app's
DATABASE_URLenv var- Add the IP to the allowlist:
nctl update postgresdatabase <db-name> --allowed-cidrs=<ip>/32- Retry the connection
- Report the outcome in plain terms — no nctl syntax in the user-facing message
# Rails console (most common)
nctl exec app <name> --project <project> -- bundle exec rails console
nctl exec app <name> --project <project> -- bin/rails c
nctl exec app <name> --project <project> -- rails c
# Interactive shell
nctl exec app <name> --project <project> -- bash
# Rails runner (non-interactive, good for quick queries)
nctl exec app <name> --project <project> -- bundle exec rails runner "puts User.count"
nctl exec app <name> --project <project> -- bundle exec rails runner "puts User.find(42).inspect"
# Rake tasks
nctl exec app <name> --project <project> -- bundle exec rake db:version
nctl exec app <name> --project <project> -- bundle exec rake db:seed
nctl exec app <name> --project <project> -- bundle exec rails db:seed:replant
# Check env vars
nctl exec app <name> --project <project> -- env | grep DATABASE
nctl exec app <name> --project <project> -- env | grep REDIS
# Destructive — always confirm explicitly before running
nctl exec app <name> --project <project> -- DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rails db:seed:replant
execconnects to the first available replica. In multi-replica setups, use logs to correlate which replica is relevant.
Read-only checks. No confirmation needed.
Spawn the deploio-cli agent with mode: bypassPermissions:
task: inspect
app: <app-name> # optional for list-apps
project: <project>
view: summary | yaml | stats | dns | basic-auth-credentials
# Status + URL
nctl get app <name> --project <project>
# Full config (env var keys, hosts, workers, jobs)
nctl get app <name> --project <project> -o yaml
# Check a specific env var value
nctl get app <name> --project <project> -o yaml | grep DATABASE_URL
# CPU, memory, restart counts per replica
# Columns: REPLICA, STATUS, CPU (millicores), CPU%, MEMORY (MiB), MEMORY%, RESTARTS, LASTEXITCODE
# Exit code 137 = OOM or storage limit exceeded
nctl get app <name> --project <project> -o stats
# DNS records for custom domain setup
nctl get app <name> --project <project> --dns
# Basic auth username + password
nctl get app <name> --project <project> --basic-auth-credentials
# All apps in a project
nctl get apps --project <project>
# All apps across every project (with stats)
nctl get apps -A
nctl get apps -A -o stats
For update operations, inspect current state first to detect conflicts.
task: inspect
app: <app-name>
project: <project>
view: yaml
Returns:
{
"status": "Running | Pending | Failed",
"current_revision": "main",
"current_size": "mini",
"replicas": 1,
"env_var_keys": ["RAILS_ENV", "DATABASE_URL"],
"hosts": ["myapp.deploio.app"],
"workers": [{ "name": "sidekiq", "command": "bundle exec sidekiq", "size": "mini" }],
"scheduled_jobs": [],
"deploy_job": null,
"health_probe_path": null,
"basic_auth_enabled": false
}
Use AskUserQuestion for any impactful operation (env var changes, scaling, size changes, rollbacks, worker/job mutations). Show the current → new value diff before asking:
Env var change:
Updating myapp:
| Variable | Current | New |
|---|---|---|
| RAILS_ENV | production | production (unchanged) |
| DATABASE_URL | *not set* | postgres://... |
question: "Apply this change?"
options:
- "Yes, proceed"
- "No, cancel"
Scale / size change:
Scaling myapp:
| Setting | Current | New |
|---|---|---|
| Replicas | 1 | 3 |
| Size | mini | standard-1 (billing impact) |
question: "Apply this change?"
options:
- "Yes, proceed"
- "No, cancel"
db:reset, db:seed:replant) — always use AskUserQuestion with an explicit "Yes, I understand this is destructive" option.If the user says "No, cancel" — do not spawn the executor.
Spawn the deploio-cli agent with mode: bypassPermissions:
task: update
app: <app-name>
project: <project>
operation: <operation>
values: <values>
Deploy new version / change branch:
nctl update app <name> --project <project> --git-revision=<branch|tag|sha>
# Pin to exact local commit:
nctl update app <name> --project <project> --git-revision=$(git rev-parse <branch>)
Force rebuild (after dependency changes or build env change):
nctl update app <name> --project <project> --retry-build
Re-deploy existing build (skip rebuild, re-run release):
nctl update app <name> --project <project> --retry-release
--retry-release: migration failed transiently, config-only change, transient infra issue--retry-build: build failed, dependency changed, build-time env var changedAdd / update runtime env vars (additive — other keys untouched):
nctl update app <name> --project <project> \
--env=KEY1=VALUE1 \
--env=KEY2=VALUE2
Add / update build-time env vars (triggers a rebuild):
nctl update app <name> --project <project> \
--build-env=RUBY_VERSION=3.3.3 \
--build-env=NODE_VERSION=20
Scale replicas:
nctl update app <name> --project <project> --replicas=<n>
Change instance size (micro | mini | standard-1 | standard-2):
nctl update app <name> --project <project> --size=<size>
Add / update custom hostname:
nctl update app <name> --project <project> --hosts=myapp.example.com,www.myapp.example.com
# Get DNS records to configure at the registrar:
nctl get app <name> --project <project> --dns
Configure health probe (Rails default is /up):
nctl update app <name> --project <project> \
--health-probe-path="/up" \
--health-probe-period-seconds=5
Toggle basic auth (HTTP basic auth in front of the app):
nctl update app <name> --project <project> --basic-auth=true
nctl update app <name> --project <project> --basic-auth=false
# Retrieve the generated credentials after enabling:
nctl get app <name> --project <project> --basic-auth-credentials
Add / update background worker (Sidekiq, GoodJob, etc.), up to 3 per app:
nctl update app <name> --project <project> \
--worker-job-name=sidekiq \
--worker-job-command="bundle exec sidekiq" \
--worker-job-size=mini
# GoodJob:
nctl update app <name> --project <project> \
--worker-job-name=good-job \
--worker-job-command="bundle exec good_job start" \
--worker-job-size=mini
Remove a worker:
nctl update app <name> --project <project> --delete-worker-job=<worker-name>
Add / update scheduled job (Deploio's term for time-triggered jobs — not "cron jobs"):
nctl update app <name> --project <project> \
--scheduled-job-name=cleanup \
--scheduled-job-command="rake cleanup:old_records" \
--scheduled-job-schedule="0 2 * * *" \
--scheduled-job-size=micro
Remove a scheduled job:
nctl update app <name> --project <project> --delete-scheduled-job=<job-name>
Add / update deploy job (runs before each release; default timeout 5m, max 30m; default retries 3, max 5):
nctl update app <name> --project <project> \
--deploy-job-command="bundle exec rake db:prepare" \
--deploy-job-name="migrate-database" \
--deploy-job-timeout=10m \
--deploy-job-retries=1
Rollback to a previous version:
# Find the last working git SHA from release history
nctl get releases <name> --project <project>
# Pin to the SHA of the last working release
nctl update app <name> --project <project> --git-revision=<sha>
A rollback is just a redeploy to an earlier commit. Show the release history from Phase 1 inspect output and ask the user which release to roll back to.
Restart the app (re-deploy existing build without changes):
nctl update app <name> --project <project> --retry-release
Remove a deploy job:
nctl update app <name> --project <project> --delete-deploy-job=<job-name>
If
--delete-deploy-jobis not accepted by your version of nctl, usenctl edit app <name> --project <project>and remove thedeployJobfield from the YAML.
Rotate SSH deploy key:
nctl update app <name> --project <project> \
--git-ssh-private-key-from-file=<path-to-key>
Move repo / change git URL:
nctl update app <name> --project <project> --git-url=https://github.com/neworg/repo
Pause app (scales to 0 replicas — stops billing; app URL returns 503 while paused):
nctl update app <name> --project <project> --replicas=0
Resume / unpause app:
nctl update app <name> --project <project> --replicas=1
Copy / duplicate app (starts paused by default, allowing review before activation):
# Copy within same project
nctl copy application <name> --target-name=<new-name>
# Copy to a different project
nctl copy application <name> --target-name=<new-name> --target-project=<project>
# Copy and start immediately
nctl copy application <name> --target-name=<new-name> --start
# Copy and include custom hosts (must be re-verified on new app)
nctl copy application <name> --target-name=<new-name> --copy-hosts
The executor reports back:
{ "status": "triggered" } — command accepted, release in progress{ "status": "failed", "error": "<nctl output>" } — command rejectedApp
Ready=True≠ latest build succeeded. Deploio may attempt a rebuild against git HEAD on any update, including env-only changes. The app can stay Running on a prior successful build while the newest build fails silently. Always checknctl get buildsafter a release — even config-only ones — and verify the live release is pinned to the newest build.
Create a task with TaskCreate when the release starts:
title: "Deploying <app-name>"
status: in_progress
After the executor confirms, spawn a monitor agent with mode: bypassPermissions:
task: watch-release
app: <app-name>
project: <project>
termination: stop ONLY when ALL three hold —
1. App Ready=True
2. Latest build status is Succeeded
3. Live release is pinned to that latest build
Otherwise stop on Failed or after 10 minutes.
The monitor polls all three signals every cycle, not just app status:
nctl get app <name> --project <project> --watch # app readiness
nctl get builds --project <project> | head # latest build status (Succeeded / Failed / Errored)
nctl get releases <name> --project <project> | head # which build the live release is pinned to
# If deploy job configured:
nctl logs app <name> --project <project> --type deploy_job -f
Surface these signals explicitly, not as parentheticals:
nctl get builds is Failed/Errored, flag it immediately, regardless of which release is live and regardless of whether the app is Running.<older>, but the previous release was on build <newer>. Platform fell back to an older build — the newer build attempt likely failed."Relay meaningful status changes:
"[30s] Release in progress — running deploy job..." "[90s] App is Running ✓ — release pinned to build
<X>, which succeeded."
On success, call TaskUpdate with status: completed.
On failure, call TaskUpdate with status: failed.
The final summary must always include a build-status line in plain language, on top of the HTTP sanity check:
"App is Running (release
<X>on build<Y>). Latest build attempt:<Z>— status: Succeeded/Failed."
On success:
Update applied. App is running.
What's next?
→ Tail the logs — I can follow them for you
→ Open rails console — I can exec in
→ Debug an issue — use deploio-debug
→ Add a database — use deploio-provision
On failure, translate the error and suggest a fix using the table below.
Deploio config flows: Organization → Project → App (lower layers override). For the full reference including how to set project-wide defaults, read skills/deploio-manage/references/config-layers.md.
| Symptom | Likely cause | Fix |
|---|---|---|
| Update triggers rebuild unexpectedly | Using --env for a build-time var | Use --build-env for build-time vars, --env for runtime only |
| Deploy job not running | --deploy-job-name or --deploy-job-command missing | Both --deploy-job-name and --deploy-job-command are required |
| Deploy job times out | Slow migration | Increase --deploy-job-timeout=15m |
| App running but new code not live | Revision not changed | Check nctl get app <name> -o yaml for current SHA |
Host not resolving after --hosts | DNS not configured | Run nctl get app <name> --dns and set CNAME/TXT at registrar |
| Worker not appearing after update | Name conflict | List existing workers with nctl get app <name> -o yaml |
| Worker not deleted | Wrong flag used | Use --delete-worker-job for workers, --delete-scheduled-job for crons |
| Health probe causing restart loop | Wrong path | Rails default is /up — verify it returns 200 |
| Scale command accepted but no new replicas | Project quota | Try a smaller size or contact Deploio support |
exec connects to wrong replica | Multi-replica app | exec always uses the first available — use logs to correlate |
npx claudepluginhub renuo/deploio-claude-plugin --plugin deploioProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.