Install
$ agentstack add skill-flyteorg-flyte-agent-plugins-deploy-flyte-kind Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Deploy Flyte to a kind cluster
Stand up Flyte on a kind cluster: the flyte-binary plus a hosted PostgreSQL and an S3-compatible object store. For evaluation only — no TLS, no auth, static credentials.
kind runs anywhere Docker runs, so the cluster can live on the user's own machine (default) or a DigitalOcean VM (droplet) — the host is a choice made in Step 0.
The PostgreSQL and object store are independent choices the user makes in Step 2:
- PostgreSQL — Supabase or another external/self-hosted PostgreSQL.
- Object store — AWS S3 or Cloudflare R2.
Both are hosted; kind runs only the flyte-binary. The user supplies connection details for each.
Step 0: Choose the host, check prerequisites, and check for an existing cluster
First, ask the user where kind should run (use AskUserQuestion):
- the user's own machine (default), or
- a DigitalOcean VM (droplet) — the only cloud-VM host this skill supports.
Do not offer or hand-roll AWS EC2 or GCP VM setups; for a real cloud deployment, point the user at the AWS deployment skill instead.
If the user picks the droplet, every kind, kubectl, and helm command below runs on the droplet (over SSH) — only the SDK/CLI and browser run on the user's own machine. Provision it and install the tools there first (needs a few GB of headroom for kind, so ≥ 4 vCPU / 8 GB):
# create the droplet (dashboard or doctl)
doctl compute droplet create flyte-kind \
--image ubuntu-24-04-x64 --size s-4vcpu-8gb --region nyc1 \
--ssh-keys
# SSH in and install Docker, kind, kubectl, helm ON the droplet
ssh root@
curl -fsSL https://get.docker.com | sh
curl -Lo /usr/local/bin/kind \
https://github.com/kubernetes-sigs/kind/releases/latest/download/kind-linux-amd64 \
&& chmod +x /usr/local/bin/kind
curl -Lo /usr/local/bin/kubectl \
"https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
&& chmod +x /usr/local/bin/kubectl
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
> [!WARNING] A cloud VM is exposed to the internet > On a droplet the stack is reachable from the public internet. Restrict ports > 80, 443, and 22 to the user's own IP with a > cloud firewall > while they evaluate.
Then verify the required tools on whichever host runs kind (run the check on the droplet over SSH if that's the host), and decide whether to create a cluster or reuse one:
for t in docker kind kubectl helm; do command -v $t >/dev/null || echo "MISSING: $t"; done
kind get clusters
- If any tool is
MISSING, stop and tell the user to install it
(docker, kind, kubectl, helm). Read the kind get clusters output literally — it is the exact list of existing cluster names, one per line. Do not assume a flyte cluster exists because this guide uses that name; only treat it as present if flyte appears verbatim in the output. A line like kind is a cluster named kind, not flyte.
flyteis in the list → reuse it: skip Step 1, use--context kind-flyte
below.
flyteis not in the list → create it in Step 1. (Don't silently reuse a
differently-named cluster; if the user wants to reuse one, confirm its name and substitute it into --context kind- everywhere below.)
- No clusters at all → create one in Step 1.
Step 1: Create the kind cluster (skip if reusing)
Create the cluster with two host-port mappings. kind fixes a cluster's port mappings at creation time — they can't be added later — so map them both now regardless of whether auth is added:
30080 → 80lets the browser reach the Traefik ingress (plain HTTP) used
by the optional auth section at http://flyte.local.
30443 → 443lets the SDK/CLI reach the Traefik ingress over TLS at
https://flyte.local. The SDK only authenticates over HTTPS (see the SDK-auth part of the auth section), so this is required if the user enables auth and wants to submit runs from the SDK. Harmless otherwise.
kind create cluster --name flyte --config - -.pooler.supabase.com` (the pooler host, *not* `db..supabase.co`)
- database name (Supabase default: `postgres`)
- username — `postgres.` (pooler username carries the project ref)
- password
Supabase requires TLS, so use `sslmode=require` in Step 3.
> [!WARNING] Why the session pooler, not the direct connection
> The direct host `db..supabase.co` resolves to **IPv6 only**; kind is IPv4-only,
> so the Flyte pod can't reach it. `wait-for-db` still passes (it only probes the
> port via `pg_isready`), then Flyte crash-loops on `failed to connect`. The session
> pooler host has IPv4. Use port **`5432` (session)**, not `6543` (transaction) —
> Flyte's migrations need session semantics. **Read the host and username straight
> from the Session pooler tab; never reconstruct them** — a wrong region connects but
> is rejected with `tenant/user not found`, and the pooler username must be
> `postgres.`, not bare `postgres`.
**Reusing your own PostgreSQL** is also fine: take the same fields as Supabase. For
a DB on the host machine use `host.docker.internal` as the host. The database must
already exist.
### Object store
**AWS S3** — nothing to install. The user creates the bucket and an access key in
their AWS account; collect (type or screenshot):
- bucket name
- region (e.g. `us-east-1`)
- access key ID
- secret access key
**Cloudflare R2** — nothing to install. The user creates an R2 bucket and an R2 API
token in the Cloudflare dashboard; collect (type or screenshot):
- bucket name
- account endpoint (`https://.r2.cloudflarestorage.com`)
- access key ID
- secret access key
Both endpoints are publicly resolvable, so no `signedURL` override is needed — the
SDK uploads code bundles straight to the bucket.
## Step 3: Write the values file
Assemble `values-local.yaml` from the `database` and `storage` blocks matching the
Step 2 choices. The skeleton:
```yaml
# values-local.yaml — local kind deployment
fullnameOverride: flyte
configuration:
# >
# >
# >
serviceAccount:
create: true
annotations: {}
ingress:
create: false
Database block
Supabase (or other external PostgreSQL) — fill in the collected values. For Supabase, host and username come from the Session pooler connection string:
database:
postgres:
host: aws--.pooler.supabase.com # session pooler host (has IPv4)
port: 5432 # session mode (not 6543 transaction mode)
dbname: postgres # Supabase default
username: postgres. # pooler username carries the project ref
password:
options: "sslmode=require" # Supabase requires TLS
Storage block
AWS S3 — fill in the collected values:
storage:
metadataContainer:
userDataContainer:
provider: s3
providerConfig:
s3:
region:
authType: accesskey
accessKey:
secretKey:
Cloudflare R2 — fill in the collected values:
storage:
metadataContainer:
userDataContainer:
provider: s3
providerConfig:
s3:
endpoint: https://.r2.cloudflarestorage.com
region: auto # R2 ignores region; "auto" is conventional
authType: accesskey
accessKey:
secretKey:
v2Signing: false
Inline block — task-pod storage credentials + storagePrefix (required)
The storage block above configures only the control plane. Two more settings are required for tasks to actually run — without them the API works but every task fails:
- Task pods get no object-store credentials. The task-side SDK reads static
credentials from the FLYTE_AWS_ENDPOINT / FLYTE_AWS_ACCESS_KEY_ID / FLYTE_AWS_SECRET_ACCESS_KEY env vars (flyte/storage/_config.py); with none set it falls back to the default AWS credential chain and probes the EC2 metadata endpoint, so tasks fail with OSError: Generic S3 error: Error performing PUT http://169.254.169.254/latest/api/token. The chart's storage.* values do not propagate to task pods — inject the vars via plugins.k8s.default-env-vars.
- Task I/O goes to a nonexistent bucket.
runs.storagePrefixdefaults to
s3://flyte-data, so task input/output/error.pb writes fail with 403 Forbidden AccessDenied. It is distinct from metadataContainer/userDataContainer (those only configure the control plane's dataproxy) — point it at the real bucket.
Add this under configuration:. The default-env-vars list replaces the chart default outright, so the three _U_* control-plane vars must be repeated — dropping them breaks task→control-plane callbacks:
inline:
runs:
storagePrefix: s3:// # the SAME bucket as the storage block
plugins:
k8s:
default-env-vars: # replaces the chart default — keep all three _U_* vars
- _U_EP_OVERRIDE: "flyte-http.flyte:8090"
- _U_INSECURE: "true"
- _U_USE_ACTIONS: "1"
- FLYTE_AWS_ACCESS_KEY_ID: ""
- FLYTE_AWS_SECRET_ACCESS_KEY: ""
# Cloudflare R2 only — task pods must also be told the endpoint:
- FLYTE_AWS_ENDPOINT: "https://.r2.cloudflarestorage.com"
For AWS S3, omit FLYTE_AWS_ENDPOINT and add the standard - AWS_REGION: "" instead (the SDK's object store reads the standard AWS env vars for anything the FLYTE_AWS_* overrides don't cover).
Step 4: Install Flyte
helm repo add flyteorg https://flyteorg.github.io/flyte
helm repo update
helm install flyte flyteorg/flyte-binary -n flyte -f values-local.yaml
kubectl -n flyte rollout status deploy/flyte
kubectl -n flyte get pods
If a pod is stuck in Init, the wait-for-db init container is blocking on PostgreSQL — the DB isn't up yet, or the host/credentials are wrong. Check kubectl -n flyte logs -c wait-for-db.
Step 5: Verify access
Make the API reachable at localhost:8090 on the machine where the SDK/CLI runs:
kubectl -n flyte port-forward service/flyte-http 8090:8090
On a DigitalOcean droplet the port-forward runs on the droplet, so tunnel it back to the user's own machine over SSH — this one command starts the port-forward on the droplet and exposes it at localhost:8090 locally:
ssh -L 8090:localhost:8090 root@ \
kubectl -n flyte port-forward service/flyte-http 8090:8090
> [!NOTE] helm upgrade kills this port-forward > Every helm upgrade rolls the flyte pod, which drops the flyte-http > port-forward — the SDK then reports "Flyte system is currently unavailable." > Restart the port-forward (and the SSH tunnel, on a droplet) after each upgrade.
In another terminal:
curl -s -X POST \
http://localhost:8090/flyteidl2.project.ProjectService/ListProjects \
-H 'Content-Type: application/json' -d '{}'
A JSON response (not a connection error) confirms Flyte is up and talking to its database. The base deployment is done.
To submit runs from the SDK, point it at the API forward. Ask the user where their SDK config lives — the SDK reads the project-local .flyte/config.yaml (the run directory) before ~/.flyte/config.yaml — and whether to edit it for them or just give them the block to apply themselves. Use this config:
admin:
endpoint: dns:///localhost:8090 # the port-forwarded API — 8090, NOT 8080
insecure: True # plain HTTP, no TLS
task:
org: local
domain: development
project: flytesnacks
The code-bundle upload needs no second port-forward — the S3/R2 endpoint is publicly resolvable, so the SDK uploads straight to the bucket.
Two different ports are in play — don't conflate them. The SDK talks to the API on :8090 (this port-forward), while the browser console lives on :8080 (Step 6). The run URL that flyte run prints (http://localhost:8080/v2/...) is a console link — it only works once Step 6 is done; it is not the API endpoint, and pointing admin.endpoint at :8080 does not work.
Step 6: Access the web console (no auth)
The base deployment leaves the console unreachable: port-forwarding flyte-console directly serves only the SPA, whose frontend calls the API at the same origin it was served from (NEXT_PUBLIC_ADMIN_API_URL is unset, so the API base URL defaults to /) — those calls 404 and run pages load blank. The fix is to put console + API behind one origin with Traefik.
Install Traefik (identical to step 1 of the auth section — if it's already installed, skip this command):
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik -n traefik --create-namespace \
--kube-context kind-flyte \
--set "service.type=NodePort" \
--set "ports.web.nodePort=30080" \
--set "ports.websecure.nodePort=30443"
Route the two path groups to one origin — flyteidl2.* (the Connect API, over h2c) to flyte-http, everything else to the console:
kubectl --context kind-flyte apply -f - /v2`**
(the Step 0 firewall scopes it to the user's IP). Or tunnel it:
`ssh -N -L 8080:localhost:80 root@` → `http://localhost:8080/v2`.
Note the SDK still prints run URLs as `http://localhost:8080/...` — swap
`localhost:8080` for `` unless the tunnel is up.
These routes carry **no auth** — they're the evaluation-mode front door. If the
user later enables the auth section, **delete them first**
(`kubectl -n flyte delete ingressroute flyte-api-noauth flyte-console-noauth`);
they match any host at low priority and would otherwise bypass the OIDC gate.
Now **ask the user whether they want to add OIDC authentication.** The base
deployment has no auth — anyone with network access can reach the API. If they
say yes, do the "Add OIDC authentication via an ingress controller" section
below. If no, stop here.
## Optional extras
Only do these if the user asks.
- **Load a local image into kind** (custom task/Flyte image, no registry):
```bash
kind load docker-image : --name flyte
```
Reference that exact `:` in task config; `IfNotPresent` pull
policy then uses the loaded image. On a **DigitalOcean droplet** the image must
be in the droplet's Docker daemon first — build it there, or ship it from the
user's machine with `docker save | ssh root@ docker load`.
## Add OIDC authentication via an ingress controller
Do this when the user opts in at the Step 6 prompt (or asks later). This adds
OIDC single sign-on at the edge, the kind equivalent of gating the cloud
console behind an ALB.
The pattern: run [Traefik](https://doc.traefik.io/traefik/) as the ingress
controller and delegate auth to
[oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/). Traefik
intercepts each request through a `ForwardAuth` middleware, asks oauth2-proxy
whether the caller is logged in, and redirects to the IdP if not. oauth2-proxy
is the auth proxy at the edge.
### First: choose the OIDC provider
oauth2-proxy needs an OIDC provider to validate against. **Ask the user which
they want** before installing anything:
- **External IdP** (Okta, Google, Auth0, …) — for a setup close to production.
Requires a registered app with redirect URI `http://flyte.local/oauth2/callback`,
and its **client ID** and **client secret**. If the user picks this but
doesn't have those ready, stop — the rest won't work.
- **Dex (local, in-cluster)** — an IdP stand-in for testing, no cloud account
or real users. If the user picks this, you'll deploy Dex via the
**`start-dex-local` ski
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [flyteorg](https://github.com/flyteorg)
- **Source:** [flyteorg/flyte-agent-plugins](https://github.com/flyteorg/flyte-agent-plugins)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.