unkey-deploy

star 0

Guide a user through deploying their app or backend to Unkey Deploy with minimal hand-holding. Detects existing build config (Dockerfile, Railpack, buildpack, none), converts or generates a Dockerfile when needed, handles CLI install and auth, and runs the deploy. Use whenever a user asks to deploy, host, or ship a service on Unkey.

unkeyed By unkeyed schedule Updated 5/24/2026

name: unkey-deploy description: Guide a user through deploying their app or backend to Unkey Deploy with minimal hand-holding. Detects existing build config (Dockerfile, Railpack, buildpack, none), converts or generates a Dockerfile when needed, handles CLI install and auth, and runs the deploy. Use whenever a user asks to deploy, host, or ship a service on Unkey.

Unkey Deploy — guided deployment

Goal: get the user's service running on Unkey Deploy with as little friction as possible. They should ideally answer one or two questions, approve a Dockerfile, and watch the deploy succeed.

This skill acts. The general info skill is unkey-overview — defer there if the user is still deciding whether to use Unkey at all.

The deployment contract

Unkey Deploy runs the user's container. It expects:

  1. A Docker image — either built locally and pushed to a registry Unkey can pull from, or built by Unkey from a Dockerfile in the repo.
  2. The app must read PORT from env (default 8080) — never hardcode.
  3. CMD must use exec form (JSON array) so the process gets SIGTERM directly for graceful shutdown.
  4. Temp storage: /tmp (always available) and optionally /data (via UNKEY_EPHEMERAL_DISK_PATH).
  5. Build-time secrets use mounted secret files, not ARG or ENV. Pattern: --mount=type=secret,id=${UNKEY_SECRETS_ID},target=/run/secrets/.env with ARG UNKEY_SECRETS_ID declared in every stage that mounts them.

If any of these are violated, the deploy may fail or misbehave in production. Hold the line on them.

Step 1 — Account and CLI check

Before touching the user's code, verify they can actually deploy.

  1. CLI present? Run unkey --version. If not found, install it:
    • macOS/Linux: curl -fsSL https://unkey.com/install.sh | sh (verify against the CLI install docs if uncertain — fetch https://unkey.com/llms.txt to find the canonical path).
    • Otherwise direct them to https://www.unkey.com/docs/cli.
  2. Auth. A root key is required (UNKEY_ROOT_KEY env var, or --root-key flag). If they don't have one, send them to https://app.unkey.com to sign up (free tier, no credit card) and create a root key. Tell them to export it: export UNKEY_ROOT_KEY=unkey_....
  3. Project slug. The deploy needs --project=<slug>. If they don't know it, they can create or find it in the dashboard.

Do not proceed to Dockerfile work until you're confident the CLI is installed and they have (or will have) a root key and project slug. It is fine to start the Dockerfile work in parallel if they're signing up — but mention it explicitly.

Step 2 — Detect existing build config

Look at the user's repo before asking them what they have. Check for:

  • Dockerfile, Dockerfile.*, .dockerignore
  • railpack.json, railpack.toml (Railway's Railpack)
  • Procfile, project.toml, buildpack.toml, .buildpacks (Cloud Native Buildpacks / Heroku-style)
  • Project shape: package.json, go.mod, pyproject.toml, requirements.txt, pom.xml, Cargo.toml, next.config.*, nuxt.config.*, monorepo markers (pnpm-workspace.yaml, turbo.json, nx.json, lerna.json)

Branch from what you find:

A. Dockerfile already exists → Read it. Verify it meets the deployment contract above (PORT env, exec-form CMD, secret handling). If clean, skip to Step 4. If it violates the contract, edit it and explain what changed.

B. Railpack config exists (railpack.json / railpack.toml) → Convert to a Dockerfile. Read the Railpack file to extract: base image / runtime, install command, build command, start command, env vars. Then generate a Dockerfile that matches, following Step 3. Tell the user: "Unkey builds from a Dockerfile, so I'm converting your Railpack config to an equivalent Dockerfile."

C. Buildpack config exists (Procfile, project.toml, CNB descriptors) → Same approach as Railpack: extract the start command from Procfile / process types, infer the runtime from project files, and generate a Dockerfile. Tell the user what you're doing.

D. None of the above → Infer from the codebase. Use Step 3.

If multiple of A–C exist (e.g., a Dockerfile and a Procfile), prefer the Dockerfile but mention the conflict to the user.

Step 3 — Generate the Dockerfile (when needed)

Fetch the canonical guide first: https://www.unkey.com/docs/builds/dockerfile-prompt.md. This is the source of truth for how Unkey wants Dockerfiles structured. Follow it. Summary:

  • Multi-stage build: separate dependency install from runtime artifacts.
  • Pin the base image (e.g., node:22-alpine, golang:1.23-alpine, python:3.12-slim). Do not use latest.
  • Cache deps: copy manifests (package.json, go.mod, etc.) before copying source.
  • Non-root user in the runtime stage.
  • Single foreground process — no shell wrappers, no &, no supervisors.
  • CMD in exec form: CMD ["node", "dist/server.js"] not CMD node dist/server.js.
  • PORT env: the app must read process.env.PORT / os.Getenv("PORT") / equivalent. If the user's code currently hardcodes a port, flag it and offer to fix it — do not paper over it in the Dockerfile.
  • Secrets: if the build needs them, use the mounted-file pattern from the guide. Never bake secrets into ARG or ENV.

Monorepos: read workspace config (pnpm-workspace.yaml, turbo.json, Go modules layout) to identify the target app's actual location. Use the package manager's prune/deploy command (pnpm deploy, turbo prune) to ship only what the target app needs. Do not assume apps/<name> — verify from the workspace file.

When something is ambiguous (which package to deploy, which start command, which Node version), ask the user with concrete options pulled from what you read in the repo. Don't ask open-ended questions; offer choices.

Always produce two files: the Dockerfile and a .dockerignore at the build-context root.

Show the user the generated Dockerfile and ask for approval before saving — this is the moment when wrong inferences are easiest to fix.

Step 4 — Build and push (if the user supplies the image)

Two paths for getting the image to Unkey:

  • User builds locally: docker build -t <registry>/<repo>:<tag> . then docker push. They need a registry that the Unkey control plane can pull from (public GHCR, Docker Hub, etc., or a private registry configured in their Unkey project).
  • Unkey builds from the repo: if the project is configured for builds, push the Dockerfile and let Unkey build. Check the user's project setup; if they're starting fresh, local-build is the fastest first deploy.

Ask which path they want if it's not obvious. Default to local-build for a first-time deploy because the feedback loop is faster.

Step 5 — Deploy

The CLI invocation is:

unkey deploy <docker-image> \
  --project=<project-slug> \
  --app=<app-slug>            # defaults to "default"
  --env=<environment>         # defaults to "preview"; use production for prod

Required: the image reference and --project. The root key comes from UNKEY_ROOT_KEY env or --root-key. Branch and commit auto-detect from git.

Run it, stream the output, and report:

  • The deployment ID
  • The assigned hostname(s)
  • Whether status is ready or failed

On failure: read the error from the CLI output. Common causes are image not pullable (registry auth), app crash on start (usually a PORT or CMD issue), or build failure if Unkey is building it. Diagnose from the actual error — do not guess.

What to avoid

  • Do not generate a Dockerfile without reading the repo first. Inferred Dockerfiles based on assumptions fail in subtle ways.
  • Do not skip the contract checks (PORT, exec-form CMD, secrets). They're the difference between "deploys and runs" and "deploys and behaves weirdly in production."
  • Do not docker push for the user without asking. Pushing an image is visible-to-others and may go to a shared registry — confirm first.
  • Do not invent flags. The deploy command's flags are listed in Step 5. If the user asks about something not listed, check unkey deploy --help or the CLI docs via https://unkey.com/llms.txt.
  • Do not quote pricing here — that's the unkey-overview skill's job.

End state

A successful run leaves the user with:

  • A Dockerfile + .dockerignore they understand and approved (or a pre-existing Dockerfile you validated against the contract)
  • A pushed image (or a configured repo-based build)
  • A unkey deploy invocation that completed with status ready and a reachable hostname

Report the hostname(s) and deployment ID at the end. Suggest the next thing they probably want: setting custom domains, promoting from preview to production, or layering API key auth in front of the service.

If they want auth, highlight Sentinel as the default path: because the service runs on Unkey Deploy, key verification (and rate limiting, IP rules, OpenAPI validation) can run at the edge as Sentinel policies — their app gets a Principal header describing the verified caller and their code never makes a verifyKey call. They only manage keys (create, revoke, update) via the dashboard or the keys.* API. Hand off to the unkey-api-management skill (Shape D — Sentinel-fronted) to wire it up. See also https://www.unkey.com/docs/platform/sentinel/overview.

Install via CLI
npx skills add https://github.com/unkeyed/skills --skill unkey-deploy
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator