Deploio Deploy
Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create ...
Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create a Deploio app", "how do I deploy to Deploio", "host on Deploio", "push to Deploio", "new app on Deploio", "first deploy to Deploio", "set up a new Deploio app", or setting up a new Deploio app from scratch. Covers auth, project setup, git credential resolution, buildpack/Dockerfile detection, and bu...
Install
Quick install
npx skills add https://github.com/org/reponpx skills add org/repo --agent claude-codenpx skills add org/repo --agent cursornpx skills add org/repo --agent codexnpx skills add org/repo --agent opencodenpx skills add org/repo --agent github-copilotnpx skills add org/repo --agent windsurfMore install options
Shorthand — useful for multi-skill repos:
npx skills add org/repoManual — clone the repo and drop the folder into your agent's skills directory:
git clone https://github.com/org/repo.gitcp -r repo ~/.claude/skills/Deploio Deploy
Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create a Deploio app", "how do I deploy to Deploio", "host on Deploio", "push to Deploio", "new app on Deploio", "first deploy to Deploio", "set up a new Deploio app", or setting up a new Deploio app from scratch. Covers auth, project setup, git credential resolution, buildpack/Dockerfile detection, and bu...
---
name: deploio-deploy
description: Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create a Deploio app", "how do I deploy to Deploio", "host on Deploio", "push to Deploio", "new app on Deploio", "first deploy to Deploio", "set up a new Deploio app", or setting up a new Deploio app from scratch. Covers auth, project setup, git credential resolution, buildpack/Dockerfile detection, and build monitoring. Do NOT use for apps already running on Deploio (use deploio-manage to update them).
license: MIT
metadata:
version: 1.3.0
---
Deploio: First-Time App Deployment
Your role is coordinator. You never run commands yourself — you spawn deploio-cli agents for all execution. Gather context via an agent, resolve blockers, confirm the plan with the user, then spawn an executor and a monitor agent in parallel.
Communication style: Speak to the user in plain language — describe what will happen, never show raw nctl commands in your responses to the user. Agents manage the CLI entirely on the user's behalf. Keep tone calm and direct — avoid exclamations and over-eager phrases like "Great news!" or "Awesome!".
---
Phase 0: Pre-flight check (from conversation context only — no commands)
Before spawning any agent, evaluate these four conditions from what the user has already told you:
| Condition | Known if… |
|---|---|
| nctl_known | User mentioned installing nctl, or ran it in this session |
| remote_known | A git URL is visible in the conversation |
| auth_known | User mentioned a Deploio project name or logged in |
| framework_known | User said "Rails app", "Next.js", etc. |
If remote_known is false, run git remote get-url origin and git branch --show-current to discover the URL and branch. If a remote is found, derive app = <branch> (e.g. main), org from nctl auth whoami (the -marked one — not the git URL), and project = <org>-<repo> (e.g. renuotest-myapp — never just <repo>; nctl errors). State: "I'll deploy project renuotest-myapp, app main, in organization renuotest — let me know if that's different."* Only ask for a URL from scratch if no remote is configured.
Pass any known values into the gather-context spec as hints to speed up detection.
---
Phase 1: Gather context
Spawn the deploio-cli agent with mode: bypassPermissions and this spec:
task: gather-context
hints:
remote_url: <from context, or null>
framework: <from context, or null>
nctl_installed: <from context, or null>
What the agent must do
Constraints: Do not run any nctl commands during context gathering — nctl commands may require auth and will produce misleading errors before the project exists. Use only: git commands, file system reads, and nctl version.
Detection steps:
nctl version→ setnctl_installed: true/false; require at least v1.16.0 for Deploio support- Use the
remote_urlhint if provided; otherwise rungit remote get-url origin→ setremote_url git branch --show-current→ setbranch- Read project files to detect
app_typeandport(framework details live inreferences/<FRAMEWORK>.md):
| File present | app_type | default port |
|---|---|---|
| Dockerfile | docker | read from EXPOSE line, else 8080 |
| Gemfile containing rails | rails | 3000 |
| package.json | nodejs | 3000 |
| manage.py + requirements.txt | django | 8000 |
| app.py or main.py + requirements.txt | flask | 8000 |
| composer.json | php | 8080 |
| go.mod | go | 8080 |
| none matched | unknown | null |
- Detect
git_auth_type:
- If
remote_urlstarts withgit@→ssh - If
remote_urlstarts withhttps://→https - If
remote_urlis null →none
- Check for multiple subdirectories each containing a
Gemfile,package.json, orgo.mod→ setis_monorepo: true/false
- Run
nctl auth whoami(only if nctl is installed). Parse the output to find:
active_org: the organization marked within the list (e.g.renuotest)available_orgs: the full list of organization names
The active_org is the one already selected — use it without asking. Only surface org selection if active_org is null (nothing marked active).
- Check for a
.deploio.yamlfile in the repo root → sethas_deploio_yaml: true/false. If present, note which keys it defines (deployJob, size, env, healthProbe, etc.) so the coordinator can skip asking about those.
Return schema (JSON)
{
"nctl_installed": true,
"remote_url": "https://github.com/org/repo.git",
"branch": "main",
"app_type": "rails",
"port": 3000,
"git_auth_type": "https",
"is_monorepo": false,
"active_org": "acme-production",
"available_orgs": ["acme-production", "acme-staging"],
"has_deploio_yaml": false,
"blockers": []
}
blockers is a list of strings, e.g. ["nctl not installed", "no git remote"].
---
Phase 2: Resolve blockers
Handle each blocker before proceeding:
nctl not installed:
brew install ninech/tap/nctl # macOS
# Linux: download from https://github.com/ninech/nctl/releases/latestRe-run Phase 1 after install.
nctl version too old: Deploio requires nctl v1.16.0 or newer. Run brew upgrade ninech/tap/nctl (macOS) or re-download from releases.
No git remote: Deploio always pulls from a git host — it never receives files directly.
git remote add origin https://github.com/<user>/<repo>.git
git push -u origin mainContinue once the user confirms it's pushed.
nctl not authenticated: Ask the user to run nctl auth login (opens browser OAuth) then nctl auth set-project <project>.
app_type unknown: Ask the user what runtime their app uses before proceeding.
---
Phase 2b: Git credentials (private repos)
GitHub note: GitHub does not support HTTPS deploy tokens for nctl. Use SSH deploy keys for GitHub private repos.
If git_auth_type is ssh:
- Ask the user for the path to their deploy key, or offer to use
~/.ssh/id_ed25519 - They can generate a dedicated key:
ssh-keygen -t ed25519 -f ~/deploio.key -N '' - Pass
git_ssh_key_pathto the executor spec
If git_auth_type is https (GitLab or Bitbucket only):
- GitLab: Settings → Access Tokens →
read_repository; pass the token name asgit_usernameand the token value asgit_password - Bitbucket: Repository settings → Access tokens →
Repository read; usex-auth-tokenas the username field - Pass
git_usernameandgit_passwordto the executor spec
If the repo is public, skip this phase.
---
Phase 2c: Organization selection
nctl auth whoami returns the currently active organization (marked *) and the full list.
Normal case — active org exists: Use active_org directly. Do not ask. The plan card will show it for confirmation.
Edge case — no active org: Present the list and ask:
Your account has access to multiple Deploio organizations:
1. acme-production
2. acme-staging
Which organization should this app be deployed to?
(Or run `nctl auth set-org <name>` first to set a default.)
Set selected_org from the user's answer.
Terminology note: Deploio calls the top-level grouping an organization (set withnctl auth set-org). The app lives within the organization. Do not use the word "project" when referring to this selection — it confuses users who see organization names innctl auth whoamioutput.
---
Phase 3: Propose the plan
The plan card MUST always include Organization, Project, and App — all three, in this order. Never omit Project.
Read the framework file before building the plan card. Once app_type is known from Phase 1, read the corresponding file:
| app_type | File to read |
|---|---|
| rails | skills/deploio-deploy/references/RAILS.md |
| nodejs | skills/deploio-deploy/references/NODE.md |
| django / flask | skills/deploio-deploy/references/PYTHON.md |
| php | skills/deploio-deploy/references/PHP.md |
| go | skills/deploio-deploy/references/GO.md |
| docker | skills/deploio-deploy/references/DOCKER.md |
| unknown | Ask the user before proceeding |
Each file contains: default instance size, required env vars, deploy job command, health probe path, and framework-specific warnings. Use these to populate both the plan card and the executor spec.
Here's what I'll set up:
| Setting | Value |
|---|---|
| Organization | <selected_org> |
| Project | <selected_org>-<repo-name> |
| App | <branch> |
| Source | github.com/org/repo · <branch> branch |
| Build | Docker (or: auto-detected buildpack) |
| Size | <from framework file — e.g. mini for Rails, micro for Node.js/Go/Python> |
| Replicas | 1 |
<Framework defaults block — copy "Plan card defaults" section from the framework file>
Deploio will build your app from source in the cloud and give it an HTTPS URL
on deploio.app. By default the URL is publicly accessible — if you'd like to
restrict access with a username and password prompt, just say so and I'll
enable basic auth.
First build and release takes ~2–5 minutes.
Use EnterPlanMode before presenting the plan card so the user can review it fully before committing. Use AskUserQuestion to get confirmation:
question: "Ready to deploy?"
options:
- "Yes, exactly that"
- "Yes, but… (tell me what to adjust)"
- "No, cancel"
Call ExitPlanMode once the user confirms or cancels.
Derive names:
- Organization:
active_orgfromnctl auth whoami(the*-marked entry) - Project:
<org>-<repo>(e.g.renuotest-myapp— never just<repo>; nctl errors) - App:
<branch>(e.g.main)
Available instance sizes (set at creation or anytime after with nctl update app):
| Size | RAM | CPU | CHF/month |
|---|---|---|---|
| micro | 256 MiB | 0.125 | ~8 |
| mini | 512 MiB | 0.25 | ~16 |
| standard-1 | 1 GiB | 0.50 | ~32 |
| standard-2 | 2 GiB | 0.75 | ~58 |
If the user requests basic auth, set basic_auth: true in the executor spec.
If the user says "cancel" or "stop" — do not spawn any agents. Offer to restart later.
If the user adjusts a name — update the spec and re-present the card before proceeding.
---
Phase 4: Execute — spawn two background agents
Once confirmed, use TaskCreate to create a task named "Deploying <app-name>" (status: in_progress) so the user has a visible progress tracker. Then spawn two deploio-cli agents with mode: bypassPermissions and return to the conversation.
Agent 1 — executor
task: deploy
org: <selected_org>
app: <app-name>
git_remote: <remote_url>
branch: <branch>
build: docker | buildpack
port: <port>
size: <size> # default: mini for Rails, micro for everything else
replicas: <replicas or null>
basic_auth: <true or false>
env_vars: <see framework table in references/frameworks.md>
build_env_vars: <see notes below>
deploy_job: <command or null>
deploy_job_timeout: <duration or null>
health_probe_path: <path or null>
git_ssh_key_path: <path or null>
git_username: <token name or null>
git_password: <token value or null>
git_sub_path: <subdir or null>
dockerfile_path: <path or null>
dockerfile_build_context: <path or null>
Rails SECRET_KEY_BASE
For Rails apps, if SECRET_KEY_BASE is not already set by the user, the executor must generate one by running openssl rand -hex 64 and using the output as the value. Do not ask the user to provide it.
nctl commands the executor will run (in order)
# 1. Authenticate (skip if already logged in)
nctl auth login
nctl auth set-project <project>
# 2. Create the app
nctl create app <app> \
--git-url=<git_remote> \
--git-revision=<branch> \
[--git-sub-path=<git_sub_path>] # monorepos only
[--git-ssh-private-key-from-file=<git_ssh_key_path>] # SSH auth
[--git-username=<git_username>] # HTTPS auth (GitLab/Bitbucket)
[--git-password=<git_password>] # HTTPS auth (GitLab/Bitbucket)
--port=<port> \
[--size=<size>] # micro|mini|standard-1|standard-2
[--replicas=<n>] # default 1
[--dockerfile] # docker builds only
[--dockerfile-path=<dockerfile_path>] # if Dockerfile is not in repo root
[--dockerfile-build-context=<dockerfile_build_context>] # if build context differs
[--build-env=<KEY=VALUE>] ... # build-time args; repeat or semicolon-separate
--env=<KEY=VALUE> ... # runtime env vars; repeat or semicolon-separate
[--basic-auth] # enable HTTP basic auth
[--deploy-job-command="<deploy_job>"] # if deploy_job is set
[--deploy-job-timeout=<deploy_job_timeout>] # default 5m, range 1–30m
[--deploy-job-retries=<n>] # default 3, max 5
[--health-probe-path=<health_probe_path>] # HTTP path for readiness check
[--health-probe-period-seconds=<n>] # default 10, min 1
[--hosts=<domain1,domain2>] # custom domains at creation time
# 3. Get the live URL and (if basic_auth) credentials
nctl get app <app> --project=<project>
[nctl get app <app> --basic-auth-credentials] # if basic_auth is true
Env var syntax: Multiple env vars can be passed either as repeated flags (--env=KEY1=VAL1 --env=KEY2=VAL2) or semicolon-separated in a single flag (--env='KEY1=VAL1;KEY2=VAL2'). Both forms are accepted. The same syntax applies to --build-env.
On success, report back: { "status": "success", "url": "https://<username>:<password>@<host>", "basic_auth_credentials": { "username": "...", "password": "..." } | null }
On failure, report back: { "status": "failed", "error": "<nctl error output>", "step": "create|auth|url" }
Agent 2 — monitor
task: monitor-logs
app: <app-name>
org: <selected_org>
termination: stop when executor reports success or failure, or after 20 minutes — whichever comes first
The monitor streams nctl logs app <app> --type build -f and relays key lines to the coordinator at regular intervals (every ~30s or on meaningful events). On timeout, emit: "Build is taking longer than expected — check Deploio dashboard for status." and exit.
After spawning both:
"Both agents are running — executor deploying, monitor watching logs. I'll update you as the build and release progresses, or ask me anything while it runs."
---
Phase 5: Stay responsive while agents run
You are the coordinator — remain in the conversation. Do not block.
When the monitor sends a progress update: relay it naturally.
"[45s] Build step: installing dependencies..."
When the user asks for a status update: share the latest from the monitor, or check agent status.
Use SendMessage to check in on background agents when the user asks for a status update, rather than waiting passively.
When the executor reports status: success:
Share the URL and any basic auth credentials, then offer structured next steps based on what the user mentioned earlier in the conversation:
Update the deployment task to `completed` using `TaskUpdate`. Then share the result:
Your app is live at [https://<host>](https://<username>:<password>@<host>)
(To rotate the password later: `nctl update app <app> --change-basic-auth-password`)
What's next?
→ Add a database — I can provision PostgreSQL or Redis (deploio-provision)
→ Set a custom domain — point your DNS to Deploio, then add the domain to the app
→ Scale the app — increase replicas or upgrade the instance size
→ Wire up CI/CD — auto-deploy on every git push (deploio-ci-cd)
→ Nothing for now — you're all set!
Custom domain setup (if the user asks):
- Add the domain to the app:
nctl update app <app> --hosts=yourdomain.com - Get the DNS records:
nctl get app <app> --dns— this returns a DNS target and TXT verification record - For subdomains: create a
CNAMEpointing to the DNS target - For apex domains: use
ALIAS(preferred) orArecord; add the TXT verification record
Lead with the option most relevant to the conversation (e.g., if the user mentioned Postgres earlier, put that first).
When the executor reports status: failed:
Update the deployment task to failed using TaskUpdate. Translate the error into plain terms using the table below, then offer a concrete fix:
"The build failed because Rails couldn't find SECRET_KEY_BASE. I'll generate a secure value for you and add it — want me to proceed?"
---
Framework defaults (include in executor task spec)
The framework file (read in Phase 3) contains env vars, deploy job command, health probe path, and build notes. Use it to populate the executor spec — do not re-read it in Phase 4 if already read. If app_type is unknown, ask the user before spawning the executor.
---
Backing services (mention if the user needs them)
If the user mentions PostgreSQL, MySQL, Redis, Sidekiq, or file storage:
"Deploio can provision a managed PostgreSQL database, Redis-compatible KVS (for Sidekiq), and S3-compatible object storage. I can set those up once the app is live using deploio-provision."
Don't block the deployment for services — unless a required env var is missing (e.g. REDIS_URL for Sidekiq), in which case ask before proceeding.
---
Worker jobs and scheduled jobs (mention if detected)
If the framework is Rails and Gemfile contains sidekiq, or the user mentions background workers, note that a worker job can be added at creation time with a dedicated size (defaults to micro).
Scheduled jobs (Deploio's term — do not call them "cron jobs") can be added with a name, schedule string, and command. Both worker jobs and scheduled jobs are configured on the same app resource and run in separate containers with their own resource allocation.
---
.deploio.yaml — app config file (advanced, optional)
Only mention .deploio.yaml if the user seems like a team lead, is setting up a shared project, or explicitly asks about config management. Do not offer it to users doing a quick first deploy.
If it comes up naturally, explain it briefly:
"If you're working in a team, a .deploio.yaml in your repo can track your deploy config in git — things like size, replicas, and deploy job settings — so the team can see and review changes. Want me to create one?"
Only create it if the user says yes. See references/RAILS.md (or the relevant framework file) for a template.
If a .deploio.yaml already exists (detected in Phase 1), read it to pre-fill the plan card values and skip any settings already defined there.
---
Platform defaults and limitations
- All apps receive these env vars automatically:
PORT,DEPLOIO_APP_NAME,DEPLOIO_PROJECT_NAME,DEPLOIO_RELEASE_NAME - Ephemeral storage is 2 GiB per app; writes outside designated writable paths may fail or cause OOM (exit 137)
- Buildpack apps: writable paths are restricted (e.g.
/workspace/tmp); Dockerfile apps control their own filesystem permissions - Only the
webkey in a Procfile is respected; other process types are ignored - Logs are retained for 30 days
- Resources (RAM, CPU) are allocated per replica — scaling replicas multiplies cost proportionally
- If resource usage exceeds the instance limit, Nine may terminate the app
---
Common first-deploy issues
Read skills/shared/troubleshooting.md for a full list of problems and fixes. Key patterns to diagnose from executor error output:
- Log contains
KeyErrororNameError→ missing env var → add it and retry - Log contains
PG::ConnectionBad→DATABASE_URLnot set → re-run with env var - App returns 502 → port mismatch → check framework port in
references/frameworks.md - Build fails, lock file error →
Gemfile.lockorpackage-lock.jsonnot committed - OOM / exit code 137 → app exceeds memory for selected instance size → upgrade to next size tier
- TLS cert not issued → DNS not yet pointing to Deploio; remove AAAA records during migration
If the first deploy fails (build or release), offer to retry via deploio-manage: --retry-build (build failure) or --retry-release (release/migration failure).
---
Source: https://github.com/org/repo.git
Author: renuo
Discovered via: skillsdirectory.com
Genre: devops
SKILL.md source
---
name: Deploio Deploy
description: Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create ...
---
# Deploio Deploy
Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create a Deploio app", "how do I deploy to Deploio", "host on Deploio", "push to Deploio", "new app on Deploio", "first deploy to Deploio", "set up a new Deploio app", or setting up a new Deploio app from scratch. Covers auth, project setup, git credential resolution, buildpack/Dockerfile detection, and bu...
---
name: deploio-deploy
description: Handles first-time deployment of an app to Deploio — from git URL to live HTTPS URL. This skill should be triggered when deploying a new app for the first time: "deploy my app on Deploio", "create a Deploio app", "how do I deploy to Deploio", "host on Deploio", "push to Deploio", "new app on Deploio", "first deploy to Deploio", "set up a new Deploio app", or setting up a new Deploio app from scratch. Covers auth, project setup, git credential resolution, buildpack/Dockerfile detection, and build monitoring. Do NOT use for apps already running on Deploio (use deploio-manage to update them).
license: MIT
metadata:
version: 1.3.0
---
# Deploio: First-Time App Deployment
Your role is coordinator. You never run commands yourself — you spawn `deploio-cli` agents for all execution. Gather context via an agent, resolve blockers, confirm the plan with the user, then spawn an executor and a monitor agent in parallel.
**Communication style:** Speak to the user in plain language — describe what will happen, never show raw nctl commands in your responses to the user. Agents manage the CLI entirely on the user's behalf. Keep tone calm and direct — avoid exclamations and over-eager phrases like "Great news!" or "Awesome!".
---
## Phase 0: Pre-flight check (from conversation context only — no commands)
Before spawning any agent, evaluate these four conditions from what the user has already told you:
| Condition | Known if… |
|---|---|
| `nctl_known` | User mentioned installing nctl, or ran it in this session |
| `remote_known` | A git URL is visible in the conversation |
| `auth_known` | User mentioned a Deploio project name or logged in |
| `framework_known` | User said "Rails app", "Next.js", etc. |
If `remote_known` is false, run `git remote get-url origin` and `git branch --show-current` to discover the URL and branch. If a remote is found, derive `app = <branch>` (e.g. `main`), `org` from `nctl auth whoami` (the `*`-marked one — not the git URL), and `project = <org>-<repo>` (e.g. `renuotest-myapp` — never just `<repo>`; nctl errors). State: *"I'll deploy project `renuotest-myapp`, app `main`, in organization `renuotest` — let me know if that's different."* Only ask for a URL from scratch if no remote is configured.
Pass any known values into the gather-context spec as hints to speed up detection.
---
## Phase 1: Gather context
Spawn the `deploio-cli` agent with `mode: bypassPermissions` and this spec:
```
task: gather-context
hints:
remote_url: <from context, or null>
framework: <from context, or null>
nctl_installed: <from context, or null>
```
### What the agent must do
**Constraints:** Do not run any `nctl` commands during context gathering — nctl commands may require auth and will produce misleading errors before the project exists. Use only: git commands, file system reads, and `nctl version`.
**Detection steps:**
1. `nctl version` → set `nctl_installed: true/false`; require at least v1.16.0 for Deploio support
2. Use the `remote_url` hint if provided; otherwise run `git remote get-url origin` → set `remote_url`
3. `git branch --show-current` → set `branch`
4. Read project files to detect `app_type` and `port` (framework details live in `references/<FRAMEWORK>.md`):
| File present | app_type | default port |
|---|---|---|
| `Dockerfile` | docker | read from `EXPOSE` line, else 8080 |
| `Gemfile` containing `rails` | rails | 3000 |
| `package.json` | nodejs | 3000 |
| `manage.py` + `requirements.txt` | django | 8000 |
| `app.py` or `main.py` + `requirements.txt` | flask | 8000 |
| `composer.json` | php | 8080 |
| `go.mod` | go | 8080 |
| none matched | unknown | null |
5. Detect `git_auth_type`:
- If `remote_url` starts with `git@` → `ssh`
- If `remote_url` starts with `https://` → `https`
- If `remote_url` is null → `none`
6. Check for multiple subdirectories each containing a `Gemfile`, `package.json`, or `go.mod` → set `is_monorepo: true/false`
7. Run `nctl auth whoami` (only if nctl is installed). Parse the output to find:
- `active_org`: the organization marked with `*` in the list (e.g. `* renuotest`)
- `available_orgs`: the full list of organization names
The `active_org` is the one already selected — use it without asking. Only surface org selection if `active_org` is null (nothing marked active).
8. Check for a `.deploio.yaml` file in the repo root → set `has_deploio_yaml: true/false`. If present, note which keys it defines (deployJob, size, env, healthProbe, etc.) so the coordinator can skip asking about those.
### Return schema (JSON)
```json
{
"nctl_installed": true,
"remote_url": "https://github.com/org/repo.git",
"branch": "main",
"app_type": "rails",
"port": 3000,
"git_auth_type": "https",
"is_monorepo": false,
"active_org": "acme-production",
"available_orgs": ["acme-production", "acme-staging"],
"has_deploio_yaml": false,
"blockers": []
}
```
`blockers` is a list of strings, e.g. `["nctl not installed", "no git remote"]`.
---
## Phase 2: Resolve blockers
Handle each blocker before proceeding:
**nctl not installed:**
```bash
brew install ninech/tap/nctl # macOS
# Linux: download from https://github.com/ninech/nctl/releases/latest
```
Re-run Phase 1 after install.
**nctl version too old:** Deploio requires nctl v1.16.0 or newer. Run `brew upgrade ninech/tap/nctl` (macOS) or re-download from releases.
**No git remote:** Deploio always pulls from a git host — it never receives files directly.
```bash
git remote add origin https://github.com/<user>/<repo>.git
git push -u origin main
```
Continue once the user confirms it's pushed.
**nctl not authenticated:** Ask the user to run `nctl auth login` (opens browser OAuth) then `nctl auth set-project <project>`.
**app_type unknown:** Ask the user what runtime their app uses before proceeding.
---
## Phase 2b: Git credentials (private repos)
**GitHub note:** GitHub does not support HTTPS deploy tokens for nctl. Use SSH deploy keys for GitHub private repos.
If `git_auth_type` is `ssh`:
- Ask the user for the path to their deploy key, or offer to use `~/.ssh/id_ed25519`
- They can generate a dedicated key: `ssh-keygen -t ed25519 -f ~/deploio.key -N ''`
- Pass `git_ssh_key_path` to the executor spec
If `git_auth_type` is `https` (GitLab or Bitbucket only):
- GitLab: Settings → Access Tokens → `read_repository`; pass the token name as `git_username` and the token value as `git_password`
- Bitbucket: Repository settings → Access tokens → `Repository read`; use `x-auth-token` as the username field
- Pass `git_username` and `git_password` to the executor spec
If the repo is public, skip this phase.
---
## Phase 2c: Organization selection
`nctl auth whoami` returns the currently active organization (marked `*`) and the full list.
**Normal case — active org exists:** Use `active_org` directly. Do not ask. The plan card will show it for confirmation.
**Edge case — no active org:** Present the list and ask:
```
Your account has access to multiple Deploio organizations:
1. acme-production
2. acme-staging
Which organization should this app be deployed to?
(Or run `nctl auth set-org <name>` first to set a default.)
```
Set `selected_org` from the user's answer.
> **Terminology note:** Deploio calls the top-level grouping an **organization** (set with `nctl auth set-org`). The app lives within the organization. Do not use the word "project" when referring to this selection — it confuses users who see organization names in `nctl auth whoami` output.
---
## Phase 3: Propose the plan
The plan card **MUST always include Organization, Project, and App** — all three, in this order. Never omit Project.
**Read the framework file before building the plan card.** Once `app_type` is known from Phase 1, read the corresponding file:
| app_type | File to read |
|---|---|
| `rails` | `skills/deploio-deploy/references/RAILS.md` |
| `nodejs` | `skills/deploio-deploy/references/NODE.md` |
| `django` / `flask` | `skills/deploio-deploy/references/PYTHON.md` |
| `php` | `skills/deploio-deploy/references/PHP.md` |
| `go` | `skills/deploio-deploy/references/GO.md` |
| `docker` | `skills/deploio-deploy/references/DOCKER.md` |
| `unknown` | Ask the user before proceeding |
Each file contains: default instance size, required env vars, deploy job command, health probe path, and framework-specific warnings. Use these to populate both the plan card and the executor spec.
```
Here's what I'll set up:
| Setting | Value |
|---|---|
| Organization | <selected_org> |
| Project | <selected_org>-<repo-name> |
| App | <branch> |
| Source | github.com/org/repo · <branch> branch |
| Build | Docker (or: auto-detected buildpack) |
| Size | <from framework file — e.g. mini for Rails, micro for Node.js/Go/Python> |
| Replicas | 1 |
<Framework defaults block — copy "Plan card defaults" section from the framework file>
Deploio will build your app from source in the cloud and give it an HTTPS URL
on deploio.app. By default the URL is publicly accessible — if you'd like to
restrict access with a username and password prompt, just say so and I'll
enable basic auth.
First build and release takes ~2–5 minutes.
```
Use `EnterPlanMode` before presenting the plan card so the user can review it fully before committing. Use `AskUserQuestion` to get confirmation:
```
question: "Ready to deploy?"
options:
- "Yes, exactly that"
- "Yes, but… (tell me what to adjust)"
- "No, cancel"
```
Call `ExitPlanMode` once the user confirms or cancels.
Derive names:
- **Organization**: `active_org` from `nctl auth whoami` (the `*`-marked entry)
- **Project**: `<org>-<repo>` (e.g. `renuotest-myapp` — never just `<repo>`; nctl errors)
- **App**: `<branch>` (e.g. `main`)
**Available instance sizes** (set at creation or anytime after with `nctl update app`):
| Size | RAM | CPU | CHF/month |
|---|---|---|---|
| micro | 256 MiB | 0.125 | ~8 |
| mini | 512 MiB | 0.25 | ~16 |
| standard-1 | 1 GiB | 0.50 | ~32 |
| standard-2 | 2 GiB | 0.75 | ~58 |
If the user requests basic auth, set `basic_auth: true` in the executor spec.
If the user says "cancel" or "stop" — do not spawn any agents. Offer to restart later.
If the user adjusts a name — update the spec and re-present the card before proceeding.
---
## Phase 4: Execute — spawn two background agents
Once confirmed, use `TaskCreate` to create a task named "Deploying `<app-name>`" (status: `in_progress`) so the user has a visible progress tracker. Then spawn **two `deploio-cli` agents** with `mode: bypassPermissions` and return to the conversation.
**Agent 1 — executor**
```
task: deploy
org: <selected_org>
app: <app-name>
git_remote: <remote_url>
branch: <branch>
build: docker | buildpack
port: <port>
size: <size> # default: mini for Rails, micro for everything else
replicas: <replicas or null>
basic_auth: <true or false>
env_vars: <see framework table in references/frameworks.md>
build_env_vars: <see notes below>
deploy_job: <command or null>
deploy_job_timeout: <duration or null>
health_probe_path: <path or null>
git_ssh_key_path: <path or null>
git_username: <token name or null>
git_password: <token value or null>
git_sub_path: <subdir or null>
dockerfile_path: <path or null>
dockerfile_build_context: <path or null>
```
### Rails SECRET_KEY_BASE
For Rails apps, if `SECRET_KEY_BASE` is not already set by the user, the executor must generate one by running `openssl rand -hex 64` and using the output as the value. Do not ask the user to provide it.
### nctl commands the executor will run (in order)
```bash
# 1. Authenticate (skip if already logged in)
nctl auth login
nctl auth set-project <project>
# 2. Create the app
nctl create app <app> \
--git-url=<git_remote> \
--git-revision=<branch> \
[--git-sub-path=<git_sub_path>] # monorepos only
[--git-ssh-private-key-from-file=<git_ssh_key_path>] # SSH auth
[--git-username=<git_username>] # HTTPS auth (GitLab/Bitbucket)
[--git-password=<git_password>] # HTTPS auth (GitLab/Bitbucket)
--port=<port> \
[--size=<size>] # micro|mini|standard-1|standard-2
[--replicas=<n>] # default 1
[--dockerfile] # docker builds only
[--dockerfile-path=<dockerfile_path>] # if Dockerfile is not in repo root
[--dockerfile-build-context=<dockerfile_build_context>] # if build context differs
[--build-env=<KEY=VALUE>] ... # build-time args; repeat or semicolon-separate
--env=<KEY=VALUE> ... # runtime env vars; repeat or semicolon-separate
[--basic-auth] # enable HTTP basic auth
[--deploy-job-command="<deploy_job>"] # if deploy_job is set
[--deploy-job-timeout=<deploy_job_timeout>] # default 5m, range 1–30m
[--deploy-job-retries=<n>] # default 3, max 5
[--health-probe-path=<health_probe_path>] # HTTP path for readiness check
[--health-probe-period-seconds=<n>] # default 10, min 1
[--hosts=<domain1,domain2>] # custom domains at creation time
# 3. Get the live URL and (if basic_auth) credentials
nctl get app <app> --project=<project>
[nctl get app <app> --basic-auth-credentials] # if basic_auth is true
```
**Env var syntax:** Multiple env vars can be passed either as repeated flags (`--env=KEY1=VAL1 --env=KEY2=VAL2`) or semicolon-separated in a single flag (`--env='KEY1=VAL1;KEY2=VAL2'`). Both forms are accepted. The same syntax applies to `--build-env`.
On success, report back: `{ "status": "success", "url": "https://<username>:<password>@<host>", "basic_auth_credentials": { "username": "...", "password": "..." } | null }`
On failure, report back: `{ "status": "failed", "error": "<nctl error output>", "step": "create|auth|url" }`
**Agent 2 — monitor**
```
task: monitor-logs
app: <app-name>
org: <selected_org>
termination: stop when executor reports success or failure, or after 20 minutes — whichever comes first
```
The monitor streams `nctl logs app <app> --type build -f` and relays key lines to the coordinator at regular intervals (every ~30s or on meaningful events). On timeout, emit: `"Build is taking longer than expected — check Deploio dashboard for status."` and exit.
After spawning both:
> "Both agents are running — executor deploying, monitor watching logs. I'll update you as the build and release progresses, or ask me anything while it runs."
---
## Phase 5: Stay responsive while agents run
You are the coordinator — remain in the conversation. Do not block.
**When the monitor sends a progress update:** relay it naturally.
> "[45s] Build step: installing dependencies..."
**When the user asks for a status update:** share the latest from the monitor, or check agent status.
Use `SendMessage` to check in on background agents when the user asks for a status update, rather than waiting passively.
**When the executor reports `status: success`:**
Share the URL and any basic auth credentials, then offer structured next steps based on what the user mentioned earlier in the conversation:
```
Update the deployment task to `completed` using `TaskUpdate`. Then share the result:
Your app is live at [https://<host>](https://<username>:<password>@<host>)
(To rotate the password later: `nctl update app <app> --change-basic-auth-password`)
What's next?
→ Add a database — I can provision PostgreSQL or Redis (deploio-provision)
→ Set a custom domain — point your DNS to Deploio, then add the domain to the app
→ Scale the app — increase replicas or upgrade the instance size
→ Wire up CI/CD — auto-deploy on every git push (deploio-ci-cd)
→ Nothing for now — you're all set!
```
**Custom domain setup (if the user asks):**
1. Add the domain to the app: `nctl update app <app> --hosts=yourdomain.com`
2. Get the DNS records: `nctl get app <app> --dns` — this returns a DNS target and TXT verification record
3. For subdomains: create a `CNAME` pointing to the DNS target
4. For apex domains: use `ALIAS` (preferred) or `A` record; add the TXT verification record
Lead with the option most relevant to the conversation (e.g., if the user mentioned Postgres earlier, put that first).
**When the executor reports `status: failed`:**
Update the deployment task to `failed` using `TaskUpdate`. Translate the error into plain terms using the table below, then offer a concrete fix:
> "The build failed because Rails couldn't find SECRET_KEY_BASE. I'll generate a secure value for you and add it — want me to proceed?"
---
## Framework defaults (include in executor task spec)
The framework file (read in Phase 3) contains env vars, deploy job command, health probe path, and build notes. Use it to populate the executor spec — do not re-read it in Phase 4 if already read. If `app_type` is `unknown`, ask the user before spawning the executor.
---
## Backing services (mention if the user needs them)
If the user mentions PostgreSQL, MySQL, Redis, Sidekiq, or file storage:
> "Deploio can provision a managed PostgreSQL database, Redis-compatible KVS (for Sidekiq), and S3-compatible object storage. I can set those up once the app is live using deploio-provision."
Don't block the deployment for services — unless a required env var is missing (e.g. `REDIS_URL` for Sidekiq), in which case ask before proceeding.
---
## Worker jobs and scheduled jobs (mention if detected)
If the framework is Rails and `Gemfile` contains `sidekiq`, or the user mentions background workers, note that a worker job can be added at creation time with a dedicated size (defaults to `micro`).
**Scheduled jobs** (Deploio's term — do not call them "cron jobs") can be added with a name, schedule string, and command. Both worker jobs and scheduled jobs are configured on the same app resource and run in separate containers with their own resource allocation.
---
## .deploio.yaml — app config file (advanced, optional)
Only mention `.deploio.yaml` if the user seems like a team lead, is setting up a shared project, or explicitly asks about config management. Do not offer it to users doing a quick first deploy.
If it comes up naturally, explain it briefly:
> "If you're working in a team, a `.deploio.yaml` in your repo can track your deploy config in git — things like size, replicas, and deploy job settings — so the team can see and review changes. Want me to create one?"
Only create it if the user says yes. See `references/RAILS.md` (or the relevant framework file) for a template.
If a `.deploio.yaml` already exists (detected in Phase 1), read it to pre-fill the plan card values and skip any settings already defined there.
---
## Platform defaults and limitations
- All apps receive these env vars automatically: `PORT`, `DEPLOIO_APP_NAME`, `DEPLOIO_PROJECT_NAME`, `DEPLOIO_RELEASE_NAME`
- Ephemeral storage is 2 GiB per app; writes outside designated writable paths may fail or cause OOM (exit 137)
- Buildpack apps: writable paths are restricted (e.g. `/workspace/tmp`); Dockerfile apps control their own filesystem permissions
- Only the `web` key in a Procfile is respected; other process types are ignored
- Logs are retained for 30 days
- Resources (RAM, CPU) are allocated per replica — scaling replicas multiplies cost proportionally
- If resource usage exceeds the instance limit, Nine may terminate the app
---
## Common first-deploy issues
Read `skills/shared/troubleshooting.md` for a full list of problems and fixes. Key patterns to diagnose from executor error output:
- Log contains `KeyError` or `NameError` → missing env var → add it and retry
- Log contains `PG::ConnectionBad` → `DATABASE_URL` not set → re-run with env var
- App returns 502 → port mismatch → check framework port in `references/frameworks.md`
- Build fails, lock file error → `Gemfile.lock` or `package-lock.json` not committed
- OOM / exit code 137 → app exceeds memory for selected instance size → upgrade to next size tier
- TLS cert not issued → DNS not yet pointing to Deploio; remove AAAA records during migration
If the first deploy fails (build or release), offer to retry via deploio-manage: `--retry-build` (build failure) or `--retry-release` (release/migration failure).
---
**Source**: https://github.com/org/repo.git
**Author**: renuo
**Discovered via**: skillsdirectory.com
**Genre**: devops
Related skills 6
microsoft-foundry
Deploy, evaluate, and manage Foundry agents end-to-end: Docker build, ACR push, hosted/prompt agent create, container start, batch eval, continuous eval, prompt optimizer workflows, agent.yaml, dataset curation from traces. USE FOR: deploy agent to Foundry, hosted agent, create agent, invoke agent, evaluate agent, run batch eval, continuous eval, continuous monitoring, continuous eval status, optimize prompt, improve prompt, prompt optimizer, optimize agent instructions, improve agent instruc...
azure-ai
Use for Azure AI: Search, Speech, OpenAI, Document Intelligence. Helps with search, vector/hybrid search, speech-to-text, text-to-speech, transcription, OCR. WHEN: AI Search, query search, vector search, hybrid search, semantic search, speech-to-text, text-to-speech, transcribe, OCR, convert text to speech.
azure-deploy
Execute Azure deployments for ALREADY-PREPARED applications that have existing .azure/deployment-plan.md and infrastructure files. DO NOT use this skill when the user asks to CREATE a new application — use azure-prepare instead. This skill runs azd up, azd deploy, terraform apply, and az deployment commands with built-in error recovery. Requires .azure/deployment-plan.md from azure-prepare and validated status from azure-validate. WHEN: "run azd up", "run azd deploy", "execute deployment", "p...
azure-diagnostics
Debug Azure production issues on Azure using AppLens, Azure Monitor, resource health, and safe triage. WHEN: debug production issues, troubleshoot app service, app service high CPU, app service deployment failure, troubleshoot container apps, troubleshoot functions, troubleshoot AKS, kubectl cannot connect, kube-system/CoreDNS failures, pod pending, crashloop, node not ready, upgrade failures, analyze logs, KQL, insights, image pull failures, cold start issues, health probe failures, resource...
azure-resource-lookup
List, find, and show Azure resources across subscriptions or resource groups. Handles prompts like "list the websites in my subscription", "list my web apps", "show my app services", "list virtual machines", "list my VMs", "show storage accounts", "find container apps", and "what resources do I have". USE FOR: list websites, list web apps, list app services, show websites in subscription, resource inventory, find resources by tag, tag analysis, orphaned resource discovery (not for cost analys...
azure-resource-visualizer
Analyze Azure resource groups and generate detailed Mermaid architecture diagrams showing the relationships between individual resources. WHEN: create architecture diagram, visualize Azure resources, show resource relationships, generate Mermaid diagram, analyze resource group, diagram my resources, architecture visualization, resource topology, map Azure infrastructure.