Okteto Onboarding Skill
1. Activation
This skill activates when all of the following are true:
- The project has no or at the repo root
- The user is asking about Okteto, dev environments, or onboarding (e.g., "how do I get this onto Okteto", "set this repo up for Okteto", "create an Okteto manifest")
Do NOT activate if:
- An or already exists at the repo root — that is the existing skill's domain. Defer to it.
- The user is asking how to use Okteto with an existing manifest. Defer to the existing skill.
Pre-flight check. Before starting Phase 1, run:
bash
ls okteto.yaml okteto.yml 2>/dev/null
If anything is returned, stop and tell the user:
"I see
already exists. The
skill is for repos
without a manifest. For working with the existing manifest, use the
skill or run
."
Two operating modes: collaborative (default — user is in the loop) and autonomous (no human; opens a PR). See Section 7. Most of the workflow is mode-agnostic; only Phase 6 and the resolution of "ask the user" branches differ.
2. Phase 1 — Discover
Build an internal model of the repo: services, their build contexts, ports, dev commands, and any existing deploy artifacts. Read signals in priority order. Do not write the manifest yet.
2.1 Signal priority
- / — the richest signal for services and dev, and a valid source on its own. If present, it is the primary blueprint for and , and Okteto can deploy it directly via (Section 4.1) — no chart or k8s manifests required. See the mapping table below.
- Existing Helm chart — any under , , , or . If found, will be a command. Do not generate or modify the chart.
- Existing k8s manifests — files with headers under , , — but excluding any subdirectory (those belong to a Helm chart and are not directly applyable). If found, will be . Do not author these manifests.
- Per-service Dockerfiles — for repos without compose, each top-level is a candidate service. Service name comes from the parent directory.
- Language manifests — , , , , , . Used to pick the dev image and infer the dev command. When this is the only signal (no Dockerfile, no compose), name the service after the project's field (e.g., 's , 's ) or, failing that, the repo directory.
- Procfile / Makefile — secondary signals when language manifests are ambiguous.
These signals are complementary, not exclusive. A repo can carry more than one. Common combinations:
- Compose only — compose drives , , and (via ). Enough to reach Level 2 with no chart or k8s manifests.
- Chart or k8s only — the artifact drives ; derive from the deployed workload names (Phase 3, bullet 4).
- Compose and a chart/k8s — the deploy source is ambiguous. Compose files are often the dev path and charts the prod path, but not always. Do not assume the chart wins . Ask the user which should drive it — compose, the chart/manifests, or both combined (Section 2.5).
Apply each signal to its own section of the manifest rather than treating priority order as a winner-takes-all.
Don't stop at the root compose file. Real projects routinely ship several: a base or production compose (often at the repo root, using published
s) plus one or more development composes under
,
, or named
/
. Glob for
and
(skip
, vendor, and build dirs) — not just
. The split usually maps cleanly:
- The dev compose — the one that builds from source ( with /, plus source bind mounts) — drives and .
- A base / production / compose can drive .
When more than one compose file exists, list them and ask which describes development and which is the deploy source (Section 2.5). Do not assume the root file is the dev blueprint — a root compose built entirely from published images is a run/deploy file, and developing against a published image is meaningless.
Follow and resolve anchors. A compose file may pull in others via
(or the older
) — read those too, or you'll miss services. Parse the
resolved YAML, not raw lines: dev composes commonly rely on anchors and
extension fields (
,
), which only make sense after expansion.
An existing Okteto-aware compose is a strong signal. If a compose file already uses Okteto Compose extensions (
,
) or is clearly an okteto deploy file, prefer it as the
source and surface it to the user rather than authoring something new — the customer has already expressed how they want to deploy.
2.2 Compose → Okteto mapping
When
is present, map fields like this:
| Compose field | Okteto manifest field |
|---|
| (when there's a block) and |
| , | , |
| (no ) | directly — do not add a entry for pre-built images |
| (Level 1) | — preserves the user's local-port intent |
| (Level 2+) | exposed via the source (compose , Helm, or k8s); also keep for active dev sessions |
| starting point for |
| (host bind mounts only) | candidates |
| ordering hint for the deploy step |
| |
Ignore compose-only concepts that don't translate (
,
, named volumes,
).
Backing services vs. dev targets. A compose service that uses a published
with no
and no source in the repo (databases, caches, object stores, mail catchers, message brokers —
,
,
,
,
, and the like) is
infrastructure: include it in the
stack, but it rarely belongs in
. Reserve
for the services you actually edit — the ones that build from source in the repo.
Don't create a entry for every compose service by default; a repo with ten compose services often has only one or two real dev targets.
2.3 Dev-image picks by language manifest
Pick the image family from the language manifest, and the tag from the version the repo declares — do not hard-code a version. Reading the declared version keeps the pick current instead of freezing a minor that goes stale.
| Language manifest | Image family | Where to read the version |
|---|
| | |
| | the line |
| / | or | / , or the wrapper version |
| / | | or |
| | or the directive |
| | (MSRV), if set |
Tag the image with the declared version, e.g.
when
says
.
State the source in an inline comment (
# go.mod declares Go 1.23
).
When the repo declares no version, do not invent a specific minor — it will be wrong as often as right. In collaborative mode, ask the user which version they target. In autonomous mode, pin to the family's current stable major and flag it in the PR as a decision to confirm (
# go.mod has no version line — defaulting to a recent stable; confirm
).
2.4 Dev-command picks
| Signal | Dev command default |
|---|
| with | |
| with (no dev) | |
| (Go projects usually want a shell to manually) |
| with | |
| with FastAPI/Flask | (varies too much to default) |
| Procfile with a line | the value of |
| compose / is or | — this is a dev-container placeholder (the container stays alive so you exec in), not a real command. Don't copy it into ; it's exactly the pattern Okteto's replaces. |
| None of the above | |
2.5 When discovery is ambiguous
If discovery leaves real ambiguity, ask one targeted question at a time rather than guessing. Examples:
- "I see Dockerfiles in and but no compose file. Should both be services?"
- "I found a Helm chart at and another at . Which one is the canonical deploy?"
- "This repo has both a and a Helm chart. Which should drive — the compose file, the chart, or both? (Compose is often the dev path and the chart the prod path, but I don't want to assume.)"
- "I see several compose files — at the root and
docker/docker-compose.dev.yml
. Which one describes how you develop (builds from source), and which should I use to deploy the stack?"
- "Your doesn't have a — what command starts your dev server?"
Do not ask the user about everything. Only ask when a guess would be likely wrong. Trust the signals.
In autonomous mode, do not ask. Pick the most conservative interpretation, proceed, and note the ambiguity in the PR description (Section 7.2 covers this).
2.6 Output of Phase 1
Internally, you should now have a model like:
Services: [api, web, worker]
api: Dockerfile @ ./api, port 8080, dev cmd `bash`, dev image `okteto/golang:1.22`
web: Dockerfile @ ./web, port 3000, dev cmd `npm run dev`, dev image `okteto/node:20`
worker: Dockerfile @ ./worker, no port, dev cmd `bash`, dev image `okteto/python:3.12`
Deploy: helm chart at ./chart
Tests: detected `go test ./...` in api, `npm test` in web
Show this summary to the user before moving to Phase 2.
3. Phase 2 — Negotiate scope
Before drafting, frame the choice and pick a level on the adaptive ladder.
3.1 The framing block (always show this to the user)
In Okteto, describes how to provision the environment — from a Docker Compose file, a Helm chart, or k8s manifests. describes how to live-edit a running service — file sync, the dev image, the startup command. You can use Okteto with just if you already have a way to deploy your stack, or have Okteto handle both.
This same framing goes at the top of the generated
as a header comment.
3.2 The adaptive ladder
| Level | What it produces | Requires |
|---|
| 1 — dev-only | section only. User runs their own deploy externally. | Nothing extra. |
| 2 — deploy + dev | + + . brings up the stack. | A Helm chart, k8s manifests, or a Docker Compose file in the repo. |
| 3 — full lifecycle | Adds containers wired to existing test commands. | Level 2 prereqs PLUS detected tests. |
3.3 Recommendation logic
Recommend a level based on what Phase 1 found:
A deploy source is a Helm chart, k8s manifests, or a deployable Docker Compose file.
- A deploy source found AND tests detected → recommend Level 3
- A deploy source found, no tests → recommend Level 2
- No deploy source at all (no chart, no k8s manifests, no compose) → recommend Level 1 (and explain why higher levels are unavailable)
When more than one deploy source exists (e.g. compose
and a chart), confirm which one drives
(Section 2.5) before drafting — don't default to the chart.
Then ask the user (collaborative mode) or accept the recommendation (autonomous mode):
"Based on what I found, I'd recommend Level [N]. Want to go with that, pick a different level, or have me explain the trade-offs?"
3.4 Locking the level
Once chosen, the level is locked for the rest of the session. Do not negotiate level mid-flight. If the draft turns out to need a different level (e.g., the chart is broken), surface that as a Phase 5 failure and re-enter Phase 2 cleanly.
4. Phases 3–4 — Draft and refine
4.1 Draft (Phase 3)
Write
to the repo root. The user must see the
actual file, not just a summary.
Required content of every draft:
-
Header comment block — the framing from Section 3.1, plus a note that the file was generated by
and the user is expected to edit it. Include the chosen scope level and a one-line rationale.
-
section (Level 2+) — one entry per service with
and
. Skip pre-built images here (they go directly under
).
-
section (Level 2+) — points at the chosen deploy source:
- Compose:
deploy: { compose: docker-compose.yml }
(optionally a subset). The simplest path when the repo has no chart or manifests.
- Helm: a command, passing built images via
--set ...=${OKTETO_BUILD_<SERVICE>_IMAGE}
(see "Wiring built images" below).
- k8s manifests: a command.
See the schema examples below.
-
section (always) — one entry per service the user wants in dev mode, with
,
, and
. The
key name must match the workload Okteto will deploy:
- Compose repo: the compose service name (mapping table, Section 2.2).
- Helm/k8s repo (no compose): the name of the Kubernetes workload the chart/manifests create — the / (render the chart or read the manifest to find it). A key matching no deployed workload won't attach.
The
,
, and
come from the Dockerfile + language manifest (Sections 2.3–2.4) regardless of repo shape.
-
section (Level 3) — one entry per detected test command. See the schema example below.
Sync-path defaults:
- Single-service repo (one Dockerfile or one language manifest at the root): is a reasonable default. If the Dockerfile sets , use that path on the right side instead of .
- Multi-service repo (per-service Dockerfiles or compose with multiple services): scope each service's sync to its own subdirectory, e.g., . Syncing the whole repo into every container is almost always wrong.
Inline comments are required on every non-obvious choice (image picks, command picks, sync paths,
ports). Image and command picks are non-obvious by default — comment them.
Wiring built images into and . Images defined under
are pushed to the Okteto Registry and exposed to the rest of the manifest as environment variables:
OKTETO_BUILD_<SERVICE>_IMAGE
— full image reference (the one you almost always want)
OKTETO_BUILD_<SERVICE>_REGISTRY
, , — the parts, if you need them
is the
key uppercased, with
replaced by
(so
→
OKTETO_BUILD_WEB_API_IMAGE
). Use these so the deployed workload runs the freshly built image instead of a stale or hard-coded one:
- Helm:
--set api.image=${OKTETO_BUILD_API_IMAGE}
- k8s manifests: substitute the value (e.g. ) before , or template the image field.
- Compose: the section already overrides the matching compose by service name — no needed.
- : set
dev.<svc>.image: ${OKTETO_BUILD_<SVC>_IMAGE}
when the user wants to develop against the built application image rather than a generic toolchain image (Section 2.3).
Example: Level 2/3 fragment (build + deploy + dev + test)
yaml
build:
api:
context: ./api
dockerfile: Dockerfile # using the existing Dockerfile
deploy:
- name: Deploy chart
command: helm upgrade --install myapp ./chart --set api.image=${OKTETO_BUILD_API_IMAGE} # built api image wired in
dev:
api:
image: okteto/golang:1.23 # tag from api/go.mod (go 1.23)
command: bash # Go services usually want a shell to `go run` manually
sync:
- ./api:/usr/src/app # per-service sync; tighten if you have large generated dirs
test:
api:
image: okteto/golang:1.23
context: ./api
commands:
- go test ./... # detected from the project's test layout
Example: Level 2 fragment (compose-driven deploy, no chart)
yaml
build:
api:
context: ./api
dockerfile: Dockerfile
deploy:
compose: docker-compose.yml # deploy the stack straight from compose — no chart or k8s manifests needed
dev:
api:
image: okteto/golang:1.23 # tag from api/go.mod (go 1.23)
command: bash
sync:
- ./api:/usr/src/app
Compose-as-deploy caveats. A compose file written for local development often won't deploy cleanly to a cluster as-is. Before committing to
, scan the compose file and warn the user about:
- Host-IP port bindings (e.g. ) — Okteto's compose deploy rejects them (). Drop the host-IP prefix ().
- Source bind-mount volumes (e.g. ) — on a cluster these become empty volumes that shadow the image's application code, so the service crashes (). They belong in , not in a deployed compose.
If the compose file leans on these dev-only constructs, surface it up front — don't discover it at deploy time. In preference order:
- Generate a deploy-ready compose — derive a from the original with the cluster-hostile parts removed, and point
deploy: compose: compose.okteto.yaml
at it. See "Generating a deploy-ready compose" below.
- Reuse an existing deploy compose — if the repo already ships one (e.g. a or production compose without the dev volumes/ports), point at that.
- Use the chart/k8s manifests for instead, if the repo has them. This is why a repo with both compose and a chart often wants the chart for even though compose drives (Section 2.5).
Generating a deploy-ready compose (). When compose is the chosen deploy source but the file carries dev-only constructs, write a
derived copy rather than editing the user's original. This is a mechanical transform of the user's own file — not a hand-authored deploy artifact — so show it to the user as a diff against the source. Reference it explicitly:
deploy: { compose: compose.okteto.yaml }
. Apply these transforms:
- Drop host-IP port prefixes — → . Debug-only ports (inspectors) can be removed entirely; they belong in .
- Remove source bind-mount volumes — and the like. The image already contains the code; these belong in . Keep named/data volumes (e.g. ).
- Replace dev-container placeholders — a service whose / is won't run the app when deployed. Restore its real start command (from the Dockerfile or a production compose), or drop the override.
Two things the transform cannot silently fix — flag these to the user rather than pretending the result is clean:
- Host-path config/script mounts (e.g. an init script mounted as the container's entrypoint) — removing the mount doesn't make the service work; the file has to be baked into an image. Leave it and warn.
- Anything else service-specific the scan can't reason about.
Add a header comment to
: that it was derived from
by the skill, what changed, and that it's safe to edit or regenerate.
The generated file is best-effort — still climb the Phase 5 ladder (validate → build → deploy) to confirm the stack actually comes up.
Example: Level 1 fragment (dev-only, single service)
yaml
dev:
myapp: # service name from pyproject.toml [project] name
image: okteto/python:3.12 # pyproject.toml declares requires-python >=3.12
command: bash # no FastAPI/Flask signal — shell is the safe default
sync:
- .:/usr/src/app # full-repo sync; exclude .venv, __pycache__ if they grow
4.2 Refine (Phase 4)
Show the file to the user. In collaborative mode, ask:
"Here's the draft. Want me to change anything before we validate? Common edits: adjust sync paths to exclude
/
/
, change the dev image version, add an env var, or swap the deploy command."
Common edit patterns:
| User says | Edit |
|---|
| "exclude " | Change to a list with ignore patterns or use a more specific path |
| "use Go 1.21 instead" | Update to |
| "I need this env var" | Add to |
| "use my values file" | Update helm command with |
Iteration is cheap — keep editing until the user is satisfied. Do not move to Phase 5 until they say "looks good" or equivalent.
In autonomous mode, skip the ask and move directly to Phase 5. Edits will be requested via the PR review.
5. Phase 5 — Validate (tiered)
Climb the validation ladder as far as the environment supports. Tier 1 is mandatory; Tiers 2 and 3 are opt-in.
5.1 Tier 1 — (always)
Run:
What it catches: YAML syntax, schema violations, missing required fields.
What it does NOT catch: wrong sync paths, missing services in
, broken Helm refs, images that fail to build.
If it fails, treat as a Phase 4 issue and fix before continuing.
The skill does not finish without Tier 1 passing.
5.2 Tier 2 — (offered if Dockerfiles or exist)
Pre-check: run
. If it errors or returns no context,
skip Tier 2 and inform the user:
"Skipping the build check — no Okteto context. The manifest is syntactically valid but I haven't proven the Dockerfiles build."
Otherwise, ask:
"I can run
to prove every Dockerfile resolves and pushes. This takes a few minutes per service. Skip / build one service / build all?"
Default to build all. If the user has many services (≥ 4) and is in a hurry, offer narrowing.
Run:
or
5.3 Tier 3 — (offered if Tier 2 passed and user has a context)
Note: there is
no flag. Full deploy is the only Tier 3 option.
Ask:
"I can do a full deploy to verify the manifest works end-to-end. This will create resources in your namespace
. After it succeeds, I'll show you the endpoints. You can
after if you want. Proceed?"
Run:
bash
okteto deploy --wait
okteto endpoints
On success: print endpoints.
Do not run
automatically — that's the user's call.
5.4 On failure at any tier
- Surface the raw CLI error verbatim. Do not paraphrase.
- Diagnose the likely cause based on the manifest section involved (e.g., a Helm error → ; a build error → or the Dockerfile).
- Propose a concrete edit to the manifest. Show the diff, not "you should change X."
- After the user approves the fix:
- If the edit changed manifest structure (added/removed/renamed sections, changed YAML shape), re-run Tier 1 first to catch new schema problems, then re-run the failing tier.
- If the edit was purely a value change (image version, sync path, env var), re-run only the failing tier.
5.5 Final summary
Once the ladder is climbed (or stopped), summarize:
"✅
passed
✅
passed for all services
⏭️
skipped (you opted out)
Next: run
or invoke the
skill to deploy and start developing."
If tiers were skipped due to environment (no context), say so:
"⚠️ Skipped Tiers 2 and 3 (no Okteto context). The manifest is syntactically valid but not deploy-tested."
6. Phase 6 — Handoff or PR
6.1 Collaborative mode: handoff
Point the user at the next step:
"The manifest is in place. To bring up your environment, you can:
- Run for a guided deploy + dev mode
- Or invoke the skill in any future session — it'll pick up the manifest you just created
If you change the manifest, re-run
(or come back to me)."
State which validation tiers were run and which were skipped (see Section 5.5).
Do NOT delete the manifest, run , or push to a remote. The work product is the file on disk.
6.2 Autonomous mode: PR
Create a branch, commit the manifest, push, and open a PR. The PR is the human review gate.
Before running the commands below, substitute every with a real value from your Phase 1 / Phase 5 results. Do not send literal
,
,
<bulleted list of services...>
, etc. to a real PR. For the validation checklist, use
if the tier passed and
if it was skipped or failed.
bash
git checkout -b okteto/onboarding
git add okteto.yaml
git commit -m "Add Okteto manifest
Generated by okteto-onboarding skill.
Discovered services: <list>
Scope level: <N>
Validation: <tiers passed>"
git push -u origin okteto/onboarding
gh pr create --title "Add Okteto manifest" --body "$(cat <<'EOF'
## Summary
This PR adds an Okteto manifest generated by the `okteto-onboarding` skill.
## Discovered services
<bulleted list of services with their Dockerfiles and dev commands>
## Scope level
Level <N> — <one-sentence rationale>
## Validation
- [<x or space>] `okteto validate` passed
- [<x or space>] `okteto build` ran for all services
- [<x or space>] `okteto deploy --wait` succeeded with endpoints
## Decisions to confirm
- [ ] Dev image picks (e.g., `okteto/golang:1.22`) match your toolchain
- [ ] Sync paths exclude appropriate generated dirs (node_modules, vendor, target)
- [ ] Helm/kubectl deploy command matches your usual workflow
🤖 Generated by the okteto-onboarding skill
EOF
)"
The skill never merges the PR. A human reviews and merges.
7. Operating modes
7.1 Collaborative (default)
A user is in the loop. Each phase that needs a decision asks a question. Defaults are presented but not auto-selected.
7.2 Autonomous (opt-in)
No human is expected to intervene. Inferred from context the same way the existing
skill does — for example, when invoked from a CI pipeline or a ticket-driven session.
In autonomous mode:
- Scope level → highest level the discovery supports. Level 2 requires a deploy source (a chart, k8s manifests, or a deployable compose file); Level 3 additionally requires detected tests. With no deploy source at all, stay at Level 1 even if tests are present. When both compose and a chart exist, pick the most conservative deploy source and note the choice in the PR.
- Validation tier → Tier 1 always; Tier 2 if succeeds; Tier 3 only if the trigger explicitly authorizes a deploy (label, env var, or explicit instruction).
- Discovery ambiguities → pick the most conservative interpretation and note it in the PR description.
- Phase 6 → always opens a PR (Section 6.2), never hands off.
8. CLI quick reference
| Command | When | Purpose |
|---|
| Phase 5 Tier 1 (always) | Check manifest syntax/schema |
| Phase 5 pre-checks | Verify cluster connection before Tiers 2/3 |
| Phase 5 Tier 2 | Prove Dockerfiles resolve and push |
| Phase 5 Tier 3 | Full end-to-end validation |
| Phase 5 Tier 3 | Print URLs after a successful deploy |
Never run
from this skill — it's interactive and belongs to the existing
skill / the user.
Never run
— leave that to the user.
9. Common mistakes to avoid
- Triggering when an already exists. Always do the pre-flight check from Section 1.
- Generating a Helm chart or k8s manifests. This skill does not author deploy artifacts. If the user has no chart, no k8s manifests, and no deployable compose file, recommend Level 1 and stop. A compose file alone is enough for Level 2 via — don't drop to Level 1 just because there's no chart. (The one exception is a derived — a mechanical transform of the user's own compose, Section 4.1 — never a chart or k8s manifest written from scratch.)
- Assuming the chart drives when a compose file is also present. Ask which the user wants (Section 2.5); don't silently pick the chart.
- Recommending without scanning for dev-only constructs. Host-IP port bindings and source bind-mount volumes break a cluster deploy (Section 4.1 caveats). Warn the user up front, don't surface it at deploy time.
- Reading only the root . Projects often keep the dev compose under or as , separate from a published-images root compose. Glob for all compose files and follow (Section 2.1). Building from a published-images compose produces a useless manifest.
- Putting every compose service in . Backing services (databases, caches, object stores, brokers) are deploy-only infrastructure. is for the services you edit (Section 2.2).
- Copying a entrypoint into . That's a dev-container placeholder, not a real command — use (Section 2.4).
- Hard-coding a dev-image version. Read the version the repo declares (Section 2.3); only fall back to a default when none is declared, and flag the fallback.
- Forgetting to wire built images downstream. A entry isn't enough on its own for Helm/k8s deploys — pass
${OKTETO_BUILD_<SERVICE>_IMAGE}
into the deploy command so the workload runs the image you just built.
- Skipping the framing block. The vs framing in Section 3.1 must be shown to the user and written into the manifest as a header comment.
- Climbing the validation ladder without checking first. Tiers 2 and 3 require a working context.
- Paraphrasing CLI errors on validation failure. Show the raw output; the user (or the next agent) needs to see exactly what Okteto said.
- Asking the user about everything. Trust the signals from Phase 1. Only ask when discovery is genuinely ambiguous.
- Merging the PR in autonomous mode. The PR is the human gate. Never merge.
- Recommending templates. Those are demos. Build the manifest from discovered facts, not templates.